zfs_main.c revision ca45db4129beff691dc46576c328149443788af2
* Enable a reasonable set of defaults for libumem debugging on DEBUG builds. return (
"default,verbose");
/* $UMEM_DEBUG setting */ return (
"fail,contents");
/* $UMEM_LOGGING setting */ * Master command table. Each ZFS command has a name, associated function, and * usage message. The usage messages need to be internationalized, so we have * to have a function to return the usage message based on a command index. * These commands are organized according to how they are displayed in the usage * message. An empty command (one with a NULL name) indicates an empty line in * the generic usage message. return (
gettext(
"\tclone [-p] [-o property=value] ... " "<snapshot> <filesystem|volume>\n"));
return (
gettext(
"\tcreate [-p] [-o property=value] ... " "\tcreate [-ps] [-b blocksize] [-o property=value] ... " "-V <size> <volume>\n"));
return (
gettext(
"\tdestroy [-rRf] " "<filesystem|volume|snapshot>\n" "\tdestroy -d [-r] <filesystem|volume|snapshot>\n"));
return (
gettext(
"\tget [-rHp] [-d max] " "[-o field[,...]] [-s source[,...]]\n" "\t <\"all\" | property[,...]> " "[filesystem|volume|snapshot] ...\n"));
return (
gettext(
"\tinherit [-r] <property> " "<filesystem|volume|snapshot> ...\n"));
"\tupgrade [-r] [-V version] <-a | filesystem ...>\n"));
return (
gettext(
"\tlist [-rH][-d max] " "[-o property[,...]] [-t type[,...]] [-s property] ...\n" "[filesystem|volume|snapshot] ...\n"));
"\tmount [-vO] [-o opts] <-a | filesystem>\n"));
return (
gettext(
"\tpromote <clone-filesystem>\n"));
return (
gettext(
"\treceive [-vnF] <filesystem|volume|" "\treceive [-vnF] -d <filesystem>\n"));
return (
gettext(
"\trename <filesystem|volume|snapshot> " "<filesystem|volume|snapshot>\n" "\trename -p <filesystem|volume> <filesystem|volume>\n" "\trename -r <snapshot> <snapshot>"));
return (
gettext(
"\trollback [-rRf] <snapshot>\n"));
return (
gettext(
"\tsend [-R] [-[iI] snapshot] <snapshot>\n"));
return (
gettext(
"\tset <property=value> " "<filesystem|volume|snapshot> ...\n"));
return (
gettext(
"\tshare <-a | filesystem>\n"));
return (
gettext(
"\tsnapshot [-r] [-o property=value] ... " "<filesystem@snapname|volume@snapname>\n"));
"<-a | filesystem|mountpoint>\n"));
"<-a | filesystem|mountpoint>\n"));
return (
gettext(
"\tallow <filesystem|volume>\n" "<\"everyone\"|user|group>[,...] <perm|@setname>[,...]\n" "\t <filesystem|volume>\n" "\tallow [-ld] -e <perm|@setname>[,...] " "\tallow -c <perm|@setname>[,...] <filesystem|volume>\n" "\tallow -s @setname <perm|@setname>[,...] " "<filesystem|volume>\n"));
return (
gettext(
"\tunallow [-rldug] " "<\"everyone\"|user|group>[,...]\n" "\t [<perm|@setname>[,...]] <filesystem|volume>\n" "\tunallow [-rld] -e [<perm|@setname>[,...]] " "\tunallow [-r] -c [<perm|@setname>[,...]] " "\tunallow [-r] -s @setname [<perm|@setname>[,...]] " "<filesystem|volume>\n"));
return (
gettext(
"\tuserspace [-hniHp] [-o field[,...]] " "[-sS field] ... [-t type[,...]]\n" "\t <filesystem|snapshot>\n"));
return (
gettext(
"\tgroupspace [-hniHpU] [-o field[,...]] " "[-sS field] ... [-t type[,...]]\n" "\t <filesystem|snapshot>\n"));
return (
gettext(
"\thold [-r] <tag> <snapshot> ...\n"));
return (
gettext(
"\tholds [-r] <snapshot> ...\n"));
return (
gettext(
"\trelease [-r] <tag> <snapshot> ...\n"));
* Utility function to guarantee malloc() success. * Callback routine that will print out information for each of * Display usage message. If we're inside a command, display only the usage for * that command. Otherwise, iterate over the entire command table and display * a complete usage message. gettext(
"where 'command' is one of the following:\n\n"));
"pool/[dataset/]*dataset[@name]\n"));
gettext(
"\nThe following properties are supported:\n"));
"PROPERTY",
"EDIT",
"INHERIT",
"VALUES");
/* Iterate over all properties */ (
void)
fprintf(
fp,
"\t%-15s ",
"userused@...");
(
void)
fprintf(
fp,
"\t%-15s ",
"groupused@...");
(
void)
fprintf(
fp,
"\t%-15s ",
"userquota@...");
(
void)
fprintf(
fp,
"YES NO <size> | none\n");
(
void)
fprintf(
fp,
"\t%-15s ",
"groupquota@...");
(
void)
fprintf(
fp,
"YES NO <size> | none\n");
"with standard units such as K, M, G, etc.\n"));
"be specified by using a name containing a colon (:).\n"));
"properties must be appended with\n" "a user or group specifier of one of these forms:\n" " POSIX name (eg: \"matt\")\n" " POSIX id (eg: \"126829\")\n" " SMB name@domain (eg: \"matt@sun\")\n" " SMB SID (eg: \"S-1-234-567-89\")\n"));
gettext(
"\nFor the property list, run: %s\n"),
gettext(
"\nFor the delegated permission list, run: %s\n"),
* See comments at end of main(). (
void)
printf(
"dumping core by request\n");
"specified multiple times\n"),
propname);
"error: out of memory\n"));
gettext(
"Depth can not be negative.\n"));
* zfs clone [-p] [-o prop=value] ... <snap> <fs | vol> * Given an existing dataset, create a writable copy whose initial contents * are the same as the source. The newly created dataset maintains a * dependency on the original; the original cannot be destroyed so long as * The '-p' flag creates all the non-existing ancestors of the target first. /* check number of arguments */ /* open the source dataset */ * Now create the ancestors of the target dataset. If the * target already exists and '-p' option was used we should not /* create the mountpoint if necessary */ * zfs create [-p] [-o prop=value] ... fs * zfs create [-ps] [-b blocksize] [-o prop=value] ... -V vol size * Create a new dataset. This command can be used to create filesystems * and volumes. Snapshot creation is handled by 'zfs snapshot'. * For volumes, the user must specify a size to be used. * The '-s' flag applies only to volumes, and indicates that we should not try * to set the reservation for this volume. By default we set a reservation * equal to the size for any volume. For pools with SPA_VERSION >= * SPA_VERSION_REFRESERVATION, we set a refreservation instead. * The '-p' flag creates all the non-existing ancestors of the target first. "error: out of memory\n"));
"block size '%s': %s\n"),
optarg,
"error: out of memory\n"));
"used when creating a volume\n"));
/* check number of arguments */ "error: out of memory\n"));
* Now create the ancestors of target dataset. If the target * already exists and '-p' option was used we should not * if the user doesn't want the dataset automatically mounted, * Mount and/or share the new filesystem as appropriate. We provide a * verbose error message to let the user know that their filesystem was * in fact created, even if we failed to mount or share it. "successfully created, but not mounted\n"));
"successfully created, but not shared\n"));
* zfs destroy [-rRf] <fs, snap, vol> * zfs destroy -d [-r] <fs, snap, vol> * -r Recursively destroy all children * -R Recursively destroy all dependents, including clones * -f Force unmounting of any dependents * -d If we can't destroy now, mark for deferred destruction * Destroys the given dataset. By default, it will unmount any filesystems, * and refuse to destroy a dataset that has any dependents. A dependent can * either be a child, or a clone of a child. * Check for any dependents based on the '-r' or '-R' flags. * This is a direct descendant, not a clone somewhere else in "the following datasets:\n"));
* This is a clone. We only want to report this if the '-r' * wasn't specified, or the target is a snapshot. "%s has dependent clones\n"),
"the following datasets:\n"));
* Ignore pools (which we've already flagged as an error before getting * Bail out on the first error. * Destroy any clones of this snapshot /* check number of arguments */ * If we are doing recursive destroy of a snapshot, then the * named snapshot may not exist. Go straight to libzfs. gettext(
"no snapshots destroyed\n"));
/* Open the given dataset */ * Perform an explicit check for pools before going any further. "operation does not apply to pools\n"),
"%s' to destroy all datasets in the pool\n"),
* Check for any dependents and/or clones. * Do the real thing. The callback will close the handle regardless of * whether it succeeds or not. * zfs get [-rHp] [-o field[,field]...] [-s source[,source]...] * < all | property[,property]... > < fs | snap | vol > ... * -r recurse over any child datasets * -H scripted mode. Headers are stripped, and fields are separated * by tabs instead of spaces. * -o Set of fields to display. One of "name,property,value,source". * -s Set of sources to allow. One of * "local,default,inherited,temporary,none". Default is all * -p Display values in parsable (literal) format. * Prints properties for the given datasets. The user can control which * columns to display as well as which property types to allow. * Invoked to display the properties for a single dataset. * Skip the special fake placeholder. This will also skip over * the name property when 'all' is specified. gettext(
"No such property '%s'\n"),
* Set up default columns and sources. * Process the set of columns to display. We zero out * the structure to give us a blank slate. {
"name",
"property",
"value",
"source",
"many fields given to -o " "local",
"default",
"inherited",
"temporary",
"none",
NULL };
* As part of zfs_expand_proplist(), we keep track of the maximum column * width for each property. For the 'NAME' (and 'SOURCE') columns, we * need to know the maximum name length. However, the user likely did * not specify 'name' as one of the properties to fetch, so we need to * make sure we always include at least this property for * print_get_headers() to work properly. /* run for each object */ * inherit [-r] <property> <fs|vol> ... * -r Recurse over all children * For each dataset specified on the command line, inherit the given property * from its parent. Inheriting a property at the pool level will cause it to * use the default value. The '-r' flag will recurse over all children, and is * useful for setting a property on a hierarchy-wide basis, regardless of any * local modifications for each dataset. * If we're doing it recursively, then ignore properties that * are not valid for this type of dataset. /* check number of arguments */ "%s property is read-only\n"),
"formatted using a newer software version and\n" "cannot be accessed on the current system.\n\n");
"out of date, and can be upgraded. After being\n" "upgraded, these filesystems (and any 'zfs send' " "streams generated from\n" "subsequent snapshots) will no longer be " "accessible by older software versions.\n\n");
"upgraded; the pool version needs to first " "be upgraded\nto version %d\n\n"),
* If they did "zfs upgrade -a", then we could * be doing ioctls to different pools. We need * to log this history once to each pool. "it is already at version %u\n"),
* zfs upgrade [-r] [-V <version>] <-a | filesystem> /* Show info on available versions. */ (
void)
printf(
"--- -----------------------------------------" "unique identifier (FUID)\n"));
"version, including supported releases, see:\n\n"));
/* Upgrade filesystems */ /* List old-version filesytems */ "formatted with the current version.\n"));
* Try the python version. If the execv fails, we'll continue * and do a simplistic implementation. (
void)
printf(
"internal error: %s not found\n" "falling back on built-in implementation, " "some features will not work\n",
pypath);
(
void)
printf(
"PROP TYPE NAME VALUE\n");
* list [-r][-d max] [-H] [-o property[,property]...] [-t type[,type]...] * [-s property [-s property]...] [-S property [-S property]...] * -r Recurse over all children * -d Limit recursion by depth. * -H Scripted mode; elide headers and separate columns by tabs * -o Control which fields to display. * -t Control which object types to display. * -s Specify sort columns, descending order. * -S Specify sort columns, ascending order. * When given no arguments, lists all filesystems in the system. * Otherwise, list the specified datasets, optionally recursing down them if * Given a list of columns to display, output appropriate headers for each one. * Given a dataset and a list of fields, print out all the properties according * to the described layout. * If this is being called in scripted mode, or if this is the * last column and it is left-justified, don't include a width * Generic callback function to list a dataset or snapshot. "name,used,available,referenced,mountpoint";
"volume",
"snapshot",
"all",
NULL };
* If "-o space" and no types were specified, don't display snapshots. * If the user specifies '-o all', the zprop_get_list() doesn't * normally include the name of the dataset. For 'zfs list', we always * want this property to be first. * zfs rename <fs | snap | vol> <fs | snap | vol> * zfs rename -p <fs | vol> <fs | vol> * zfs rename -r <snap> <snap> * Renames the given dataset to another of the same type. * The '-p' flag creates all the non-existing ancestors of the target first. /* check number of arguments */ "rename must be a snapshot\n"));
/* If we were asked and the name looks good, try to create ancestors. */ * Promotes the given clone fs to be the parent /* check number of arguments */ * zfs rollback [-rRf] <snapshot> * -r Delete any intervening snapshots before doing rollback * -R Delete any snapshots and their clones * -f ignored for backwards compatability * Given a filesystem, rollback to a specific snapshot, discarding any changes * since then and making it the active dataset. If more recent snapshots exist, * the command will complain unless the '-r' flag is given. * Report any snapshots more recent than the one specified. Used when '-r' is * not specified. We reuse this same callback for the snapshot dependents - if * 'cb_dependent' is set, then this is a dependent and we should report it * without checking the transaction group. "rollback to '%s': more recent snapshots " "force deletion of the following " "'%s': clones of previous snapshots exist\n"),
"force deletion of the following clones and " /* check number of arguments */ /* open the parent dataset */ * Check for more recent snapshots and/or clones based on the presence * Rollback parent to the given snapshot. * zfs set property=value { fs | snap | vol } ... * Sets the given property for all datasets specified on the command line. "but unable to remount filesystem\n"));
"but unable to reshare filesystem\n"));
/* check number of arguments */ /* validate property=value argument */ "property=value argument\n"));
gettext(
"missing property in property=value argument\n"));
* zfs snapshot [-r] [-o prop=value] ... <fs@snap> * Creates a snapshot with the given name. While functionally equivalent to * 'zfs create', it is a separate command to differentiate intent. /* check number of arguments */ * zfs send [-v] -R [-i|-I <@snap>] <fs@snap> * zfs send [-v] [-i|-I <@snap>] <fs@snap> * Send a backup stream to stdout. /* check number of arguments */ gettext(
"Error: Stream can not be written to a terminal.\n" "You must redirect standard output.\n"));
gettext(
"argument must be a snapshot\n"));
* If they specified the full path to the snapshot, chop off * everything except the short name of the snapshot, but special * case if they specify the origin. gettext(
"incremental source must be " "in same filesystem\n"));
gettext(
"invalid incremental source\n"));
* zfs receive [-dnvF] <fs@snap> * Restore a backup stream from stdin. /* check number of arguments */ gettext(
"Error: Backup stream can not be read " "You must redirect standard input.\n"));
/* check number of arguments */ /* tags starting with '.' are reserved for libzfs */ for (i = 0; i <
argc; ++i) {
* zfs hold [-r] [-t] <tag> <snap> ... * -t Temporary hold (hidden option) * Apply a user-hold with the given tag to the list of snapshots. * zfs release [-r] <tag> <snap> ... * Release a user-hold with the given tag from the list of snapshots. static char spin[] = {
'-',
'\\',
'|',
'/' };
* Interate over any nested datasets. * Skip any datasets whose type does not match. * Generic callback for sharing or mounting filesystems. Because the code is so * similar, we have a common function with an extra parameter to determine which * Share or mount a dataset. * Check to make sure we can mount/share this dataset. If we * are in the global zone and the filesystem is exported to a * local zone, or if we are in a local zone and the * filesystem is not exported, then it is an error. "dataset is exported to a local zone\n"),
cmdname,
* Ignore any filesystems which don't apply to us. This * includes those with a legacy mountpoint, or those with "share this filesystem, or set " "sharenfs property on\n"));
* We cannot share or mount legacy filesystems. If the * shareopts is non-legacy but the mountpoint is legacy, we * treat it as a legacy share. * canmount explicit outcome * off yes display error, return 1 * noauto yes pass through "'canmount' property is set to 'off'\n"),
cmdname,
* At this point, we have verified that the mountpoint and/or * shareopts are appropriate for auto management. If the * filesystem is already mounted or shared, return (failing * for explicit requests); otherwise mount or share the "'%s': filesystem already shared\n"),
"'%s': invalid share type '%s' " "'%s': filesystem already mounted\n"),
* Ignore any volumes that aren't shared. "'shareiscsi' property not set\n"),
"property or use iscsitadm(1M) to share this " "'%s': volume already shared\n"),
* Reports progress in the form "(current/total)". Not thread-safe. static char *
reverse =
"\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b" "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b";
/* report 1..n instead of 0..n-1 */ /* display header if we're here for the first time */ /* too soon to report again */ /* back up to prepare for overwriting */ /* We put a newline at the end if this is the last one. */ /* original length plus new string to append plus 1 for the comma */ "'%c' option is too long (more than %d chars)\n"),
"options (-o) specified\n"));
/* option validation is done later */ /* check number of arguments */ "must be 'nfs', 'smb' or 'iscsi'\n"));
for (i = 0; i <
count; i++) {
"argument (specify -a for all)\n"));
* When mount is given no arguments, go through /etc/mnttab and * display any active ZFS mounts. We hide any snapshots, since * they are controlled automatically. * zfs mount -a [nfs | iscsi] * Mount all filesystems, or mount the given filesystem. * zfs share -a [nfs | iscsi | smb] * Share all filesystems, or share the given filesystem. * Convenience routine used by zfs_do_umount() and manual_unmount(). Given an * absolute path, find the entry /etc/mnttab, verify that its a ZFS filesystem, * and unmount it appropriately. * Search for the path in /etc/mnttab. Rather than looking for the * specific path, which can be fooled by non-standard paths (i.e. ".." * or "//"), we stat() the path and search for the corresponding * (major,minor) device pair. * Search for the given (major,minor) pair in the mount table. "'%s': legacy share\n"),
path);
"unshare(1M) to unshare this filesystem\n"));
"not currently shared\n"),
path);
"'%s': legacy mountpoint\n"),
"to unmount this filesystem\n"));
* Generic callback for unsharing or unmounting a filesystem. * We could make use of zfs_for_each() to walk all datasets in * the system, but this would be very inefficient, especially * since we would have to linearly search /etc/mnttab for each * one. Instead, do one pass through /etc/mnttab looking for * zfs entries and call zfs_unmount() for each one. * Things get a little tricky if the administrator has created * mountpoints beneath other ZFS filesystems. In this case, we * have to unmount the deepest filesystems first. To accomplish * this, we place all the mountpoints in an AVL tree sorted by * the special type (dataset name), and walk the result in * reverse to make sure to get any snapshots first. /* ignore non-ZFS entries */ /* Ignore legacy mounts */ /* Ignore canmount=noauto mounts */ * Walk the AVL tree in reverse, unmounting each filesystem and * removing it from the AVL tree in the process. gettext(
"internal error: out of memory"));
* Finally, unshare any volumes shared via iSCSI. for (i = 0; i <
count; i++) {
gettext(
"missing filesystem argument\n"));
* We have an argument, but it may be a full path or a ZFS * filesystem. Pass full paths off to unmount_path() (shared by * manual_unmount), otherwise open the filesystem and pass to "unshare '%s': legacy share\n"),
"unshare(1M) to unshare this " "unshare '%s': not currently " "umount(1M) to unmount this " "unmount '%s': not currently " "'%s': 'shareiscsi' property not set\n"),
"'shareiscsi' property or use " "iscsitadm(1M) to share this volume\n"));
"unshare '%s': not currently shared\n"),
* Unmount all filesystems, or a specific ZFS filesystem. * Unshare all filesystems, or a specific ZFS filesystem. * Called when invoked as /etc/fs/zfs/mount. Do the mount if the mountpoint is * 'legacy'. Otherwise, complain that use should be using 'zfs mount'. /* check that we only have two arguments */ gettext(
"missing mountpoint argument\n"));
/* try to open the dataset */ /* check for legacy mountpoint and complain appropriately */ "mounted using 'mount -F zfs'\n"),
dataset);
"or /etc/vfstab, use 'zfs set mountpoint=legacy'.\n"));
* Called when invoked as /etc/fs/zfs/umount. Unlike a manual mount, we allow * unmounts of non-legacy filesystems, as this is the dominant administrative * Iterate over all pools in the system and either create or destroy /dev/zvol * links, depending on the value of 'isinit'. "initialize ZFS library\n"));
* This command also doubles as the /etc/fs mount and unmount program. * Determine if we should take this behavior based on argv[0]. * Make sure the user has specified some command. * The 'umount' command is an alias for 'unmount' * The 'recv' command is an alias for 'receive' * 'volinit' and 'volfini' do not appear in the usage message, * so we have to special case them here. * Run the appropriate command. * The 'ZFS_ABORT' environment variable causes us to dump core on exit * for the purposes of running ::findleaks. (
void)
printf(
"dumping core by request\n");