zfs_main.c revision e7437265dc2a4920c197ed4337665539d358b22c
* The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * See the License for the specific language governing permissions * and limitations under the License. * When distributing Covered Code, include this CDDL HEADER in each * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. #
pragma ident "%Z%%M% %I% %E% SMI" * These libumem hooks provide a reasonable set of defaults for the allocator's 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] <snapshot> " "<filesystem|volume>\n"));
return (
gettext(
"\tcreate [-p] [[-o property=value] ... ] " "\tcreate [-ps] [-b blocksize] [[-o property=value] " "\t -V <size> <volume>\n"));
return (
gettext(
"\tdestroy [-rRf] " "<filesystem|volume|snapshot>\n"));
return (
gettext(
"\tget [-rHp] [-o field[,field]...] " "[-s source[,source]...]\n" "\t <all | property[,property]...> " "[filesystem|volume|snapshot] ...\n"));
return (
gettext(
"\tinherit [-r] <property> " "<filesystem|volume> ...\n"));
"\tupgrade [-r] [-V version] <-a | filesystem ...>\n"));
return (
gettext(
"\tlist [-rH] [-o property[,property]...] " "\t [-s property [-s property]...]" " [-S property [-S property]...]\n" "\t [filesystem|volume|snapshot] ...\n"));
"\tmount [-o opts] [-O] -a\n" "\tmount [-o opts] [-O] <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 [-i <snapshot>] <snapshot>\n"));
return (
gettext(
"\tset <property=value> " "<filesystem|volume> ...\n"));
"\tshare <filesystem>\n"));
"<filesystem@name|volume@name>\n"));
return (
gettext(
"\tunmount [-f] -a\n" "\tunmount [-f] <filesystem|mountpoint>\n"));
return (
gettext(
"\tunshare [-f] -a\n" "\tunshare [-f] <filesystem|mountpoint>\n"));
return (
gettext(
"\tallow [-l][-d] <everyone|user|group>[," "<everyone|user|group>...]\n\t " "<perm>|@<setname>[,<perm>|@<setname>...]\n\t" "\tallow [-l] [-d] -u <user> " "<perm>|@<setname>[,<perm>|@<setname>...]\n\t" "\tallow [-l] [-d] -g <group> " "<perm>|@<setname>[,<perm>|@<setname>...]\n\t" "<perm>|@<setname>[,<perm>|@<setname>...]\n\t" "<perm>|@<setname>[,<perm>|@<setname>...]\n\t" "<perm>|@<setname>[,<perm>|@<setname>...]\n\t" " <filesystem|volume>\n"));
return (
gettext(
"\tunallow [-r][-l][-d] <everyone|user|group>[," "<everyone|user|group>...] \n\t " "[<perm>|@<setname>[,<perm>|@<setname>...]]\n\t" "\tunallow [-r][-l][-d] -u user " "[<perm>|@<setname>[,<perm>|@<setname>...]]\n\t" "\tunallow [-r][-l][-d] -g group " "[<perm>|@<setname>[,<perm>|@<setname>...]]\n\t" "\tunallow [-r][-l][-d] -e " "[<perm>|@<setname>[,<perm>|@<setname>...]]\n\t" "[<perm>|@<setname>[,<perm>|@<setname>...]]\n\t" "\tunallow [-r] -s @setname " "[<perm>|@<setname>[,<perm>|@<setname>...]]\n\t" " <filesystem|volume> \n\t"));
* Utility function to guarantee malloc() success. * Callback routinue that will print out information for each of the * 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 */ "with standard units such as K, M, G, etc.\n"));
"be specified by using a name containing a colon (:).\n"));
* "zfs set|get" must not be localised this is the * command name and arguments. gettext(
"\nFor the property list, run: zfs set|get\n"));
* See comments at end of main(). (
void)
printf(
"dumping core by request\n");
* zfs clone [-p] <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 */ return (
ret == 0 ? 0 :
1);
* 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. * 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"));
"specified multiple times\n"),
propname);
"error: out of memory\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 * 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. "created, but not mounted\n"));
"created, but not shared\n"));
* zfs destroy [-rf] <fs, snap, vol> * -r Recursively destroy all children * -R Recursively destroy all dependents, including clones * -f Force unmounting of any dependents * 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. /* 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");
* 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(
"--- -----------------------------------------" "version, including supported releases, see:\n\n"));
/* Upgrade filesystems */ /* List old-version filesytems */ "formatted with the current version.\n"));
* list [-rH] [-o property[,property]...] [-t type[,type]...] * [-s property [-s property]...] [-S property [-S property]...] * -r Recurse over all children * -H Scripted mode; elide headers and separate colums 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";
* If the user specifies '-o all', the zfs_get_proplist() 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 [-rfR] <snapshot> * -r Delete any intervening snapshots before doing rollback * -R Delete any snapshots and their clones * -f Force unmount filesystems, even if they are in use. * 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] <fs@snap> * Creates a snapshot with the given name. While functionally equivalent to * 'zfs create', it is a separate command to diffferentiate intent. /* check number of arguments */ * zfs send [-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"));
* If they specified the full path to the snapshot, chop off * everything except the short name of the snapshot. gettext(
"incremental source must be " "in same filesystem\n"));
gettext(
"invalid incremental source\n"));
* Restore a backup stream from stdin. /* check number of arguments */ gettext(
"Error: Backup stream can not be read " "You must redirect standard input.\n"));
* function to print actual permission when tree has >0 nodes. * Avoid an extra space being printed * for "everyone" which is keyed with a null #
define LINES "-------------------------------------------------------------\n" gettext(
"Failed to retrieve 'allows' on %s\n"),
ds);
"Local+Descendent permissions on (%s)",
* Returns 1 - If permissions should be displayed. * Returns -1 - on failure * Only print permissions if no options were processed * initialize variables for zfs_build_perms based on number * 3 arguments ==> zfs [un]allow joe perm,perm,perm <dataset> or * zfs [un]allow -s @set1 perm,perm <dataset> * 2 arguments ==> zfs [un]allow -c perm,perm <dataset> or * zfs [un]allow -u|-g <name> perm <dataset> or * zfs [un]allow -e perm,perm <dataset> * zfs unallow joe <dataset> * zfs unallow -s @set1 <dataset> * 1 argument ==> zfs [un]allow -e <dataset> or * zfs [un]allow -c <dataset> * for do_allow case make sure who have a know who type * and its not a permission set. gettext(
"Can't set or remove 'allow' permissions " * 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\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. "share(1M)" :
"mount(1M)",
cmdname);
"'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': 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"),
/* check number of arguments */ "must be 'nfs' or 'iscsi'\n"));
for (i = 0; i <
count; i++) {
* 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] * 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 and shares */ * 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. * Check and see if they are doing property=value gettext(
"invalid property '%s'\n"),
* 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");