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