*/
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011 by Delphix. All rights reserved.
*/
/*
#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>
switch (error) {
case ENXIO:
case ENODEV:
+ case EPIPE:
zfs_verror(hdl, EZFS_IO, fmt, ap);
break;
hdl->libzfs_printerr = printerr;
}
+static int
+libzfs_module_loaded(const char *module)
+{
+ const char path_prefix[] = "/sys/module/";
+ char path[256];
+
+ memcpy(path, path_prefix, sizeof(path_prefix) - 1);
+ strcpy(path + sizeof(path_prefix) - 1, module);
+
+ return (access(path, F_OK) == 0);
+}
+
+int
+libzfs_run_process(const char *path, char *argv[], int flags)
+{
+ pid_t pid;
+ int rc, devnull_fd;
+
+ pid = vfork();
+ if (pid == 0) {
+ devnull_fd = open("/dev/null", O_WRONLY);
+
+ if (devnull_fd < 0)
+ _exit(-1);
+
+ if (!(flags & STDOUT_VERBOSE))
+ (void) dup2(devnull_fd, STDOUT_FILENO);
+
+ if (!(flags & STDERR_VERBOSE))
+ (void) dup2(devnull_fd, STDERR_FILENO);
+
+ close(devnull_fd);
+
+ (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, 0);
+}
+
libzfs_handle_t *
libzfs_init(void)
{
libzfs_handle_t *hdl;
+ 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[6][9] = {"by-id", "by-label", "by-path", "by-uuid", "zpool",
+ "by-vdev"};
+
+ /* /dev/ */
+ (void) snprintf(path, pathlen, "%s/%s", DISK_ROOT, name);
+ err = access(path, F_OK);
+ if (err == 0)
+ return (err);
+
+ /* /dev/mapper/ */
+ (void) snprintf(path, pathlen, "%s/mapper/%s", DISK_ROOT, name);
+ err = access(path, F_OK);
+ if (err == 0)
+ return (err);
+
+ /* /dev/disk/<dirs>/ */
+ for (i = 0; i < 6 && 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().
*/
}
/*
- * We want to allow trailing 'b' characters for 'GB' or 'Mb'. But don't
- * allow 'BB' - that's just weird.
+ * Allow 'G' = 'GB' = 'GiB', case-insensitively.
+ * However, 'BB' and 'BiB' are disallowed.
*/
- if (buf[1] == '\0' || (toupper(buf[1]) == 'B' && buf[2] == '\0' &&
- toupper(buf[0]) != 'B'))
+ if (buf[1] == '\0' ||
+ (toupper(buf[0]) != 'B' &&
+ ((toupper(buf[1]) == 'B' && buf[2] == '\0') ||
+ (toupper(buf[1]) == 'I' && toupper(buf[2]) == 'B' &&
+ buf[3] == '\0'))))
return (10*i);
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
* dataset property,
*/
if (prop == ZPROP_INVAL && (type == ZFS_TYPE_POOL ||
- (!zfs_prop_user(propname) && !zfs_prop_userquota(propname)))) {
+ (!zfs_prop_user(propname) && !zfs_prop_userquota(propname) &&
+ !zfs_prop_written(propname)))) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"invalid property '%s'"), propname);
return (zfs_error(hdl, EZFS_BADPROP,