Path: blob/main/sys/contrib/openzfs/cmd/zfs/zfs_project.c
109175 views
// SPDX-License-Identifier: CDDL-1.01/*2* CDDL HEADER START3*4* The contents of this file are subject to the terms of the5* Common Development and Distribution License (the "License").6* You may not use this file except in compliance with the License.7*8* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE9* or https://opensource.org/licenses/CDDL-1.0.10* See the License for the specific language governing permissions11* and limitations under the License.12*13* When distributing Covered Code, include this CDDL HEADER in each14* file and include the License file at usr/src/OPENSOLARIS.LICENSE.15* If applicable, add the following below this CDDL HEADER, with the16* fields enclosed by brackets "[]" replaced with your own identifying17* information: Portions Copyright [yyyy] [name of copyright owner]18*19* CDDL HEADER END20*/2122/*23* Copyright (c) 2017, Intle Corporation. All rights reserved.24*/2526#include <errno.h>27#include <getopt.h>28#include <stdio.h>29#include <stdlib.h>30#include <string.h>31#include <unistd.h>32#include <fcntl.h>33#include <dirent.h>34#include <stddef.h>35#include <libintl.h>36#include <sys/stat.h>37#include <sys/types.h>38#include <sys/list.h>39#include <sys/zfs_project.h>4041#include "zfs_util.h"42#include "zfs_projectutil.h"4344typedef struct zfs_project_item {45list_node_t zpi_list;46char zpi_name[0];47} zfs_project_item_t;4849static void50zfs_project_item_alloc(list_t *head, const char *name)51{52zfs_project_item_t *zpi;5354zpi = safe_malloc(sizeof (zfs_project_item_t) + strlen(name) + 1);55strcpy(zpi->zpi_name, name);56list_insert_tail(head, zpi);57}5859static int60zfs_project_sanity_check(const char *name, zfs_project_control_t *zpc,61struct stat *st)62{63int ret;6465ret = stat(name, st);66if (ret) {67(void) fprintf(stderr, gettext("failed to stat %s: %s\n"),68name, strerror(errno));69return (ret);70}7172if (!S_ISREG(st->st_mode) && !S_ISDIR(st->st_mode)) {73(void) fprintf(stderr, gettext("only support project quota on "74"regular file or directory\n"));75return (-1);76}7778if (!S_ISDIR(st->st_mode)) {79if (zpc->zpc_dironly) {80(void) fprintf(stderr, gettext(81"'-d' option on non-dir target %s\n"), name);82return (-1);83}8485if (zpc->zpc_recursive) {86(void) fprintf(stderr, gettext(87"'-r' option on non-dir target %s\n"), name);88return (-1);89}90}9192return (0);93}9495static int96zfs_project_load_projid(const char *name, zfs_project_control_t *zpc)97{98zfsxattr_t fsx;99int ret, fd;100101fd = open(name, O_RDONLY | O_NOCTTY);102if (fd < 0) {103(void) fprintf(stderr, gettext("failed to open %s: %s\n"),104name, strerror(errno));105return (fd);106}107108ret = ioctl(fd, ZFS_IOC_FSGETXATTR, &fsx);109if (ret)110(void) fprintf(stderr,111gettext("failed to get xattr for %s: %s\n"),112name, strerror(errno));113else114zpc->zpc_expected_projid = fsx.fsx_projid;115116close(fd);117return (ret);118}119120static int121zfs_project_handle_one(const char *name, zfs_project_control_t *zpc)122{123zfsxattr_t fsx;124int ret, fd;125126fd = open(name, O_RDONLY | O_NOCTTY);127if (fd < 0) {128if (errno == ENOENT && zpc->zpc_ignore_noent)129return (0);130131(void) fprintf(stderr, gettext("failed to open %s: %s\n"),132name, strerror(errno));133return (fd);134}135136ret = ioctl(fd, ZFS_IOC_FSGETXATTR, &fsx);137if (ret) {138(void) fprintf(stderr,139gettext("failed to get xattr for %s: %s\n"),140name, strerror(errno));141goto out;142}143144switch (zpc->zpc_op) {145case ZFS_PROJECT_OP_LIST:146(void) printf("%5u %c %s\n", fsx.fsx_projid,147(fsx.fsx_xflags & FS_XFLAG_PROJINHERIT) ? 'P' : '-', name);148goto out;149case ZFS_PROJECT_OP_CHECK:150if (fsx.fsx_projid == zpc->zpc_expected_projid &&151fsx.fsx_xflags & FS_XFLAG_PROJINHERIT)152goto out;153154if (!zpc->zpc_newline) {155char c = '\0';156157(void) printf("%s%c", name, c);158goto out;159}160161if (fsx.fsx_projid != zpc->zpc_expected_projid)162(void) printf("%s - project ID is not set properly "163"(%u/%u)\n", name, fsx.fsx_projid,164(uint32_t)zpc->zpc_expected_projid);165166if (!(fsx.fsx_xflags & FS_XFLAG_PROJINHERIT))167(void) printf("%s - project inherit flag is not set\n",168name);169170goto out;171case ZFS_PROJECT_OP_CLEAR:172if (!(fsx.fsx_xflags & FS_XFLAG_PROJINHERIT) &&173(zpc->zpc_keep_projid ||174fsx.fsx_projid == ZFS_DEFAULT_PROJID))175goto out;176177fsx.fsx_xflags &= ~FS_XFLAG_PROJINHERIT;178if (!zpc->zpc_keep_projid)179fsx.fsx_projid = ZFS_DEFAULT_PROJID;180break;181case ZFS_PROJECT_OP_SET:182if (fsx.fsx_projid == zpc->zpc_expected_projid &&183(!zpc->zpc_set_flag ||184fsx.fsx_xflags & FS_XFLAG_PROJINHERIT))185goto out;186187fsx.fsx_projid = zpc->zpc_expected_projid;188if (zpc->zpc_set_flag)189fsx.fsx_xflags |= FS_XFLAG_PROJINHERIT;190break;191default:192ASSERT(0);193break;194}195196ret = ioctl(fd, ZFS_IOC_FSSETXATTR, &fsx);197if (ret) {198(void) fprintf(stderr,199gettext("failed to set xattr for %s: %s\n"),200name, strerror(errno));201202if (errno == ENOTSUP) {203char *kver = zfs_version_kernel();204/*205* Special case: a module/userspace version mismatch can206* return ENOTSUP due to us fixing the XFLAGs bits in207* #17884. In that case give a hint to the user that208* they should take action to make the versions match.209*/210if (strcmp(kver, ZFS_META_ALIAS) != 0) {211fprintf(stderr,212gettext("Warning: The zfs module version "213"(%s) and userspace\nversion (%s) do not "214"match up. This may be the\ncause of the "215"\"Operation not supported\" error.\n"),216kver, ZFS_META_ALIAS);217}218}219}220221out:222close(fd);223return (ret);224}225226static int227zfs_project_handle_dir(const char *name, zfs_project_control_t *zpc,228list_t *head)229{230struct dirent *ent;231DIR *dir;232int ret = 0;233234dir = opendir(name);235if (dir == NULL) {236if (errno == ENOENT && zpc->zpc_ignore_noent)237return (0);238239ret = -errno;240(void) fprintf(stderr, gettext("failed to opendir %s: %s\n"),241name, strerror(errno));242return (ret);243}244245/* Non-top item, ignore the case of being removed or renamed by race. */246zpc->zpc_ignore_noent = B_TRUE;247errno = 0;248while (!ret && (ent = readdir(dir)) != NULL) {249char *fullname;250251/* skip "." and ".." */252if (strcmp(ent->d_name, ".") == 0 ||253strcmp(ent->d_name, "..") == 0)254continue;255256if (strlen(ent->d_name) + strlen(name) + 1 >= PATH_MAX) {257errno = ENAMETOOLONG;258break;259}260261if (asprintf(&fullname, "%s/%s", name, ent->d_name) == -1) {262errno = ENOMEM;263break;264}265266ret = zfs_project_handle_one(fullname, zpc);267if (!ret && zpc->zpc_recursive && ent->d_type == DT_DIR)268zfs_project_item_alloc(head, fullname);269270free(fullname);271}272273if (errno && !ret) {274ret = -errno;275(void) fprintf(stderr, gettext("failed to readdir %s: %s\n"),276name, strerror(errno));277}278279closedir(dir);280return (ret);281}282283int284zfs_project_handle(const char *name, zfs_project_control_t *zpc)285{286zfs_project_item_t *zpi;287struct stat st;288list_t head;289int ret;290291ret = zfs_project_sanity_check(name, zpc, &st);292if (ret)293return (ret);294295if ((zpc->zpc_op == ZFS_PROJECT_OP_SET ||296zpc->zpc_op == ZFS_PROJECT_OP_CHECK) &&297zpc->zpc_expected_projid == ZFS_INVALID_PROJID) {298ret = zfs_project_load_projid(name, zpc);299if (ret)300return (ret);301}302303zpc->zpc_ignore_noent = B_FALSE;304ret = zfs_project_handle_one(name, zpc);305if (ret || !S_ISDIR(st.st_mode) || zpc->zpc_dironly ||306(!zpc->zpc_recursive &&307zpc->zpc_op != ZFS_PROJECT_OP_LIST &&308zpc->zpc_op != ZFS_PROJECT_OP_CHECK))309return (ret);310311list_create(&head, sizeof (zfs_project_item_t),312offsetof(zfs_project_item_t, zpi_list));313zfs_project_item_alloc(&head, name);314while ((zpi = list_remove_head(&head)) != NULL) {315if (!ret)316ret = zfs_project_handle_dir(zpi->zpi_name, zpc, &head);317free(zpi);318}319320return (ret);321}322323324