]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - quota/project.c
xfs_quota: Don't ignore every error when asking for quota
[thirdparty/xfsprogs-dev.git] / quota / project.c
CommitLineData
5aead01d 1/*
da23017d
NS
2 * Copyright (c) 2005 Silicon Graphics, Inc.
3 * All Rights Reserved.
5aead01d 4 *
da23017d
NS
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
5aead01d
NS
7 * published by the Free Software Foundation.
8 *
da23017d
NS
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.
5aead01d 13 *
da23017d
NS
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
5aead01d
NS
17 */
18
19#include <xfs/command.h>
20#include <xfs/input.h>
21#include "init.h"
22#include "quota.h"
23
24static cmdinfo_t project_cmd;
25static prid_t prid;
26
27enum {
28 CHECK_PROJECT = 0x1,
29 SETUP_PROJECT = 0x2,
30 CLEAR_PROJECT = 0x4,
31};
32
366127f7
DD
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
5aead01d
NS
40static void
41project_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
83static int
84check_project(
85 const char *path,
86 const struct stat *stat,
366127f7 87 int flag,
5aead01d
NS
88 struct FTW *data)
89{
90 struct fsxattr fsx;
5aead01d
NS
91 int fd;
92
366127f7 93 if (flag == FTW_NS ){
e3210fd8 94 exitcode = 1;
366127f7
DD
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
e3210fd8
AM
103 if ((fd = open(path, O_RDONLY|O_NOCTTY)) == -1) {
104 exitcode = 1;
5aead01d
NS
105 fprintf(stderr, _("%s: cannot open %s: %s\n"),
106 progname, path, strerror(errno));
e3210fd8
AM
107 } else if ((xfsctl(path, fd, XFS_IOC_FSGETXATTR, &fsx)) < 0) {
108 exitcode = 1;
5aead01d
NS
109 fprintf(stderr, _("%s: cannot get flags on %s: %s\n"),
110 progname, path, strerror(errno));
e3210fd8 111 } else {
764b1982 112 if (fsx.fsx_projid != prid)
5aead01d
NS
113 printf(_("%s - project identifier is not set"
114 " (inode=%u, tree=%u)\n"),
2a1888c5 115 path, fsx.fsx_projid, (unsigned int)prid);
5aead01d
NS
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
125static int
126clear_project(
127 const char *path,
128 const struct stat *stat,
366127f7 129 int flag,
5aead01d
NS
130 struct FTW *data)
131{
132 struct fsxattr fsx;
133 int fd;
134
366127f7 135 if (flag == FTW_NS ){
e3210fd8 136 exitcode = 1;
366127f7
DD
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
5aead01d 145 if ((fd = open(path, O_RDONLY|O_NOCTTY)) == -1) {
e3210fd8 146 exitcode = 1;
5aead01d
NS
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) {
e3210fd8 151 exitcode = 1;
5aead01d
NS
152 fprintf(stderr, _("%s: cannot get flags on %s: %s\n"),
153 progname, path, strerror(errno));
154 close(fd);
155 return 0;
156 }
157
764b1982 158 fsx.fsx_projid = 0;
5aead01d 159 fsx.fsx_xflags &= ~XFS_XFLAG_PROJINHERIT;
e3210fd8
AM
160 if (xfsctl(path, fd, XFS_IOC_FSSETXATTR, &fsx) < 0) {
161 exitcode = 1;
764b1982 162 fprintf(stderr, _("%s: cannot clear project on %s: %s\n"),
5aead01d 163 progname, path, strerror(errno));
e3210fd8 164 }
5aead01d
NS
165 close(fd);
166 return 0;
167}
168
169static int
170setup_project(
171 const char *path,
172 const struct stat *stat,
366127f7 173 int flag,
5aead01d
NS
174 struct FTW *data)
175{
176 struct fsxattr fsx;
177 int fd;
178
366127f7 179 if (flag == FTW_NS ){
e3210fd8 180 exitcode = 1;
366127f7
DD
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
5aead01d 189 if ((fd = open(path, O_RDONLY|O_NOCTTY)) == -1) {
e3210fd8 190 exitcode = 1;
5aead01d
NS
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) {
e3210fd8 195 exitcode = 1;
5aead01d
NS
196 fprintf(stderr, _("%s: cannot get flags on %s: %s\n"),
197 progname, path, strerror(errno));
198 close(fd);
199 return 0;
200 }
201
764b1982 202 fsx.fsx_projid = prid;
5aead01d 203 fsx.fsx_xflags |= XFS_XFLAG_PROJINHERIT;
e3210fd8
AM
204 if (xfsctl(path, fd, XFS_IOC_FSSETXATTR, &fsx) < 0) {
205 exitcode = 1;
764b1982 206 fprintf(stderr, _("%s: cannot set project on %s: %s\n"),
5aead01d 207 progname, path, strerror(errno));
e3210fd8 208 }
5aead01d
NS
209 close(fd);
210 return 0;
211}
212
213static void
214project_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
235static void
236project(
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
256static int
257project_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) {
e3210fd8 288 exitcode = 1;
5aead01d
NS
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]);
e3210fd8
AM
296 if (prid == -1) {
297 exitcode = 1;
5aead01d
NS
298 fprintf(stderr, _("%s - no such project in %s\n"),
299 argv[optind], projects_file);
e3210fd8 300 } else
5aead01d
NS
301 project(argv[optind], type);
302 optind++;
303 }
304
305 return 0;
306}
307
308void
309project_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}