diff options
author | Nasf-Fan <fan.yong@intel.com> | 2019-08-19 21:21:09 +0000 |
---|---|---|
committer | Jerry Jelinek <jerry.jelinek@joyent.com> | 2019-08-20 11:27:30 +0000 |
commit | f67950b21e185934ccabe311516f4dcbdb00ef79 (patch) | |
tree | 0876bfea682f05ade72b05689808909878907070 /usr/src/cmd/zfs/zfs_project.c | |
parent | a2f04351e04971ab0879872d264d6038c156b860 (diff) | |
download | illumos-joyent-f67950b21e185934ccabe311516f4dcbdb00ef79.tar.gz |
11479 zfs project support
Portions contributed by: Jerry Jelinek <jerry.jelinek@joyent.com>
Portions contributed by: Jinshan Xiong <jinshan.xiong@intel.com>
Portions contributed by: Suman Chakravartula <schakrava@gmail.com>
Portions contributed by: jxiong <jinshan.xiong@gmail.com>
Reviewed by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed by: Andreas Dilger <andreas.dilger@intel.com>
Reviewed by Ned Bass <bass6@llnl.gov>
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed by: C Fraire <cfraire@me.com>
Reviewed by: Kody Kantor <kody.kantor@joyent.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
Diffstat (limited to 'usr/src/cmd/zfs/zfs_project.c')
-rw-r--r-- | usr/src/cmd/zfs/zfs_project.c | 304 |
1 files changed, 304 insertions, 0 deletions
diff --git a/usr/src/cmd/zfs/zfs_project.c b/usr/src/cmd/zfs/zfs_project.c new file mode 100644 index 0000000000..7b7652f786 --- /dev/null +++ b/usr/src/cmd/zfs/zfs_project.c @@ -0,0 +1,304 @@ +/* + * CDDL HEADER START + * + * 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. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * 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] + * + * CDDL HEADER END + */ + +/* + * Copyright (c) 2017, Intle Corporation. All rights reserved. + * Copyright 2019 Joyent, Inc. + */ + +#include <errno.h> +#include <getopt.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> +#include <unistd.h> +#include <fcntl.h> +#include <dirent.h> +#include <stddef.h> +#include <libintl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/list.h> +#include <limits.h> +#include <sys/debug.h> +#include <sys/stat.h> +#include <sys/zfs_project.h> + +#include "zfs_util.h" +#include "zfs_projectutil.h" + +typedef struct zfs_project_item { + list_node_t zpi_list; + char zpi_name[0]; +} zfs_project_item_t; + +static void +zfs_project_item_alloc(list_t *head, const char *name) +{ + zfs_project_item_t *zpi; + + zpi = safe_malloc(sizeof (zfs_project_item_t) + strlen(name) + 1); + (void) strcpy(zpi->zpi_name, name); + list_insert_tail(head, zpi); +} + +static int +zfs_project_sanity_check(const char *name, zfs_project_control_t *zpc, + struct stat *st) +{ + int ret; + + ret = stat(name, st); + if (ret) { + (void) fprintf(stderr, gettext("failed to stat %s: %s\n"), + name, strerror(errno)); + return (ret); + } + + if (!S_ISREG(st->st_mode) && !S_ISDIR(st->st_mode)) { + (void) fprintf(stderr, gettext("only support project quota on " + "regular file or directory\n")); + return (-1); + } + + if (!S_ISDIR(st->st_mode)) { + if (zpc->zpc_dironly) { + (void) fprintf(stderr, gettext( + "'-d' option on non-dir target %s\n"), name); + return (-1); + } + + if (zpc->zpc_recursive) { + (void) fprintf(stderr, gettext( + "'-r' option on non-dir target %s\n"), name); + return (-1); + } + } + + return (0); +} + +static int +zfs_project_load_projid(const char *name, zfs_project_control_t *zpc) +{ + zfsxattr_t fsx; + int ret, fd; + + fd = open(name, O_RDONLY | O_NOCTTY); + if (fd < 0) { + (void) fprintf(stderr, gettext("failed to open %s: %s\n"), + name, strerror(errno)); + return (fd); + } + + ret = ioctl(fd, ZFS_IOC_FSGETXATTR, &fsx); + if (ret) + (void) fprintf(stderr, + gettext("failed to get xattr for %s: %s\n"), + name, strerror(errno)); + else + zpc->zpc_expected_projid = fsx.fsx_projid; + + (void) close(fd); + return (ret); +} + +static int +zfs_project_handle_one(const char *name, zfs_project_control_t *zpc) +{ + zfsxattr_t fsx; + int ret, fd; + + fd = open(name, O_RDONLY | O_NOCTTY); + if (fd < 0) { + if (errno == ENOENT && zpc->zpc_ignore_noent) + return (0); + + (void) fprintf(stderr, gettext("failed to open %s: %s\n"), + name, strerror(errno)); + return (fd); + } + + ret = ioctl(fd, ZFS_IOC_FSGETXATTR, &fsx); + if (ret) { + (void) fprintf(stderr, + gettext("failed to get xattr for %s: %s\n"), + name, strerror(errno)); + goto out; + } + + switch (zpc->zpc_op) { + case ZFS_PROJECT_OP_LIST: + (void) printf("%5u %c %s\n", fsx.fsx_projid, + (fsx.fsx_xflags & ZFS_PROJINHERIT_FL) ? 'P' : '-', name); + goto out; + case ZFS_PROJECT_OP_CHECK: + if (fsx.fsx_projid == zpc->zpc_expected_projid && + fsx.fsx_xflags & ZFS_PROJINHERIT_FL) + goto out; + + if (!zpc->zpc_newline) { + char c = '\0'; + + (void) printf("%s%c", name, c); + goto out; + } + + if (fsx.fsx_projid != zpc->zpc_expected_projid) + (void) printf("%s - project ID is not set properly " + "(%u/%u)\n", name, fsx.fsx_projid, + (uint32_t)zpc->zpc_expected_projid); + + if (!(fsx.fsx_xflags & ZFS_PROJINHERIT_FL)) + (void) printf("%s - project inherit flag is not set\n", + name); + + goto out; + case ZFS_PROJECT_OP_CLEAR: + if (!(fsx.fsx_xflags & ZFS_PROJINHERIT_FL) && + (zpc->zpc_keep_projid || + fsx.fsx_projid == ZFS_DEFAULT_PROJID)) + goto out; + + fsx.fsx_xflags &= ~ZFS_PROJINHERIT_FL; + if (!zpc->zpc_keep_projid) + fsx.fsx_projid = ZFS_DEFAULT_PROJID; + break; + case ZFS_PROJECT_OP_SET: + if (fsx.fsx_projid == zpc->zpc_expected_projid && + (!zpc->zpc_set_flag || fsx.fsx_xflags & ZFS_PROJINHERIT_FL)) + goto out; + + fsx.fsx_projid = zpc->zpc_expected_projid; + if (zpc->zpc_set_flag) + fsx.fsx_xflags |= ZFS_PROJINHERIT_FL; + break; + default: + ASSERT(0); + break; + } + + ret = ioctl(fd, ZFS_IOC_FSSETXATTR, &fsx); + if (ret) + (void) fprintf(stderr, + gettext("failed to set xattr for %s: %s\n"), + name, strerror(errno)); + +out: + (void) close(fd); + return (ret); +} + +static int +zfs_project_handle_dir(const char *name, zfs_project_control_t *zpc, + list_t *head) +{ + char fullname[PATH_MAX]; + struct dirent *ent; + DIR *dir; + int ret = 0; + + dir = opendir(name); + if (dir == NULL) { + if (errno == ENOENT && zpc->zpc_ignore_noent) + return (0); + + ret = -errno; + (void) fprintf(stderr, gettext("failed to opendir %s: %s\n"), + name, strerror(errno)); + return (ret); + } + + /* Non-top item, ignore the case of being removed or renamed by race. */ + zpc->zpc_ignore_noent = B_TRUE; + errno = 0; + while (!ret && (ent = readdir(dir)) != NULL) { + /* skip "." and ".." */ + if (strcmp(ent->d_name, ".") == 0 || + strcmp(ent->d_name, "..") == 0) + continue; + + if (strlen(ent->d_name) + strlen(name) >= + sizeof (fullname) + 1) { + errno = ENAMETOOLONG; + break; + } + + (void) sprintf(fullname, "%s/%s", name, ent->d_name); + ret = zfs_project_handle_one(fullname, zpc); + if (!ret && zpc->zpc_recursive) { + struct stat64 sb; + + if (stat64(fullname, &sb) == 0 && + (sb.st_mode & S_IFMT) == S_IFDIR) + zfs_project_item_alloc(head, fullname); + } + } + + if (errno && !ret) { + ret = -errno; + (void) fprintf(stderr, gettext("failed to readdir %s: %s\n"), + name, strerror(errno)); + } + + (void) closedir(dir); + return (ret); +} + +int +zfs_project_handle(const char *name, zfs_project_control_t *zpc) +{ + zfs_project_item_t *zpi; + struct stat st; + list_t head; + int ret; + + ret = zfs_project_sanity_check(name, zpc, &st); + if (ret) + return (ret); + + if ((zpc->zpc_op == ZFS_PROJECT_OP_SET || + zpc->zpc_op == ZFS_PROJECT_OP_CHECK) && + zpc->zpc_expected_projid == ZFS_INVALID_PROJID) { + ret = zfs_project_load_projid(name, zpc); + if (ret) + return (ret); + } + + zpc->zpc_ignore_noent = B_FALSE; + ret = zfs_project_handle_one(name, zpc); + if (ret || !S_ISDIR(st.st_mode) || zpc->zpc_dironly || + (!zpc->zpc_recursive && + zpc->zpc_op != ZFS_PROJECT_OP_LIST && + zpc->zpc_op != ZFS_PROJECT_OP_CHECK)) + return (ret); + + list_create(&head, sizeof (zfs_project_item_t), + offsetof(zfs_project_item_t, zpi_list)); + zfs_project_item_alloc(&head, name); + while ((zpi = list_remove_head(&head)) != NULL) { + if (!ret) + ret = zfs_project_handle_dir(zpi->zpi_name, zpc, &head); + free(zpi); + } + + return (ret); +} |