]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - quota/project.c
xfs: fix maxicount division by zero error
[thirdparty/xfsprogs-dev.git] / quota / project.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (c) 2005 Silicon Graphics, Inc.
4 * All Rights Reserved.
5 */
6
7 #include "command.h"
8 #include "input.h"
9 #include "init.h"
10 #include "quota.h"
11
12 static cmdinfo_t project_cmd;
13 static prid_t prid;
14 static int recurse_depth = -1;
15
16 enum {
17 CHECK_PROJECT = 0x1,
18 SETUP_PROJECT = 0x2,
19 CLEAR_PROJECT = 0x4,
20 };
21
22 #define EXCLUDED_FILE_TYPES(x) \
23 (S_ISCHR((x)) \
24 || S_ISBLK((x)) \
25 || S_ISFIFO((x)) \
26 || S_ISLNK((x)) \
27 || S_ISSOCK((x)))
28
29 static void
30 project_help(void)
31 {
32 printf(_(
33 "\n"
34 " list projects or setup a project tree for tree quota management\n"
35 "\n"
36 " Example:\n"
37 " 'project -c logfiles'\n"
38 " (match project 'logfiles' to a directory, and setup the directory tree)\n"
39 "\n"
40 " Without arguments, report all projects found in the /etc/projects file.\n"
41 " The project quota mechanism in XFS can be used to implement a form of\n"
42 " directory tree quota, where a specified directory and all of the files\n"
43 " and subdirectories below it (i.e. a tree) can be restricted to using a\n"
44 " subset of the available space in the filesystem.\n"
45 "\n"
46 " A managed tree must be setup initially using the -s option with a project.\n"
47 " The specified project name or identifier is matched to one or more trees\n"
48 " defined in /etc/projects, and these trees are then recursively descended\n"
49 " to mark the affected inodes as being part of that tree - which sets inode\n"
50 " flags and the project identifier on every file.\n"
51 " Once this has been done, new files created in the tree will automatically\n"
52 " be accounted to the tree based on their project identifier. An attempt to\n"
53 " create a hard link to a file in the tree will only succeed if the project\n"
54 " identifier matches the project identifier for the tree. The xfs_io utility\n"
55 " can be used to set the project ID for an arbitrary file, but this can only\n"
56 " be done by a privileged user.\n"
57 "\n"
58 " A previously setup tree can be cleared from project quota control through\n"
59 " use of the -C option, which will recursively descend the tree, clearing\n"
60 " the affected inodes from project quota control.\n"
61 "\n"
62 " The -c option can be used to check whether a tree is setup, it reports\n"
63 " nothing if the tree is correct, otherwise it reports the paths of inodes\n"
64 " which do not have the project ID of the rest of the tree, or if the inode\n"
65 " flag is not set.\n"
66 "\n"
67 " The -p <path> option can be used to manually specify project path without\n"
68 " need to create /etc/projects file. This option can be used multiple times\n"
69 " to specify multiple paths. When using this option only one projid/name can\n"
70 " be specified at command line. Note that /etc/projects is also used if exists.\n"
71 "\n"
72 " The -d <depth> option allows to descend at most <depth> levels of directories\n"
73 " below the command line arguments. -d 0 means only apply the actions\n"
74 " to the top level of the projects. -d -1 means no recursion limit (default).\n"
75 "\n"
76 " The /etc/projid and /etc/projects file formats are simple, and described\n"
77 " on the xfs_quota man page.\n"
78 "\n"));
79 }
80
81 static int
82 check_project(
83 const char *path,
84 const struct stat *stat,
85 int flag,
86 struct FTW *data)
87 {
88 struct fsxattr fsx;
89 int fd;
90
91 if (recurse_depth >= 0 && data->level > recurse_depth)
92 return 0;
93
94 if (flag == FTW_NS ){
95 exitcode = 1;
96 fprintf(stderr, _("%s: cannot stat file %s\n"), progname, path);
97 return 0;
98 }
99 if (EXCLUDED_FILE_TYPES(stat->st_mode)) {
100 fprintf(stderr, _("%s: skipping special file %s\n"), progname, path);
101 return 0;
102 }
103
104 if ((fd = open(path, O_RDONLY|O_NOCTTY)) == -1) {
105 exitcode = 1;
106 fprintf(stderr, _("%s: cannot open %s: %s\n"),
107 progname, path, strerror(errno));
108 } else if ((xfsctl(path, fd, FS_IOC_FSGETXATTR, &fsx)) < 0) {
109 exitcode = 1;
110 fprintf(stderr, _("%s: cannot get flags on %s: %s\n"),
111 progname, path, strerror(errno));
112 } else {
113 if (fsx.fsx_projid != prid)
114 printf(_("%s - project identifier is not set"
115 " (inode=%u, tree=%u)\n"),
116 path, fsx.fsx_projid, (unsigned int)prid);
117 if (!(fsx.fsx_xflags & FS_XFLAG_PROJINHERIT) && S_ISDIR(stat->st_mode))
118 printf(_("%s - project inheritance flag is not set\n"),
119 path);
120 }
121 if (fd != -1)
122 close(fd);
123 return 0;
124 }
125
126 static int
127 clear_project(
128 const char *path,
129 const struct stat *stat,
130 int flag,
131 struct FTW *data)
132 {
133 struct fsxattr fsx;
134 int fd;
135
136 if (recurse_depth >= 0 && data->level > recurse_depth)
137 return 0;
138
139 if (flag == FTW_NS ){
140 exitcode = 1;
141 fprintf(stderr, _("%s: cannot stat file %s\n"), progname, path);
142 return 0;
143 }
144 if (EXCLUDED_FILE_TYPES(stat->st_mode)) {
145 fprintf(stderr, _("%s: skipping special file %s\n"), progname, path);
146 return 0;
147 }
148
149 if ((fd = open(path, O_RDONLY|O_NOCTTY)) == -1) {
150 exitcode = 1;
151 fprintf(stderr, _("%s: cannot open %s: %s\n"),
152 progname, path, strerror(errno));
153 return 0;
154 } else if (xfsctl(path, fd, FS_IOC_FSGETXATTR, &fsx) < 0) {
155 exitcode = 1;
156 fprintf(stderr, _("%s: cannot get flags on %s: %s\n"),
157 progname, path, strerror(errno));
158 close(fd);
159 return 0;
160 }
161
162 fsx.fsx_projid = 0;
163 fsx.fsx_xflags &= ~FS_XFLAG_PROJINHERIT;
164 if (xfsctl(path, fd, FS_IOC_FSSETXATTR, &fsx) < 0) {
165 exitcode = 1;
166 fprintf(stderr, _("%s: cannot clear project on %s: %s\n"),
167 progname, path, strerror(errno));
168 }
169 close(fd);
170 return 0;
171 }
172
173 static int
174 setup_project(
175 const char *path,
176 const struct stat *stat,
177 int flag,
178 struct FTW *data)
179 {
180 struct fsxattr fsx;
181 int fd;
182
183 if (recurse_depth >= 0 && data->level > recurse_depth)
184 return 0;
185
186 if (flag == FTW_NS ){
187 exitcode = 1;
188 fprintf(stderr, _("%s: cannot stat file %s\n"), progname, path);
189 return 0;
190 }
191 if (EXCLUDED_FILE_TYPES(stat->st_mode)) {
192 fprintf(stderr, _("%s: skipping special file %s\n"), progname, path);
193 return 0;
194 }
195
196 if ((fd = open(path, O_RDONLY|O_NOCTTY)) == -1) {
197 exitcode = 1;
198 fprintf(stderr, _("%s: cannot open %s: %s\n"),
199 progname, path, strerror(errno));
200 return 0;
201 } else if (xfsctl(path, fd, FS_IOC_FSGETXATTR, &fsx) < 0) {
202 exitcode = 1;
203 fprintf(stderr, _("%s: cannot get flags on %s: %s\n"),
204 progname, path, strerror(errno));
205 close(fd);
206 return 0;
207 }
208
209 fsx.fsx_projid = prid;
210 fsx.fsx_xflags |= FS_XFLAG_PROJINHERIT;
211 if (xfsctl(path, fd, FS_IOC_FSSETXATTR, &fsx) < 0) {
212 exitcode = 1;
213 fprintf(stderr, _("%s: cannot set project on %s: %s\n"),
214 progname, path, strerror(errno));
215 }
216 close(fd);
217 return 0;
218 }
219
220 static void
221 project_operations(
222 char *project,
223 char *dir,
224 int type)
225 {
226 switch (type) {
227 case CHECK_PROJECT:
228 printf(_("Checking project %s (path %s)...\n"), project, dir);
229 nftw(dir, check_project, 100, FTW_PHYS|FTW_MOUNT);
230 break;
231 case SETUP_PROJECT:
232 printf(_("Setting up project %s (path %s)...\n"), project, dir);
233 nftw(dir, setup_project, 100, FTW_PHYS|FTW_MOUNT);
234 break;
235 case CLEAR_PROJECT:
236 printf(_("Clearing project %s (path %s)...\n"), project, dir);
237 nftw(dir, clear_project, 100, FTW_PHYS|FTW_MOUNT);
238 break;
239 }
240 }
241
242 static void
243 project(
244 char *project,
245 int type)
246 {
247 fs_cursor_t cursor;
248 fs_path_t *path;
249 int count = 0;
250
251 fs_cursor_initialise(NULL, FS_PROJECT_PATH, &cursor);
252 while ((path = fs_cursor_next_entry(&cursor))) {
253 if (prid != path->fs_prid && path->fs_prid != -1)
254 continue;
255 project_operations(project, path->fs_dir, type);
256 count++;
257 }
258
259 printf(_("Processed %d (%s and cmdline) paths for project %s with "
260 "recursion depth %s (%d).\n"),
261 count, projects_file, project,
262 recurse_depth < 0 ? _("infinite") : _("limited"), recurse_depth);
263 }
264
265 static int
266 project_f(
267 int argc,
268 char **argv)
269 {
270 int c, type = 0, ispath = 0;
271
272 while ((c = getopt(argc, argv, "cd:p:sC")) != EOF) {
273 switch (c) {
274 case 'c':
275 type = CHECK_PROJECT;
276 break;
277 case 'd':
278 recurse_depth = atoi(optarg);
279 if (recurse_depth < 0)
280 recurse_depth = -1;
281 break;
282 case 'p':
283 ispath = 1;
284 fs_table_insert_project_path(optarg, -1);
285 break;
286 case 's':
287 type = SETUP_PROJECT;
288 break;
289 case 'C':
290 type = CLEAR_PROJECT;
291 break;
292 default:
293 return command_usage(&project_cmd);
294 }
295 }
296
297 if (argc == optind)
298 return command_usage(&project_cmd);
299
300 /* no options - just check the given projects */
301 if (!type)
302 type = CHECK_PROJECT;
303
304 setprfiles();
305 if (!ispath && access(projects_file, F_OK) != 0) {
306 exitcode = 1;
307 fprintf(stderr, _("projects file \"%s\" doesn't exist\n"),
308 projects_file);
309 return 0;
310 }
311
312 if (ispath && argc - optind > 1) {
313 exitcode = 1;
314 fprintf(stderr, _("%s: only one projid/name can be specified "
315 "when using -p <path>, %d found.\n"),
316 progname, argc - optind);
317 return 0;
318 }
319
320 while (argc > optind) {
321 prid = prid_from_string(argv[optind]);
322 if (prid == -1) {
323 exitcode = 1;
324 fprintf(stderr, _("%s - no such project in %s "
325 "or invalid project number\n"),
326 argv[optind], projects_file);
327 } else
328 project(argv[optind], type);
329 optind++;
330 }
331
332 return 0;
333 }
334
335 void
336 project_init(void)
337 {
338 project_cmd.name = "project";
339 project_cmd.altname = "tree";
340 project_cmd.cfunc = project_f;
341 project_cmd.args = _("[-c|-s|-C|-d <depth>|-p <path>] project ...");
342 project_cmd.argmin = 1;
343 project_cmd.argmax = -1;
344 project_cmd.oneline = _("check, setup or clear project quota trees");
345 project_cmd.help = project_help;
346 project_cmd.flags = CMD_FLAG_FOREIGN_OK;
347
348 if (expert)
349 add_command(&project_cmd);
350 }