Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/contrib/openzfs/cmd/zfs/zfs_project.c
48288 views
1
// SPDX-License-Identifier: CDDL-1.0
2
/*
3
* CDDL HEADER START
4
*
5
* The contents of this file are subject to the terms of the
6
* Common Development and Distribution License (the "License").
7
* You may not use this file except in compliance with the License.
8
*
9
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10
* or https://opensource.org/licenses/CDDL-1.0.
11
* See the License for the specific language governing permissions
12
* and limitations under the License.
13
*
14
* When distributing Covered Code, include this CDDL HEADER in each
15
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16
* If applicable, add the following below this CDDL HEADER, with the
17
* fields enclosed by brackets "[]" replaced with your own identifying
18
* information: Portions Copyright [yyyy] [name of copyright owner]
19
*
20
* CDDL HEADER END
21
*/
22
23
/*
24
* Copyright (c) 2017, Intle Corporation. All rights reserved.
25
*/
26
27
#include <errno.h>
28
#include <getopt.h>
29
#include <stdio.h>
30
#include <stdlib.h>
31
#include <string.h>
32
#include <unistd.h>
33
#include <fcntl.h>
34
#include <dirent.h>
35
#include <stddef.h>
36
#include <libintl.h>
37
#include <sys/stat.h>
38
#include <sys/types.h>
39
#include <sys/list.h>
40
#include <sys/zfs_project.h>
41
42
#include "zfs_util.h"
43
#include "zfs_projectutil.h"
44
45
typedef struct zfs_project_item {
46
list_node_t zpi_list;
47
char zpi_name[0];
48
} zfs_project_item_t;
49
50
static void
51
zfs_project_item_alloc(list_t *head, const char *name)
52
{
53
zfs_project_item_t *zpi;
54
55
zpi = safe_malloc(sizeof (zfs_project_item_t) + strlen(name) + 1);
56
strcpy(zpi->zpi_name, name);
57
list_insert_tail(head, zpi);
58
}
59
60
static int
61
zfs_project_sanity_check(const char *name, zfs_project_control_t *zpc,
62
struct stat *st)
63
{
64
int ret;
65
66
ret = stat(name, st);
67
if (ret) {
68
(void) fprintf(stderr, gettext("failed to stat %s: %s\n"),
69
name, strerror(errno));
70
return (ret);
71
}
72
73
if (!S_ISREG(st->st_mode) && !S_ISDIR(st->st_mode)) {
74
(void) fprintf(stderr, gettext("only support project quota on "
75
"regular file or directory\n"));
76
return (-1);
77
}
78
79
if (!S_ISDIR(st->st_mode)) {
80
if (zpc->zpc_dironly) {
81
(void) fprintf(stderr, gettext(
82
"'-d' option on non-dir target %s\n"), name);
83
return (-1);
84
}
85
86
if (zpc->zpc_recursive) {
87
(void) fprintf(stderr, gettext(
88
"'-r' option on non-dir target %s\n"), name);
89
return (-1);
90
}
91
}
92
93
return (0);
94
}
95
96
static int
97
zfs_project_load_projid(const char *name, zfs_project_control_t *zpc)
98
{
99
zfsxattr_t fsx;
100
int ret, fd;
101
102
fd = open(name, O_RDONLY | O_NOCTTY);
103
if (fd < 0) {
104
(void) fprintf(stderr, gettext("failed to open %s: %s\n"),
105
name, strerror(errno));
106
return (fd);
107
}
108
109
ret = ioctl(fd, ZFS_IOC_FSGETXATTR, &fsx);
110
if (ret)
111
(void) fprintf(stderr,
112
gettext("failed to get xattr for %s: %s\n"),
113
name, strerror(errno));
114
else
115
zpc->zpc_expected_projid = fsx.fsx_projid;
116
117
close(fd);
118
return (ret);
119
}
120
121
static int
122
zfs_project_handle_one(const char *name, zfs_project_control_t *zpc)
123
{
124
zfsxattr_t fsx;
125
int ret, fd;
126
127
fd = open(name, O_RDONLY | O_NOCTTY);
128
if (fd < 0) {
129
if (errno == ENOENT && zpc->zpc_ignore_noent)
130
return (0);
131
132
(void) fprintf(stderr, gettext("failed to open %s: %s\n"),
133
name, strerror(errno));
134
return (fd);
135
}
136
137
ret = ioctl(fd, ZFS_IOC_FSGETXATTR, &fsx);
138
if (ret) {
139
(void) fprintf(stderr,
140
gettext("failed to get xattr for %s: %s\n"),
141
name, strerror(errno));
142
goto out;
143
}
144
145
switch (zpc->zpc_op) {
146
case ZFS_PROJECT_OP_LIST:
147
(void) printf("%5u %c %s\n", fsx.fsx_projid,
148
(fsx.fsx_xflags & ZFS_PROJINHERIT_FL) ? 'P' : '-', name);
149
goto out;
150
case ZFS_PROJECT_OP_CHECK:
151
if (fsx.fsx_projid == zpc->zpc_expected_projid &&
152
fsx.fsx_xflags & ZFS_PROJINHERIT_FL)
153
goto out;
154
155
if (!zpc->zpc_newline) {
156
char c = '\0';
157
158
(void) printf("%s%c", name, c);
159
goto out;
160
}
161
162
if (fsx.fsx_projid != zpc->zpc_expected_projid)
163
(void) printf("%s - project ID is not set properly "
164
"(%u/%u)\n", name, fsx.fsx_projid,
165
(uint32_t)zpc->zpc_expected_projid);
166
167
if (!(fsx.fsx_xflags & ZFS_PROJINHERIT_FL))
168
(void) printf("%s - project inherit flag is not set\n",
169
name);
170
171
goto out;
172
case ZFS_PROJECT_OP_CLEAR:
173
if (!(fsx.fsx_xflags & ZFS_PROJINHERIT_FL) &&
174
(zpc->zpc_keep_projid ||
175
fsx.fsx_projid == ZFS_DEFAULT_PROJID))
176
goto out;
177
178
fsx.fsx_xflags &= ~ZFS_PROJINHERIT_FL;
179
if (!zpc->zpc_keep_projid)
180
fsx.fsx_projid = ZFS_DEFAULT_PROJID;
181
break;
182
case ZFS_PROJECT_OP_SET:
183
if (fsx.fsx_projid == zpc->zpc_expected_projid &&
184
(!zpc->zpc_set_flag || fsx.fsx_xflags & ZFS_PROJINHERIT_FL))
185
goto out;
186
187
fsx.fsx_projid = zpc->zpc_expected_projid;
188
if (zpc->zpc_set_flag)
189
fsx.fsx_xflags |= ZFS_PROJINHERIT_FL;
190
break;
191
default:
192
ASSERT(0);
193
break;
194
}
195
196
ret = ioctl(fd, ZFS_IOC_FSSETXATTR, &fsx);
197
if (ret)
198
(void) fprintf(stderr,
199
gettext("failed to set xattr for %s: %s\n"),
200
name, strerror(errno));
201
202
out:
203
close(fd);
204
return (ret);
205
}
206
207
static int
208
zfs_project_handle_dir(const char *name, zfs_project_control_t *zpc,
209
list_t *head)
210
{
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
char *fullname;
231
232
/* skip "." and ".." */
233
if (strcmp(ent->d_name, ".") == 0 ||
234
strcmp(ent->d_name, "..") == 0)
235
continue;
236
237
if (strlen(ent->d_name) + strlen(name) + 1 >= PATH_MAX) {
238
errno = ENAMETOOLONG;
239
break;
240
}
241
242
if (asprintf(&fullname, "%s/%s", name, ent->d_name) == -1) {
243
errno = ENOMEM;
244
break;
245
}
246
247
ret = zfs_project_handle_one(fullname, zpc);
248
if (!ret && zpc->zpc_recursive && ent->d_type == DT_DIR)
249
zfs_project_item_alloc(head, fullname);
250
251
free(fullname);
252
}
253
254
if (errno && !ret) {
255
ret = -errno;
256
(void) fprintf(stderr, gettext("failed to readdir %s: %s\n"),
257
name, strerror(errno));
258
}
259
260
closedir(dir);
261
return (ret);
262
}
263
264
int
265
zfs_project_handle(const char *name, zfs_project_control_t *zpc)
266
{
267
zfs_project_item_t *zpi;
268
struct stat st;
269
list_t head;
270
int ret;
271
272
ret = zfs_project_sanity_check(name, zpc, &st);
273
if (ret)
274
return (ret);
275
276
if ((zpc->zpc_op == ZFS_PROJECT_OP_SET ||
277
zpc->zpc_op == ZFS_PROJECT_OP_CHECK) &&
278
zpc->zpc_expected_projid == ZFS_INVALID_PROJID) {
279
ret = zfs_project_load_projid(name, zpc);
280
if (ret)
281
return (ret);
282
}
283
284
zpc->zpc_ignore_noent = B_FALSE;
285
ret = zfs_project_handle_one(name, zpc);
286
if (ret || !S_ISDIR(st.st_mode) || zpc->zpc_dironly ||
287
(!zpc->zpc_recursive &&
288
zpc->zpc_op != ZFS_PROJECT_OP_LIST &&
289
zpc->zpc_op != ZFS_PROJECT_OP_CHECK))
290
return (ret);
291
292
list_create(&head, sizeof (zfs_project_item_t),
293
offsetof(zfs_project_item_t, zpi_list));
294
zfs_project_item_alloc(&head, name);
295
while ((zpi = list_remove_head(&head)) != NULL) {
296
if (!ret)
297
ret = zfs_project_handle_dir(zpi->zpi_name, zpc, &head);
298
free(zpi);
299
}
300
301
return (ret);
302
}
303
304