]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - quota/project.c
xfs: fix maxicount division by zero error
[thirdparty/xfsprogs-dev.git] / quota / project.c
CommitLineData
959ef981 1// SPDX-License-Identifier: GPL-2.0
5aead01d 2/*
da23017d
NS
3 * Copyright (c) 2005 Silicon Graphics, Inc.
4 * All Rights Reserved.
5aead01d
NS
5 */
6
6b803e5a
CH
7#include "command.h"
8#include "input.h"
5aead01d
NS
9#include "init.h"
10#include "quota.h"
11
12static cmdinfo_t project_cmd;
13static prid_t prid;
544f6ba0 14static int recurse_depth = -1;
5aead01d
NS
15
16enum {
17 CHECK_PROJECT = 0x1,
18 SETUP_PROJECT = 0x2,
19 CLEAR_PROJECT = 0x4,
20};
21
366127f7 22#define EXCLUDED_FILE_TYPES(x) \
b9d2d4a2 23 (S_ISCHR((x)) \
366127f7
DD
24 || S_ISBLK((x)) \
25 || S_ISFIFO((x)) \
26 || S_ISLNK((x)) \
b9d2d4a2 27 || S_ISSOCK((x)))
366127f7 28
5aead01d
NS
29static void
30project_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"
8bfb5eac 46" A managed tree must be setup initially using the -s option with a project.\n"
5aead01d
NS
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"
ff1f79a7 54" identifier matches the project identifier for the tree. The xfs_io utility\n"
5aead01d
NS
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"
02b26a6d
AM
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"
544f6ba0
AM
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"
5aead01d
NS
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
81static int
82check_project(
83 const char *path,
84 const struct stat *stat,
366127f7 85 int flag,
5aead01d
NS
86 struct FTW *data)
87{
88 struct fsxattr fsx;
5aead01d
NS
89 int fd;
90
544f6ba0 91 if (recurse_depth >= 0 && data->level > recurse_depth)
1472d00f 92 return 0;
544f6ba0 93
366127f7 94 if (flag == FTW_NS ){
e3210fd8 95 exitcode = 1;
366127f7
DD
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
e3210fd8
AM
104 if ((fd = open(path, O_RDONLY|O_NOCTTY)) == -1) {
105 exitcode = 1;
5aead01d
NS
106 fprintf(stderr, _("%s: cannot open %s: %s\n"),
107 progname, path, strerror(errno));
83f4b5ac 108 } else if ((xfsctl(path, fd, FS_IOC_FSGETXATTR, &fsx)) < 0) {
e3210fd8 109 exitcode = 1;
5aead01d
NS
110 fprintf(stderr, _("%s: cannot get flags on %s: %s\n"),
111 progname, path, strerror(errno));
e3210fd8 112 } else {
764b1982 113 if (fsx.fsx_projid != prid)
5aead01d
NS
114 printf(_("%s - project identifier is not set"
115 " (inode=%u, tree=%u)\n"),
2a1888c5 116 path, fsx.fsx_projid, (unsigned int)prid);
b136f48b 117 if (!(fsx.fsx_xflags & FS_XFLAG_PROJINHERIT) && S_ISDIR(stat->st_mode))
5aead01d
NS
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
126static int
127clear_project(
128 const char *path,
129 const struct stat *stat,
366127f7 130 int flag,
5aead01d
NS
131 struct FTW *data)
132{
133 struct fsxattr fsx;
134 int fd;
135
544f6ba0 136 if (recurse_depth >= 0 && data->level > recurse_depth)
1472d00f 137 return 0;
544f6ba0 138
366127f7 139 if (flag == FTW_NS ){
e3210fd8 140 exitcode = 1;
366127f7
DD
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
5aead01d 149 if ((fd = open(path, O_RDONLY|O_NOCTTY)) == -1) {
e3210fd8 150 exitcode = 1;
5aead01d
NS
151 fprintf(stderr, _("%s: cannot open %s: %s\n"),
152 progname, path, strerror(errno));
153 return 0;
83f4b5ac 154 } else if (xfsctl(path, fd, FS_IOC_FSGETXATTR, &fsx) < 0) {
e3210fd8 155 exitcode = 1;
5aead01d
NS
156 fprintf(stderr, _("%s: cannot get flags on %s: %s\n"),
157 progname, path, strerror(errno));
158 close(fd);
159 return 0;
160 }
161
764b1982 162 fsx.fsx_projid = 0;
83f4b5ac
DC
163 fsx.fsx_xflags &= ~FS_XFLAG_PROJINHERIT;
164 if (xfsctl(path, fd, FS_IOC_FSSETXATTR, &fsx) < 0) {
e3210fd8 165 exitcode = 1;
764b1982 166 fprintf(stderr, _("%s: cannot clear project on %s: %s\n"),
5aead01d 167 progname, path, strerror(errno));
e3210fd8 168 }
5aead01d
NS
169 close(fd);
170 return 0;
171}
172
173static int
174setup_project(
175 const char *path,
176 const struct stat *stat,
366127f7 177 int flag,
5aead01d
NS
178 struct FTW *data)
179{
180 struct fsxattr fsx;
181 int fd;
182
544f6ba0 183 if (recurse_depth >= 0 && data->level > recurse_depth)
1472d00f 184 return 0;
544f6ba0 185
366127f7 186 if (flag == FTW_NS ){
e3210fd8 187 exitcode = 1;
366127f7
DD
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
5aead01d 196 if ((fd = open(path, O_RDONLY|O_NOCTTY)) == -1) {
e3210fd8 197 exitcode = 1;
5aead01d
NS
198 fprintf(stderr, _("%s: cannot open %s: %s\n"),
199 progname, path, strerror(errno));
200 return 0;
83f4b5ac 201 } else if (xfsctl(path, fd, FS_IOC_FSGETXATTR, &fsx) < 0) {
e3210fd8 202 exitcode = 1;
5aead01d
NS
203 fprintf(stderr, _("%s: cannot get flags on %s: %s\n"),
204 progname, path, strerror(errno));
205 close(fd);
206 return 0;
207 }
208
764b1982 209 fsx.fsx_projid = prid;
83f4b5ac
DC
210 fsx.fsx_xflags |= FS_XFLAG_PROJINHERIT;
211 if (xfsctl(path, fd, FS_IOC_FSSETXATTR, &fsx) < 0) {
e3210fd8 212 exitcode = 1;
764b1982 213 fprintf(stderr, _("%s: cannot set project on %s: %s\n"),
5aead01d 214 progname, path, strerror(errno));
e3210fd8 215 }
5aead01d
NS
216 close(fd);
217 return 0;
218}
219
220static void
221project_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);
544f6ba0 229 nftw(dir, check_project, 100, FTW_PHYS|FTW_MOUNT);
5aead01d
NS
230 break;
231 case SETUP_PROJECT:
232 printf(_("Setting up project %s (path %s)...\n"), project, dir);
544f6ba0 233 nftw(dir, setup_project, 100, FTW_PHYS|FTW_MOUNT);
5aead01d
NS
234 break;
235 case CLEAR_PROJECT:
236 printf(_("Clearing project %s (path %s)...\n"), project, dir);
544f6ba0 237 nftw(dir, clear_project, 100, FTW_PHYS|FTW_MOUNT);
5aead01d
NS
238 break;
239 }
240}
241
242static void
243project(
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))) {
02b26a6d 253 if (prid != path->fs_prid && path->fs_prid != -1)
5aead01d
NS
254 continue;
255 project_operations(project, path->fs_dir, type);
256 count++;
257 }
258
272f4db5
AM
259 printf(_("Processed %d (%s and cmdline) paths for project %s with "
260 "recursion depth %s (%d).\n"),
544f6ba0
AM
261 count, projects_file, project,
262 recurse_depth < 0 ? _("infinite") : _("limited"), recurse_depth);
5aead01d
NS
263}
264
265static int
266project_f(
267 int argc,
268 char **argv)
269{
02b26a6d 270 int c, type = 0, ispath = 0;
5aead01d 271
02b26a6d 272 while ((c = getopt(argc, argv, "cd:p:sC")) != EOF) {
5aead01d
NS
273 switch (c) {
274 case 'c':
275 type = CHECK_PROJECT;
276 break;
544f6ba0
AM
277 case 'd':
278 recurse_depth = atoi(optarg);
279 if (recurse_depth < 0)
280 recurse_depth = -1;
281 break;
02b26a6d
AM
282 case 'p':
283 ispath = 1;
284 fs_table_insert_project_path(optarg, -1);
285 break;
5aead01d
NS
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();
02b26a6d 305 if (!ispath && access(projects_file, F_OK) != 0) {
e3210fd8 306 exitcode = 1;
5aead01d
NS
307 fprintf(stderr, _("projects file \"%s\" doesn't exist\n"),
308 projects_file);
309 return 0;
310 }
311
02b26a6d
AM
312 if (ispath && argc - optind > 1) {
313 exitcode = 1;
272f4db5
AM
314 fprintf(stderr, _("%s: only one projid/name can be specified "
315 "when using -p <path>, %d found.\n"),
316 progname, argc - optind);
02b26a6d
AM
317 return 0;
318 }
319
5aead01d
NS
320 while (argc > optind) {
321 prid = prid_from_string(argv[optind]);
e3210fd8
AM
322 if (prid == -1) {
323 exitcode = 1;
272f4db5
AM
324 fprintf(stderr, _("%s - no such project in %s "
325 "or invalid project number\n"),
5aead01d 326 argv[optind], projects_file);
e3210fd8 327 } else
5aead01d
NS
328 project(argv[optind], type);
329 optind++;
330 }
331
332 return 0;
333}
334
335void
336project_init(void)
337{
ad765595
AM
338 project_cmd.name = "project";
339 project_cmd.altname = "tree";
5aead01d 340 project_cmd.cfunc = project_f;
02b26a6d 341 project_cmd.args = _("[-c|-s|-C|-d <depth>|-p <path>] project ...");
5aead01d
NS
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;
29647c8d 346 project_cmd.flags = CMD_FLAG_FOREIGN_OK;
5aead01d
NS
347
348 if (expert)
349 add_command(&project_cmd);
350}