#include <unistd.h>
#include <ctype.h>
#include <math.h>
+#include <sys/stat.h>
#include <sys/mnttab.h>
#include <sys/mntent.h>
#include <sys/types.h>
+#include <wait.h>
#include <libzfs.h>
case EZFS_BADPROP:
return (dgettext(TEXT_DOMAIN, "invalid property value"));
case EZFS_PROPREADONLY:
- return (dgettext(TEXT_DOMAIN, "read only property"));
+ return (dgettext(TEXT_DOMAIN, "read-only property"));
case EZFS_PROPTYPE:
return (dgettext(TEXT_DOMAIN, "property doesn't apply to "
"datasets of this type"));
case EZFS_BADSTREAM:
return (dgettext(TEXT_DOMAIN, "invalid backup stream"));
case EZFS_DSREADONLY:
- return (dgettext(TEXT_DOMAIN, "dataset is read only"));
+ return (dgettext(TEXT_DOMAIN, "dataset is read-only"));
case EZFS_VOLTOOBIG:
return (dgettext(TEXT_DOMAIN, "volume size exceeds limit for "
"this system"));
case EZFS_NODELEGATION:
return (dgettext(TEXT_DOMAIN, "delegated administration is "
"disabled on pool"));
- case EZFS_PERMRDONLY:
- return (dgettext(TEXT_DOMAIN, "snapshot permissions cannot be"
- " modified"));
case EZFS_BADCACHE:
return (dgettext(TEXT_DOMAIN, "invalid or missing cache file"));
case EZFS_ISL2CACHE:
"use 'zpool scrub -s' to cancel current scrub"));
case EZFS_NO_SCRUB:
return (dgettext(TEXT_DOMAIN, "there is no active scrub"));
+ case EZFS_DIFF:
+ return (dgettext(TEXT_DOMAIN, "unable to generate diffs"));
+ case EZFS_DIFFDATA:
+ return (dgettext(TEXT_DOMAIN, "invalid diff data"));
+ case EZFS_POOLREADONLY:
+ return (dgettext(TEXT_DOMAIN, "pool is read-only"));
case EZFS_UNKNOWN:
return (dgettext(TEXT_DOMAIN, "unknown error"));
default:
zfs_verror(hdl, EZFS_BUSY, fmt, ap);
break;
case EROFS:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "snapshot permissions cannot be modified"));
- zfs_verror(hdl, EZFS_PERMRDONLY, fmt, ap);
+ zfs_verror(hdl, EZFS_POOLREADONLY, fmt, ap);
break;
case ENAMETOOLONG:
zfs_verror(hdl, EZFS_NAMETOOLONG, fmt, ap);
case EDQUOT:
zfs_verror(hdl, EZFS_NOSPC, fmt, ap);
return (-1);
+
case EAGAIN:
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"pool I/O is currently suspended"));
zfs_verror(hdl, EZFS_POOLUNAVAIL, fmt, ap);
break;
+ case EROFS:
+ zfs_verror(hdl, EZFS_POOLREADONLY, fmt, ap);
+ break;
+
default:
zfs_error_aux(hdl, strerror(error));
zfs_verror(hdl, EZFS_UNKNOWN, fmt, ap);
}
/*
+ * A safe form of asprintf() which will die if the allocation fails.
+ */
+/*PRINTFLIKE2*/
+char *
+zfs_asprintf(libzfs_handle_t *hdl, const char *fmt, ...)
+{
+ va_list ap;
+ char *ret;
+ int err;
+
+ va_start(ap, fmt);
+
+ err = vasprintf(&ret, fmt, ap);
+
+ va_end(ap);
+
+ if (err < 0)
+ (void) no_memory(hdl);
+
+ return (ret);
+}
+
+/*
* A safe form of realloc(), which also zeroes newly allocated space.
*/
void *
u = " KMGTPE"[index];
if (index == 0) {
- (void) snprintf(buf, buflen, "%llu", n);
+ (void) snprintf(buf, buflen, "%llu", (u_longlong_t) n);
} else if ((num & ((1ULL << 10 * index) - 1)) == 0) {
/*
* If this is an even multiple of the base, always display
* without any decimal precision.
*/
- (void) snprintf(buf, buflen, "%llu%c", n, u);
+ (void) snprintf(buf, buflen, "%llu%c", (u_longlong_t) n, u);
} else {
/*
* We want to choose a precision that reflects the best choice
hdl->libzfs_printerr = printerr;
}
+static int
+libzfs_module_loaded(const char *module)
+{
+ FILE *f;
+ int result = 0;
+ char name[256];
+
+ f = fopen("/proc/modules", "r");
+ if (f == NULL)
+ return -1;
+
+ while (fgets(name, sizeof(name), f)) {
+ char *c = strchr(name, ' ');
+ if (!c)
+ continue;
+ *c = 0;
+ if (strcmp(module, name) == 0) {
+ result = 1;
+ break;
+ }
+ }
+ fclose(f);
+
+ return result;
+}
+
+int
+libzfs_run_process(const char *path, char *argv[])
+{
+ pid_t pid;
+ int rc;
+
+ pid = vfork();
+ if (pid == 0) {
+ close(1);
+ close(2);
+ (void) execvp(path, argv);
+ _exit(-1);
+ } else if (pid > 0) {
+ int status;
+
+ while ((rc = waitpid(pid, &status, 0)) == -1 &&
+ errno == EINTR);
+ if (rc < 0 || !WIFEXITED(status))
+ return -1;
+
+ return WEXITSTATUS(status);
+ }
+
+ return -1;
+}
+
+int
+libzfs_load_module(const char *module)
+{
+ char *argv[4] = {"/sbin/modprobe", "-q", (char *)module, (char *)0};
+
+ if (libzfs_module_loaded(module))
+ return 0;
+
+ return libzfs_run_process("/sbin/modprobe", argv);
+}
+
libzfs_handle_t *
libzfs_init(void)
{
libzfs_handle_t *hdl;
- if ((hdl = calloc(sizeof (libzfs_handle_t), 1)) == NULL) {
+ if (libzfs_load_module("zfs") != 0) {
+ (void) fprintf(stderr, gettext("Failed to load ZFS module "
+ "stack.\nLoad the module manually by running "
+ "'insmod <location>/zfs.ko' as root.\n"));
+ return (NULL);
+ }
+
+ if ((hdl = calloc(1, sizeof (libzfs_handle_t))) == NULL) {
return (NULL);
}
if ((hdl->libzfs_fd = open(ZFS_DEV, O_RDWR)) < 0) {
+ (void) fprintf(stderr, gettext("Unable to open %s: %s.\n"),
+ ZFS_DEV, strerror(errno));
+ if (errno == ENOENT)
+ (void) fprintf(stderr,
+ gettext("Verify the ZFS module stack is "
+ "loaded by running '/sbin/modprobe zfs'.\n"));
+
free(hdl);
return (NULL);
}
+#ifdef HAVE_SETMNTENT
+ if ((hdl->libzfs_mnttab = setmntent(MNTTAB, "r")) == NULL) {
+#else
if ((hdl->libzfs_mnttab = fopen(MNTTAB, "r")) == NULL) {
+#endif
(void) close(hdl->libzfs_fd);
free(hdl);
return (NULL);
{
(void) close(hdl->libzfs_fd);
if (hdl->libzfs_mnttab)
+#ifdef HAVE_SETMNTENT
+ (void) endmntent(hdl->libzfs_mnttab);
+#else
(void) fclose(hdl->libzfs_mnttab);
+#endif
if (hdl->libzfs_sharetab)
(void) fclose(hdl->libzfs_sharetab);
zfs_uninit_libshare(hdl);
}
/*
+ * Given a shorthand device name, check if a file by that name exists in a list
+ * of directories under /dev. If one is found, store its full path in the
+ * buffer pointed to by the path argument and return 0, else return -1. The
+ * path buffer must be allocated by the caller.
+ */
+int
+zfs_resolve_shortname(const char *name, char *path, size_t pathlen)
+{
+ int i, err;
+ char dirs[5][9] = {"by-id", "by-label", "by-path", "by-uuid", "zpool"};
+
+ (void) snprintf(path, pathlen, "%s/%s", DISK_ROOT, name);
+ err = access(path, F_OK);
+ for (i = 0; i < 5 && err < 0; i++) {
+ (void) snprintf(path, pathlen, "%s/%s/%s",
+ UDISK_ROOT, dirs[i], name);
+ err = access(path, F_OK);
+ }
+ return err;
+}
+
+/*
+ * Append partition suffix to a device path. This should be used to generate
+ * the name of a whole disk as it is stored in the vdev label. The
+ * user-visible names of whole disks do not contain the partition information.
+ * Modifies buf which must be allocated by the caller.
+ */
+void
+zfs_append_partition(const char *path, char *buf, size_t buflen)
+{
+ if (strncmp(path, UDISK_ROOT, strlen(UDISK_ROOT)) == 0)
+ (void) snprintf(buf, buflen, "%s%s%s", path, "-part",
+ FIRST_SLICE);
+ else
+ (void) snprintf(buf, buflen, "%s%s%s", path,
+ isdigit(path[strlen(path)-1]) ? "p" : "",
+ FIRST_SLICE);
+}
+
+/*
* Initialize the zc_nvlist_dst member to prepare for receiving an nvlist from
* an ioctl().
*/
zcmd_alloc_dst_nvlist(libzfs_handle_t *hdl, zfs_cmd_t *zc, size_t len)
{
if (len == 0)
- len = 4*1024;
+ len = 16 * 1024;
zc->zc_nvlist_dst_size = len;
if ((zc->zc_nvlist_dst = (uint64_t)(uintptr_t)
- zfs_alloc(hdl, zc->zc_nvlist_dst_size)) == NULL)
+ zfs_alloc(hdl, zc->zc_nvlist_dst_size)) == 0)
return (-1);
return (0);
{
free((void *)(uintptr_t)zc->zc_nvlist_dst);
if ((zc->zc_nvlist_dst = (uint64_t)(uintptr_t)
- zfs_alloc(hdl, zc->zc_nvlist_dst_size))
- == NULL)
+ zfs_alloc(hdl, zc->zc_nvlist_dst_size)) == 0)
return (-1);
return (0);
const char *source, const char *recvd_value)
{
int i;
- const char *str;
+ const char *str = NULL;
char buf[128];
/*