]> granicus.if.org Git - zfs/blob - cmd/zfs/zfs_project.c
Fix typo "/zbin/zpool" -> "/sbin/zpool"
[zfs] / cmd / zfs / zfs_project.c
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * 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.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21
22 /*
23  * Copyright (c) 2017, Intle Corporation. All rights reserved.
24  */
25
26 #include <errno.h>
27 #include <getopt.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <strings.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>
40
41 #include "zfs_util.h"
42 #include "zfs_projectutil.h"
43
44 typedef struct zfs_project_item {
45         list_node_t     zpi_list;
46         char            zpi_name[0];
47 } zfs_project_item_t;
48
49 static void
50 zfs_project_item_alloc(list_t *head, const char *name)
51 {
52         zfs_project_item_t *zpi;
53
54         zpi = safe_malloc(sizeof (zfs_project_item_t) + strlen(name) + 1);
55         strcpy(zpi->zpi_name, name);
56         list_insert_tail(head, zpi);
57 }
58
59 static int
60 zfs_project_sanity_check(const char *name, zfs_project_control_t *zpc,
61     struct stat *st)
62 {
63         int ret;
64
65         ret = stat(name, st);
66         if (ret) {
67                 (void) fprintf(stderr, gettext("failed to stat %s: %s\n"),
68                     name, strerror(errno));
69                 return (ret);
70         }
71
72         if (!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"));
75                 return (-1);
76         }
77
78         if (!S_ISDIR(st->st_mode)) {
79                 if (zpc->zpc_dironly) {
80                         (void) fprintf(stderr, gettext(
81                             "'-d' option on non-dir target %s\n"), name);
82                         return (-1);
83                 }
84
85                 if (zpc->zpc_recursive) {
86                         (void) fprintf(stderr, gettext(
87                             "'-r' option on non-dir target %s\n"), name);
88                         return (-1);
89                 }
90         }
91
92         return (0);
93 }
94
95 static int
96 zfs_project_load_projid(const char *name, zfs_project_control_t *zpc)
97 {
98         zfsxattr_t fsx;
99         int ret, fd;
100
101         fd = open(name, O_RDONLY | O_NOCTTY);
102         if (fd < 0) {
103                 (void) fprintf(stderr, gettext("failed to open %s: %s\n"),
104                     name, strerror(errno));
105                 return (fd);
106         }
107
108         ret = ioctl(fd, ZFS_IOC_FSGETXATTR, &fsx);
109         if (ret)
110                 (void) fprintf(stderr,
111                     gettext("failed to get xattr for %s: %s\n"),
112                     name, strerror(errno));
113         else
114                 zpc->zpc_expected_projid = fsx.fsx_projid;
115
116         close(fd);
117         return (ret);
118 }
119
120 static int
121 zfs_project_handle_one(const char *name, zfs_project_control_t *zpc)
122 {
123         zfsxattr_t fsx;
124         int ret, fd;
125
126         fd = open(name, O_RDONLY | O_NOCTTY);
127         if (fd < 0) {
128                 if (errno == ENOENT && zpc->zpc_ignore_noent)
129                         return (0);
130
131                 (void) fprintf(stderr, gettext("failed to open %s: %s\n"),
132                     name, strerror(errno));
133                 return (fd);
134         }
135
136         ret = ioctl(fd, ZFS_IOC_FSGETXATTR, &fsx);
137         if (ret) {
138                 (void) fprintf(stderr,
139                     gettext("failed to get xattr for %s: %s\n"),
140                     name, strerror(errno));
141                 goto out;
142         }
143
144         switch (zpc->zpc_op) {
145         case ZFS_PROJECT_OP_LIST:
146                 (void) printf("%5u %c %s\n", fsx.fsx_projid,
147                     (fsx.fsx_xflags & ZFS_PROJINHERIT_FL) ? 'P' : '-', name);
148                 goto out;
149         case ZFS_PROJECT_OP_CHECK:
150                 if (fsx.fsx_projid == zpc->zpc_expected_projid &&
151                     fsx.fsx_xflags & ZFS_PROJINHERIT_FL)
152                         goto out;
153
154                 if (!zpc->zpc_newline) {
155                         char c = '\0';
156
157                         (void) printf("%s%c", name, c);
158                         goto out;
159                 }
160
161                 if (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);
165
166                 if (!(fsx.fsx_xflags & ZFS_PROJINHERIT_FL))
167                         (void) printf("%s - project inherit flag is not set\n",
168                             name);
169
170                 goto out;
171         case ZFS_PROJECT_OP_CLEAR:
172                 if (!(fsx.fsx_xflags & ZFS_PROJINHERIT_FL) &&
173                     (zpc->zpc_keep_projid ||
174                     fsx.fsx_projid == ZFS_DEFAULT_PROJID))
175                         goto out;
176
177                 fsx.fsx_xflags &= ~ZFS_PROJINHERIT_FL;
178                 if (!zpc->zpc_keep_projid)
179                         fsx.fsx_projid = ZFS_DEFAULT_PROJID;
180                 break;
181         case ZFS_PROJECT_OP_SET:
182                 if (fsx.fsx_projid == zpc->zpc_expected_projid &&
183                     (!zpc->zpc_set_flag || fsx.fsx_xflags & ZFS_PROJINHERIT_FL))
184                         goto out;
185
186                 fsx.fsx_projid = zpc->zpc_expected_projid;
187                 if (zpc->zpc_set_flag)
188                         fsx.fsx_xflags |= ZFS_PROJINHERIT_FL;
189                 break;
190         default:
191                 ASSERT(0);
192                 break;
193         }
194
195         ret = ioctl(fd, ZFS_IOC_FSSETXATTR, &fsx);
196         if (ret)
197                 (void) fprintf(stderr,
198                     gettext("failed to set xattr for %s: %s\n"),
199                     name, strerror(errno));
200
201 out:
202         close(fd);
203         return (ret);
204 }
205
206 static int
207 zfs_project_handle_dir(const char *name, zfs_project_control_t *zpc,
208     list_t *head)
209 {
210         char fullname[PATH_MAX];
211         struct dirent *ent;
212         DIR *dir;
213         int ret = 0;
214
215         dir = opendir(name);
216         if (dir == NULL) {
217                 if (errno == ENOENT && zpc->zpc_ignore_noent)
218                         return (0);
219
220                 ret = -errno;
221                 (void) fprintf(stderr, gettext("failed to opendir %s: %s\n"),
222                     name, strerror(errno));
223                 return (ret);
224         }
225
226         /* Non-top item, ignore the case of being removed or renamed by race. */
227         zpc->zpc_ignore_noent = B_TRUE;
228         errno = 0;
229         while (!ret && (ent = readdir(dir)) != NULL) {
230                 /* skip "." and ".." */
231                 if (strcmp(ent->d_name, ".") == 0 ||
232                     strcmp(ent->d_name, "..") == 0)
233                         continue;
234
235                 if (strlen(ent->d_name) + strlen(name) >=
236                     sizeof (fullname) + 1) {
237                         errno = ENAMETOOLONG;
238                         break;
239                 }
240
241                 sprintf(fullname, "%s/%s", name, ent->d_name);
242                 ret = zfs_project_handle_one(fullname, zpc);
243                 if (!ret && zpc->zpc_recursive && ent->d_type == DT_DIR)
244                         zfs_project_item_alloc(head, fullname);
245         }
246
247         if (errno && !ret) {
248                 ret = -errno;
249                 (void) fprintf(stderr, gettext("failed to readdir %s: %s\n"),
250                     name, strerror(errno));
251         }
252
253         closedir(dir);
254         return (ret);
255 }
256
257 int
258 zfs_project_handle(const char *name, zfs_project_control_t *zpc)
259 {
260         zfs_project_item_t *zpi;
261         struct stat st;
262         list_t head;
263         int ret;
264
265         ret = zfs_project_sanity_check(name, zpc, &st);
266         if (ret)
267                 return (ret);
268
269         if ((zpc->zpc_op == ZFS_PROJECT_OP_SET ||
270             zpc->zpc_op == ZFS_PROJECT_OP_CHECK) &&
271             zpc->zpc_expected_projid == ZFS_INVALID_PROJID) {
272                 ret = zfs_project_load_projid(name, zpc);
273                 if (ret)
274                         return (ret);
275         }
276
277         zpc->zpc_ignore_noent = B_FALSE;
278         ret = zfs_project_handle_one(name, zpc);
279         if (ret || !S_ISDIR(st.st_mode) || zpc->zpc_dironly ||
280             (!zpc->zpc_recursive &&
281             zpc->zpc_op != ZFS_PROJECT_OP_LIST &&
282             zpc->zpc_op != ZFS_PROJECT_OP_CHECK))
283                 return (ret);
284
285         list_create(&head, sizeof (zfs_project_item_t),
286             offsetof(zfs_project_item_t, zpi_list));
287         zfs_project_item_alloc(&head, name);
288         while ((zpi = list_remove_head(&head)) != NULL) {
289                 if (!ret)
290                         ret = zfs_project_handle_dir(zpi->zpi_name, zpc, &head);
291                 free(zpi);
292         }
293
294         return (ret);
295 }