Initial commit to Gerrit
[profile/ivi/quota.git] / edquota.c
1 /*
2  * Copyright (c) 1980, 1990 Regents of the University of California. All
3  * rights reserved.
4  * 
5  * This code is derived from software contributed to Berkeley by Robert Elz at
6  * The University of Melbourne.
7  * 
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are
10  * met: 1. Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer. 2.
12  * Redistributions in binary form must reproduce the above copyright notice,
13  * this list of conditions and the following disclaimer in the documentation
14  * and/or other materials provided with the distribution. 3. All advertising
15  * materials mentioning features or use of this software must display the
16  * following acknowledgement: This product includes software developed by the
17  * University of California, Berkeley and its contributors. 4. Neither the
18  * name of the University nor the names of its contributors may be used to
19  * endorse or promote products derived from this software without specific
20  * prior written permission.
21  * 
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
23  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
24  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25  * DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
26  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
28  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34
35 #include "config.h"
36
37 /*
38  * Disk quota editor.
39  */
40 #include <sys/types.h>
41 #include <sys/stat.h>
42 #include <errno.h>
43 #include <pwd.h>
44 #include <grp.h>
45 #include <ctype.h>
46 #include <stdio.h>
47 #include <string.h>
48 #include <signal.h>
49 #include <unistd.h>
50 #include <paths.h>
51 #include <stdlib.h>
52 #include <fcntl.h>
53 #include <getopt.h>
54
55 #include "pot.h"
56 #include "quotaops.h"
57 #include "quotasys.h"
58 #include "quotaio.h"
59 #include "common.h"
60
61 #define FL_EDIT_PERIOD 1
62 #define FL_EDIT_TIMES 2
63 #define FL_REMOTE 4
64 #define FL_NUMNAMES 8
65 #define FL_NO_MIXED_PATHS 16
66
67 char *progname;
68
69 int flags, quotatype;
70 int fmt = -1;
71 char *protoname;
72 char *dirname;
73
74 void usage(void)
75 {
76 #if defined(RPC_SETQUOTA)
77         char *rpcflag = "[-rm] ";
78 #else
79         char *rpcflag = "";
80 #endif
81         errstr(_("Usage:\n\tedquota %1$s[-u] [-F formatname] [-p username] [-f filesystem] username ...\n\
82 \tedquota %1$s-g [-F formatname] [-p groupname] [-f filesystem] groupname ...\n\
83 \tedquota %1$s[-u|g] [-F formatname] [-f filesystem] -t\n\
84 \tedquota %1$s[-u|g] [-F formatname] [-f filesystem] -T username|groupname ...\n"), rpcflag);
85         fputs(_("\n\
86 -u, --user                    edit user data\n\
87 -g, --group                   edit group data\n"), stderr);
88 #if defined(RPC_SETQUOTA)
89         fputs(_("-r, --remote                  edit remote quota (via RPC)\n\
90 -m, --no-mixed-pathnames      trim leading slashes from NFSv4 mountpoints\n"), stderr);
91 #endif
92         fputs(_("-F, --format=formatname       edit quotas of a specific format\n\
93 -p, --prototype=name          copy data from a prototype user/group\n\
94     --always-resolve          always try to resolve name, even if it is\n\
95                               composed only of digits\n\
96 -f, --filesystem=filesystem   edit data only on a specific filesystem\n\
97 -t, --edit-period             edit grace period\n\
98 -T, --edit-times              edit grace time of a user/group\n\
99 -h, --help                    display this help text and exit\n\
100 -V, --version                 display version information and exit\n\n"), stderr);
101         fprintf(stderr, _("Bugs to: %s\n"), MY_EMAIL);
102         exit(1);
103 }
104
105 int parse_options(int argc, char **argv)
106 {
107         int ret;
108         struct option long_opts[] = {
109                 { "help", 0, NULL, 'h' },
110                 { "version", 0, NULL, 'V' },
111                 { "prototype", 1, NULL, 'p' },
112                 { "user", 0, NULL, 'u' },
113                 { "group", 0, NULL, 'g' },
114                 { "format", 1, NULL, 'F' },
115                 { "filesystem", 1, NULL, 'f' },
116 #if defined(RPC_SETQUOTA)
117                 { "remote", 0, NULL, 'r' },
118                 { "no-mixed-pathnames", 0, NULL, 'm' },
119 #endif
120                 { "always-resolve", 0, NULL, 256 },
121                 { "edit-period", 0, NULL, 't' },
122                 { "edit-times", 0, NULL, 'T' },
123                 { NULL, 0, NULL, 0 }
124         };
125
126         if (argc < 2)
127                 usage();
128
129         quotatype = USRQUOTA;
130 #if defined(RPC_SETQUOTA)
131         while ((ret = getopt_long(argc, argv, "ugrmntTVp:F:f:", long_opts, NULL)) != -1) {
132 #else
133         while ((ret = getopt_long(argc, argv, "ugtTVp:F:f:", long_opts, NULL)) != -1) {
134 #endif
135                 switch (ret) {
136                   case 'p':
137                           protoname = optarg;
138                           break;
139                   case 'g':
140                           quotatype = GRPQUOTA;
141                           break;
142 #if defined(RPC_SETQUOTA)
143                   case 'n':
144                   case 'r':
145                           flags |= FL_REMOTE;
146                           break;
147                   case 'm':
148                           flags |= FL_NO_MIXED_PATHS;
149                           break;
150 #endif
151                   case 'u':
152                           quotatype = USRQUOTA;
153                           break;
154                   case 't':
155                           flags |= FL_EDIT_PERIOD;
156                           break;
157                   case 'T':
158                           flags |= FL_EDIT_TIMES;
159                           break;
160                   case 'F':
161                           if ((fmt = name2fmt(optarg)) == QF_ERROR)     /* Error? */
162                                   exit(1);
163                           break;
164                   case 'f':
165                           dirname = optarg;
166                           break;
167                   case 256:
168                           flags |= FL_NUMNAMES;
169                           break;
170                   case 'V':
171                           version();
172                           exit(0);
173                   default:
174                           usage();
175                 }
176         }
177         argc -= optind;
178
179         if (((flags & FL_EDIT_PERIOD) && argc != 0) || ((flags & FL_EDIT_TIMES) && argc < 1))
180                 usage();
181         if ((flags & (FL_EDIT_PERIOD | FL_EDIT_TIMES)) && protoname) {
182                 errstr(_("Prototype name does not make sense when editing grace period or times.\n"));
183                 usage();
184         }
185         return optind;
186 }
187
188 void copy_prototype(int argc, char **argv, struct quota_handle **handles)
189 {
190         int ret, protoid, id;
191         struct dquot *protoprivs, *curprivs, *pprivs, *cprivs;
192         
193         ret = 0;
194         protoid = name2id(protoname, quotatype, !!(flags & FL_NUMNAMES), NULL);
195         protoprivs = getprivs(protoid, handles, 0);
196         while (argc-- > 0) {
197                 id = name2id(*argv, quotatype, !!(flags & FL_NUMNAMES), NULL);
198                 curprivs = getprivs(id, handles, 0);
199                 if (!curprivs)
200                         die(1, _("Cannot get quota information for user %s\n"), *argv);
201                 argv++;
202
203                 for (pprivs = protoprivs, cprivs = curprivs; pprivs && cprivs;
204                      pprivs = pprivs->dq_next, cprivs = cprivs->dq_next) {
205                         if (!devcmp_handles(pprivs->dq_h, cprivs->dq_h)) {
206                                 errstr(_("fsname mismatch\n"));
207                                 continue;
208                         }
209                         cprivs->dq_dqb.dqb_bsoftlimit =
210                                 pprivs->dq_dqb.dqb_bsoftlimit;
211                         cprivs->dq_dqb.dqb_bhardlimit =
212                                 pprivs->dq_dqb.dqb_bhardlimit;
213                         cprivs->dq_dqb.dqb_isoftlimit =
214                                 pprivs->dq_dqb.dqb_isoftlimit;
215                         cprivs->dq_dqb.dqb_ihardlimit =
216                                 pprivs->dq_dqb.dqb_ihardlimit;
217                         update_grace_times(cprivs);
218                 }
219                 if (putprivs(curprivs, COMMIT_LIMITS) == -1)
220                         ret = -1;
221                 freeprivs(curprivs);
222         }
223         if (dispose_handle_list(handles) == -1)
224                 ret = -1;
225         freeprivs(protoprivs);
226         exit(ret ? 1 : 0);
227 }
228
229 int main(int argc, char **argv)
230 {
231         struct dquot *curprivs;
232         int tmpfd, ret, id;
233         struct quota_handle **handles;
234         char *tmpfil, *tmpdir = NULL;
235
236         gettexton();
237         progname = basename(argv[0]);
238         ret = parse_options(argc, argv);
239         argc -= ret;
240         argv += ret;
241
242         init_kernel_interface();
243         handles = create_handle_list(dirname ? 1 : 0, dirname ? &dirname : NULL, quotatype, fmt,
244                         (flags & FL_NO_MIXED_PATHS) ? 0 : IOI_NFS_MIXED_PATHS,
245                         (flags & FL_REMOTE) ? 0 : MS_LOCALONLY);
246         if (!handles[0]) {
247                 dispose_handle_list(handles);
248                 fputs(_("No filesystems with quota detected.\n"), stderr);
249                 return 0;
250         }
251         if (protoname)
252                 copy_prototype(argc, argv, handles);
253
254         umask(077);
255         if (getuid() == geteuid() && getgid() == getegid())
256                 tmpdir = getenv("TMPDIR");
257         if (!tmpdir)
258                 tmpdir = _PATH_TMP;
259         tmpfil = smalloc(strlen(tmpdir) + strlen("/EdP.aXXXXXX") + 1);
260         strcpy(tmpfil, tmpdir);
261         strcat(tmpfil, "/EdP.aXXXXXX");
262         tmpfd = mkstemp(tmpfil);
263         if (tmpfd < 0) {
264                 errstr(_("Cannot create temporary file: %s\n"), strerror(errno));
265                 ret = -1;
266                 goto out;
267         }
268         if (fchown(tmpfd, getuid(), getgid()) < 0) {
269                 errstr(_("Cannot change owner of temporary file: %s\n"), strerror(errno));
270                 ret = -1;
271                 goto out;
272         }
273         ret = 0;
274         if (flags & FL_EDIT_PERIOD) {
275                 if (writetimes(handles, tmpfd) < 0) {
276                         errstr(_("Cannot write grace times to file.\n"));
277                         ret = -1;
278                 }
279                 if (editprivs(tmpfil) < 0) {
280                         errstr(_("Error while editing grace times.\n"));
281                         ret = -1;
282                 }
283                 if (readtimes(handles, tmpfd) < 0) {
284                         errstr(_("Failed to parse grace times file.\n"));
285                         ret = -1;
286                 }
287         }
288         else if (flags & FL_EDIT_TIMES) {
289                 for (; argc > 0; argc--, argv++) {
290                         id = name2id(*argv, quotatype, !!(flags & FL_NUMNAMES), NULL);
291                         curprivs = getprivs(id, handles, 0);
292                         if (!curprivs)
293                                 die(1, _("Cannot get quota information for user %s.\n"), *argv);
294                         if (writeindividualtimes(curprivs, tmpfd, *argv, quotatype) < 0) {
295                                 errstr(_("Cannot write individual grace times to file.\n"));
296                                 ret = -1;
297                                 continue;
298                         }
299                         if (editprivs(tmpfil) < 0) {
300                                 errstr(_("Error while editing individual grace times.\n"));
301                                 ret = -1;
302                                 continue;
303                         }
304                         if (readindividualtimes(curprivs, tmpfd) < 0) {
305                                 errstr(_("Cannot read individual grace times from file.\n"));
306                                 ret = -1;
307                                 continue;
308                         }
309                         if (putprivs(curprivs, COMMIT_TIMES) == -1)
310                                 ret = -1;
311                         freeprivs(curprivs);
312                 }
313         }
314         else {
315                 for (; argc > 0; argc--, argv++) {
316                         id = name2id(*argv, quotatype, !!(flags & FL_NUMNAMES), NULL);
317                         curprivs = getprivs(id, handles, 0);
318                         if (!curprivs)
319                                 die(1, _("Cannot get quota information for user %s.\n"), *argv);
320                         if (writeprivs(curprivs, tmpfd, *argv, quotatype) < 0) {
321                                 errstr(_("Cannot write quotas to file.\n"));
322                                 ret = -1;
323                                 continue;
324                         }
325                         if (editprivs(tmpfil) < 0) {
326                                 errstr(_("Error while editing quotas.\n"));
327                                 ret = -1;
328                                 continue;
329                         }
330                         close(tmpfd);
331                         if ((tmpfd = open(tmpfil, O_RDONLY)) < 0)
332                                 die(1, _("Cannot reopen!"));
333                         if (readprivs(curprivs, tmpfd) < 0) {
334                                 errstr(_("Cannot read quotas from file.\n"));
335                                 ret = -1;
336                                 continue;
337                         }
338                         if (putprivs(curprivs, COMMIT_LIMITS) == -1)
339                                 ret = -1;
340                         freeprivs(curprivs);
341                 }
342         }
343 out:
344         if (dispose_handle_list(handles) == -1)
345                 ret = -1;
346
347         close(tmpfd);
348         unlink(tmpfil);
349         free(tmpfil);
350         return ret ? 1 : 0;
351 }