Initial commit to Gerrit
[profile/ivi/quota.git] / quotacheck.c
1 /*
2  *
3  *      Utility to check disk quotas
4  *
5  *      Some parts of this utility are copied from old quotacheck by
6  *      Marco van Wieringen <mvw@planets.elm.net> and Edvard Tuinder <ed@elm.net>
7  * 
8  *      New quota format implementation - Jan Kara <jack@suse.cz> - Sponsored by SuSE CR
9  */
10
11 #include "config.h"
12
13 #include <dirent.h>
14 #include <stdio.h>
15 #include <string.h>
16 #include <stdarg.h>
17 #include <getopt.h>
18 #include <limits.h>
19 #include <unistd.h>
20 #include <stdlib.h>
21 #include <errno.h>
22
23 #include <sys/stat.h>
24 #include <sys/types.h>
25 #include <sys/file.h>
26 #include <sys/statfs.h>
27 #include <sys/ioctl.h>
28 #include <sys/mount.h>
29 #include <sys/utsname.h>
30
31 #if defined(HAVE_EXT2_INCLUDE)
32 #include <linux/types.h>
33 #include <ext2fs/ext2fs.h>
34 #endif
35
36 #include "pot.h"
37 #include "common.h"
38 #include "quotaio.h"
39 #include "quotasys.h"
40 #include "mntopt.h"
41 #include "bylabel.h"
42 #include "quotacheck.h"
43 #include "quotaops.h"
44
45 #ifndef HAVE_EXT2_INO_T
46 typedef ino_t ext2_ino_t;
47 #endif
48
49 #define LINKSHASHSIZE 16384     /* Size of hashtable for hardlinked inodes */
50 #define DQUOTHASHSIZE 32768     /* Size of hashtable for dquots from file */
51
52 struct dlinks {
53         ino_t i_num;
54         struct dlinks *next;
55 };
56
57 struct dirs {
58         char *dir_name;
59         struct dirs *next;
60 };
61
62 #define BITS_SIZE 4             /* sizeof(bits) == 5 */
63 #define BLIT_RATIO 10           /* Blit in just 1/10 of blit() calls */
64
65 dev_t cur_dev;                  /* Device we are working on */
66 int files_done, dirs_done;
67 int flags, fmt = -1, cfmt;      /* Options from command line; Quota format to use spec. by user; Actual format to check */
68 int uwant, gwant, ucheck, gcheck;       /* Does user want to check user/group quota; Do we check user/group quota? */
69 char *mntpoint;                 /* Mountpoint to check */
70 char *progname;
71 struct util_dqinfo old_info[MAXQUOTAS]; /* Loaded infos */
72
73 char extensions[MAXQUOTAS + 2][20] = INITQFNAMES;       /* Extensions depending on quota type */
74 char *basenames[] = INITQFBASENAMES;    /* Names of quota files */
75
76 #ifdef DEBUG_MALLOC
77 size_t malloc_mem = 0;
78 size_t free_mem = 0;
79 #endif
80
81 struct dquot *dquot_hash[MAXQUOTAS][DQUOTHASHSIZE];
82 struct dlinks *links_hash[MAXQUOTAS][DQUOTHASHSIZE];
83
84 /*
85  * Ok check each memory allocation.
86  */
87 void *xmalloc(size_t size)
88 {
89         void *ptr;
90
91 #ifdef DEBUG_MALLOC
92         malloc_mem += size;
93 #endif
94         ptr = malloc(size);
95         if (!ptr)
96                 die(3, _("Not enough memory.\n"));
97         memset(ptr, 0, size);
98         return (ptr);
99 }
100
101 void debug(int df, char *fmtstr, ...)
102 {
103         va_list args;
104
105         if (!(flags & df))
106                 return;
107
108         fprintf(stderr, "%s: ", progname);
109         va_start(args, fmtstr);
110         vfprintf(stderr, fmtstr, args);
111         va_end(args);
112 }
113
114 /* Compute hashvalue for given inode number */
115 static inline uint hash_ino(uint i_num)
116 {
117         return ((i_num ^ (i_num << 16)) * 997) & (LINKSHASHSIZE - 1);
118 }
119
120 /*
121  * Store a hardlinked inode as we don't want to count it more then once.
122  */
123 static int store_dlinks(int type, ino_t i_num)
124 {
125         struct dlinks *lptr;
126         uint hash = hash_ino(i_num);
127
128         debug(FL_DEBUG, _("Adding hardlink for ino %llu\n"), (unsigned long long)i_num);
129
130         for (lptr = links_hash[type][hash]; lptr; lptr = lptr->next)
131                 if (lptr->i_num == i_num)
132                         return 1;
133
134         lptr = (struct dlinks *)xmalloc(sizeof(struct dlinks));
135
136         lptr->i_num = i_num;
137         lptr->next = links_hash[type][hash];
138         links_hash[type][hash] = lptr;
139         return 0;
140 }
141
142 /* Hash given id */
143 static inline uint hash_dquot(uint id)
144 {
145         return ((id ^ (id << 16)) * 997) & (DQUOTHASHSIZE - 1);
146 }
147
148 /*
149  * Do a lookup of a type of quota for a specific id. Use short cut with
150  * most recently used dquot struct pointer.
151  */
152 struct dquot *lookup_dquot(qid_t id, int type)
153 {
154         struct dquot *lptr;
155         uint hash = hash_dquot(id);
156
157         for (lptr = dquot_hash[type][hash]; lptr != NODQUOT; lptr = lptr->dq_next)
158                 if (lptr->dq_id == id)
159                         return lptr;
160         return NODQUOT;
161 }
162
163 /*
164  * Add a new dquot for a new id to the list.
165  */
166 struct dquot *add_dquot(qid_t id, int type)
167 {
168         struct dquot *lptr;
169         uint hash = hash_dquot(id);
170
171         debug(FL_DEBUG, _("Adding dquot structure type %s for %d\n"), type2name(type), (int)id);
172
173         lptr = (struct dquot *)xmalloc(sizeof(struct dquot));
174
175         lptr->dq_id = id;
176         lptr->dq_next = dquot_hash[type][hash];
177         dquot_hash[type][hash] = lptr;
178         lptr->dq_dqb.dqb_btime = lptr->dq_dqb.dqb_itime = (time_t) 0;
179
180         return lptr;
181 }
182
183 /*
184  * Add a number of blocks and inodes to a quota.
185  */
186 static void add_to_quota(int type, ino_t i_num, uid_t i_uid, gid_t i_gid, mode_t i_mode,
187                          nlink_t i_nlink, loff_t i_space, int need_remember)
188 {
189         qid_t wanted;
190         struct dquot *lptr;
191
192         if (type == USRQUOTA)
193                 wanted = i_uid;
194         else
195                 wanted = i_gid;
196
197         if ((lptr = lookup_dquot(wanted, type)) == NODQUOT)
198                 lptr = add_dquot(wanted, type);
199
200         if (i_nlink != 1 && need_remember)
201                 if (store_dlinks(type, i_num))  /* Did we already count this inode? */
202                         return;
203         lptr->dq_dqb.dqb_curinodes++;
204         lptr->dq_dqb.dqb_curspace += i_space;;
205 }
206
207 /*
208  * Clean up all list from a previous run.
209  */
210 static void remove_list(void)
211 {
212         int cnt;
213         uint i;
214         struct dquot *dquot, *dquot_free;
215         struct dlinks *dlink, *dlink_free;
216
217         for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
218                 for (i = 0; i < DQUOTHASHSIZE; i++) {
219                         dquot = dquot_hash[cnt][i];
220                         while (dquot != NODQUOT) {
221                                 dquot_free = dquot;
222                                 dquot = dquot->dq_next;
223 #ifdef DEBUG_MALLOC
224                                 free_mem += sizeof(struct dquot);
225 #endif
226                                 free(dquot_free);
227                         }
228                         dquot_hash[cnt][i] = NODQUOT;
229                 }
230                 for (i = 0; i < LINKSHASHSIZE; i++) {
231                         dlink = links_hash[cnt][i];
232                         while (dlink) {
233                                 dlink_free = dlink;
234                                 dlink = dlink->next;
235 #ifdef DEBUG_MALLOC
236                                 free_mem += sizeof(struct dlinks);
237 #endif
238                                 free(dlink_free);
239                         }
240                         links_hash[cnt][i] = NULL;
241                 }
242         }
243 }
244
245 /* Get size used by file */
246 static loff_t getqsize(char *fname, struct stat *st)
247 {
248         static char ioctl_fail_warn;
249         int fd;
250         loff_t size;
251
252         if (S_ISLNK(st->st_mode))       /* There's no way to do ioctl() on links... */
253                 return st->st_blocks << 9;
254         if (!S_ISDIR(st->st_mode) && !S_ISREG(st->st_mode))
255                 return st->st_blocks << 9;
256         if ((fd = open(fname, O_RDONLY)) == -1)
257                 die(2, _("Cannot open file %s: %s\n"), fname, strerror(errno));
258         if (ioctl(fd, FIOQSIZE, &size) == -1) {
259                 size = st->st_blocks << 9;
260                 if (!ioctl_fail_warn) {
261                         ioctl_fail_warn = 1;
262                         fputs(_("Cannot get exact used space... Results might be inaccurate.\n"), stderr);
263                 }
264         }
265         close(fd);
266         return size;
267 }
268
269 /*
270  * Show a blitting cursor as means of visual progress indicator.
271  */
272 static inline void blit(char *msg)
273 {
274         static int bitc = 0;
275         static const char bits[] = "|/-\\";
276         static int slow_down;
277
278         if (flags & FL_VERYVERBOSE && msg) {
279                 int len = strlen(msg);
280                 
281                 putchar('\r');
282                 printf("%.70s", msg);
283                 if (len > 70)
284                         fputs("...", stdout);
285                 else
286                         printf("%*s",73-len, "");
287         }
288         if (flags & FL_VERYVERBOSE || ++slow_down >= BLIT_RATIO) {
289                 putchar(bits[bitc]);
290                 putchar('\b');
291                 fflush(stdout);
292                 bitc++;
293                 bitc %= BITS_SIZE;
294                 slow_down = 0;
295         }
296 }
297
298 static void usage(void)
299 {
300         printf(_("Utility for checking and repairing quota files.\n%s [-gucbfinvdmMR] [-F <quota-format>] filesystem|-a\n\n\
301 -u, --user                check user files\n\
302 -g, --group               check group files\n\
303 -c, --create-files        create new quota files\n\
304 -b, --backup              create backups of old quota files\n\
305 -f, --force               force check even if quotas are enabled\n\
306 -i, --interactive         interactive mode\n\
307 -n, --use-first-dquot     use the first copy of duplicated structure\n\
308 -v, --verbose             print more information\n\
309 -d, --debug               print even more messages\n\
310 -m, --no-remount          do not remount filesystem read-only\n\
311 -M, --try-remount         try remounting filesystem read-only,\n\
312                           continue even if it fails\n\
313 -R, --exclude-root        exclude root when checking all filesystems\n\
314 -F, --format=formatname   check quota files of specific format\n\
315 -a, --all                 check all filesystems\n\
316 -h, --help                display this message and exit\n\
317 -V, --version             display version information and exit\n\n"), progname);
318         printf(_("Bugs to %s\n"), MY_EMAIL);
319         exit(1);
320 }
321
322 static void parse_options(int argcnt, char **argstr)
323 {
324         int ret;
325         struct option long_opts[] = {
326                 { "version", 0, NULL, 'V' },
327                 { "help", 0, NULL, 'h' },
328                 { "backup", 0, NULL, 'b' },
329                 { "create-files", 0, NULL, 'c' },
330                 { "verbose", 0, NULL, 'v' },
331                 { "debug", 0, NULL, 'd' },
332                 { "user", 0, NULL, 'u' },
333                 { "group", 0, NULL, 'g' },
334                 { "interactive", 0, NULL, 'i' },
335                 { "use-first-dquot", 0, NULL, 'n' },
336                 { "force", 0, NULL, 'f' },
337                 { "format", 1, NULL, 'F' },
338                 { "no-remount", 0, NULL, 'm' },
339                 { "try-remount", 0, NULL, 'M' },
340                 { "exclude-root", 0, NULL, 'R' },
341                 { "all", 0, NULL, 'a' },
342                 { NULL, 0, NULL, 0 }
343         };
344
345         while ((ret = getopt_long(argcnt, argstr, "VhbcvugidnfF:mMRa", long_opts, NULL)) != -1) {
346                 switch (ret) {
347                   case 'b':
348                           flags |= FL_BACKUPS;
349                           break;
350                   case 'g':
351                           gwant = 1;
352                           break;
353                   case 'u':
354                           uwant = 1;
355                           break;
356                   case 'd':
357                           flags |= FL_DEBUG;
358                           setlinebuf(stderr);
359                           break;
360                   case 'v':
361                           if (flags & FL_VERBOSE)
362                                 flags |= FL_VERYVERBOSE;
363                           else
364                                 flags |= FL_VERBOSE;
365                           break;
366                   case 'f':
367                           flags |= FL_FORCE;
368                           break;
369                   case 'i':
370                           flags |= FL_INTERACTIVE;
371                           break;
372                   case 'n':
373                           flags |= FL_GUESSDQ;
374                           break;
375                   case 'c':
376                           flags |= FL_NEWFILE;
377                           break;
378                   case 'V':
379                           version();
380                           exit(0);
381                   case 'M':
382                           flags |= FL_FORCEREMOUNT;
383                           break;
384                   case 'm':
385                           flags |= FL_NOREMOUNT;
386                           break;
387                   case 'a':
388                           flags |= FL_ALL;
389                           break;
390                   case 'R':
391                           flags |= FL_NOROOT;
392                           break;
393                   case 'F':
394                           if ((fmt = name2fmt(optarg)) == QF_ERROR)
395                                   exit(1);
396                           break;
397                   default:
398                         usage();
399                 }
400         }
401         if (!(uwant | gwant))
402                 uwant = 1;
403         if ((argcnt == optind && !(flags & FL_ALL)) || (argcnt > optind && flags & FL_ALL)) {
404                 fputs(_("Bad number of arguments.\n"), stderr);
405                 usage();
406         }
407         if (fmt == QF_XFS) {
408                 fputs(_("XFS quota format needs no checking.\n"), stderr);
409                 exit(0);
410         }
411         if (flags & FL_VERBOSE && flags & FL_DEBUG)
412                 flags &= ~FL_VERBOSE;
413         if (!(flags & FL_ALL))
414                 mntpoint = argstr[optind];
415         else
416                 mntpoint = NULL;
417 }
418
419 #if defined(EXT2_DIRECT)
420 static int ext2_direct_scan(char *device)
421 {
422         ext2_ino_t i_num;
423         ext2_filsys fs;
424         errcode_t error;
425         ext2_inode_scan scan;
426         struct ext2_inode inode;
427         int inode_buffer_blocks = 0;
428         ext2fs_inode_bitmap inode_used_map;
429         ext2fs_inode_bitmap inode_dir_map;
430         uid_t uid;
431         gid_t gid;
432
433         if ((error = ext2fs_open(device, 0, 0, 0, unix_io_manager, &fs))) {
434                 errstr(_("error (%d) while opening %s\n"), (int)error, device);
435                 return -1;
436         }
437
438         if ((error = ext2fs_allocate_inode_bitmap(fs, "in-use inode map", &inode_used_map))) {
439                 errstr(_("error (%d) while allocating inode file bitmap\n"), (int)error);
440                 return -1;
441         }
442
443         if ((error = ext2fs_allocate_inode_bitmap(fs, "directory inode map", &inode_dir_map))) {
444                 errstr(_("errstr (%d) while allocating inode directory bitmap\n"), (int)error);
445                 return -1;
446         }
447
448         if ((error = ext2fs_open_inode_scan(fs, inode_buffer_blocks, &scan))) {
449                 errstr(_("error (%d) while opening inode scan\n"), (int)error);
450                 return -1;
451         }
452
453         if ((error = ext2fs_get_next_inode(scan, &i_num, &inode))) {
454                 errstr(_("error (%d) while starting inode scan\n"), (int)error);
455                 return -1;
456         }
457
458         while ((long)i_num) {
459                 if (inode.i_links_count) {
460                         debug(FL_DEBUG, _("Found i_num %ld, blocks %ld\n"), (long)i_num, (long)inode.i_blocks);
461                         if (flags & FL_VERBOSE)
462                                 blit(NULL);
463                         uid = inode.i_uid | (inode.i_uid_high << 16);
464                         gid = inode.i_gid | (inode.i_gid_high << 16);
465                         if (inode.i_uid_high | inode.i_gid_high)
466                                 debug(FL_DEBUG, _("High uid detected.\n"));
467                         if (ucheck)
468                                 add_to_quota(USRQUOTA, i_num, uid, gid,
469                                              inode.i_mode, inode.i_links_count,
470                                              ((loff_t)inode.i_blocks) << 9, 0);
471                         if (gcheck)
472                                 add_to_quota(GRPQUOTA, i_num, uid, gid,
473                                              inode.i_mode, inode.i_links_count,
474                                              ((loff_t)inode.i_blocks) << 9, 0);
475                         if (S_ISDIR(inode.i_mode))
476                                 dirs_done++;
477                         else
478                                 files_done++;
479                 }
480
481                 if ((error = ext2fs_get_next_inode(scan, &i_num, &inode))) {
482                         errstr(_("Something weird happened while scanning. Error %d\n"), (int)error);
483                         return -1;
484                 }
485         }
486         return 0;
487 }
488 #endif
489
490 /*
491  * Scan a directory with the readdir systemcall. Stat the files and add the sizes
492  * of the files to the appropriate quotas. When we find a dir we recursivly call
493  * ourself to scan that dir.
494  */
495 static int scan_dir(char *pathname)
496 {
497         struct dirs *dir_stack = NULL;
498         struct dirs *new_dir;
499         struct dirent *de;
500         struct stat st;
501         loff_t qspace;
502         DIR *dp;
503         int ret;
504
505         if (lstat(pathname, &st) == -1) {
506                 errstr(_("Cannot stat directory %s: %s\n"), pathname, strerror(errno));
507                 goto out;
508         }
509         qspace = getqsize(pathname, &st);
510         if (ucheck)
511                 add_to_quota(USRQUOTA, st.st_ino, st.st_uid, st.st_gid, st.st_mode,
512                              st.st_nlink, qspace, 0);
513         if (gcheck)
514                 add_to_quota(GRPQUOTA, st.st_ino, st.st_uid, st.st_gid, st.st_mode,
515                              st.st_nlink, qspace, 0);
516
517         if ((dp = opendir(pathname)) == (DIR *) NULL)
518                 die(2, _("\nCan open directory %s: %s\n"), pathname, strerror(errno));
519
520         chdir(pathname);
521         if (flags & FL_VERYVERBOSE)
522                 blit(pathname);
523         while ((de = readdir(dp)) != (struct dirent *)NULL) {
524                 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
525                         continue;
526                 if (flags & FL_VERBOSE)
527                         blit(NULL);
528
529                 if ((lstat(de->d_name, &st)) == -1) {
530                         errstr(_("lstat Cannot stat `%s/%s': %s\nGuess you'd better run fsck first !\nexiting...\n"),
531                                 pathname, de->d_name, strerror(errno));
532                         goto out;
533                 }
534
535                 if (S_ISDIR(st.st_mode)) {
536                         if (st.st_dev != cur_dev)
537                                 continue;
538                         /*
539                          * Add this to the directory stack and check this later on.
540                          */
541                         debug(FL_DEBUG, _("pushd %s/%s\n"), pathname, de->d_name);
542                         new_dir = xmalloc(sizeof(struct dirs));
543
544                         new_dir->dir_name = xmalloc(strlen(pathname) + strlen(de->d_name) + 2);
545                         sprintf(new_dir->dir_name, "%s/%s", pathname, de->d_name);
546                         new_dir->next = dir_stack;
547                         dir_stack = new_dir;
548                 }
549                 else {
550                         qspace = getqsize(de->d_name, &st);
551                         if (ucheck)
552                                 add_to_quota(USRQUOTA, st.st_ino, st.st_uid, st.st_gid, st.st_mode,
553                                              st.st_nlink, qspace, 1);
554                         if (gcheck)
555                                 add_to_quota(GRPQUOTA, st.st_ino, st.st_uid, st.st_gid, st.st_mode,
556                                              st.st_nlink, qspace, 1);
557                         debug(FL_DEBUG, _("\tAdding %s size %lld ino %d links %d uid %u gid %u\n"), de->d_name,
558                               (long long)st.st_size, (int)st.st_ino, (int)st.st_nlink, (int)st.st_uid, (int)st.st_gid);
559                         files_done++;
560                 }
561         }
562         closedir(dp);
563
564         /*
565          * Traverse the directory stack, and check it.
566          */
567         debug(FL_DEBUG, _("Scanning stored directories from directory stack\n"));
568         while (dir_stack != (struct dirs *)NULL) {
569                 new_dir = dir_stack;
570                 dir_stack = dir_stack->next;
571                 debug(FL_DEBUG, _("popd %s\nEntering directory %s\n"), new_dir->dir_name,
572                       new_dir->dir_name);
573                 ret = scan_dir(new_dir->dir_name);
574                 dirs_done++;
575 #ifdef DEBUG_MALLOC
576                 free_mem += sizeof(struct dirs) + strlen(new_dir->dir_name) + 1;
577 #endif
578                 free(new_dir->dir_name);
579                 free(new_dir);
580                 if (ret < 0)    /* Error while scanning? */
581                         goto out;
582         }
583         debug(FL_DEBUG, _("Leaving %s\n"), pathname);
584         return 0;
585       out:
586         for (new_dir = dir_stack; new_dir; new_dir = dir_stack) {
587                 dir_stack = dir_stack->next;
588 #ifdef DEBUG_MALLOC
589                 free_mem += sizeof(struct dirs) + strlen(new_dir->dir_name) + 1;
590 #endif
591                 free(new_dir->dir_name);
592                 free(new_dir);
593         }
594         return -1;
595 }
596
597 /* Ask user y/n question */
598 int ask_yn(char *q, int def)
599 {
600         char a[10];             /* Users answer */
601
602         printf("%s [%c]: ", q, def ? 'y' : 'n');
603         fflush(stdout);
604         while (1) {
605                 fgets(a, sizeof(a)-1, stdin);
606                 if (a[0] == '\n')
607                         return def;
608                 if (!strcasecmp(a, "y\n"))
609                         return 1;
610                 if (!strcasecmp(a, "n\n"))
611                         return 0;
612                 printf("Illegal answer. Please answer y/n: ");
613                 fflush(stdout);
614         }
615 }
616
617 /* Do checks and buffer quota file into memory */
618 static int process_file(struct mntent *mnt, int type)
619 {
620         char *qfname = NULL;
621         int fd = -1, ret;
622
623         debug(FL_DEBUG, _("Going to check %s quota file of %s\n"), type2name(type),
624               mnt->mnt_dir);
625
626         if (kern_quota_on(mnt->mnt_fsname, type, cfmt) >= 0) {  /* Is quota enabled? */
627                 if (!(flags & FL_FORCE)) {
628                         if (flags & FL_INTERACTIVE) {
629                                 printf(_("Quota for %ss is enabled on mountpoint %s so quotacheck might damage the file.\n"), type2name(type), mnt->mnt_dir);
630                                 if (!ask_yn(_("Should I continue"), 0)) {
631                                         printf(_("As you wish... Canceling check of this file.\n"));
632                                         return -1;
633                                 }
634                         }
635                         else
636                                 die(6, _("Quota for %ss is enabled on mountpoint %s so quotacheck might damage the file.\n\
637 Please turn quotas off or use -f to force checking.\n"),
638                                     type2name(type), mnt->mnt_dir);
639                 }
640                 /* At least sync quotas so damage will be smaller */
641                 if (quotactl(QCMD((kernel_iface == IFACE_GENERIC)? Q_SYNC : Q_6_5_SYNC, type),
642                              mnt->mnt_fsname, 0, NULL) < 0)
643                         die(4, _("Error while syncing quotas on %s: %s\n"), mnt->mnt_fsname, strerror(errno));
644         }
645
646         if (!(flags & FL_NEWFILE)) {    /* Need to buffer file? */
647                 if (get_qf_name(mnt, type, cfmt, 0, &qfname) < 0) {
648                         errstr(_("Cannot get quotafile name for %s\n"), mnt->mnt_fsname);
649                         return -1;
650                 }
651                 if ((fd = open(qfname, O_RDONLY)) < 0) {
652                         if (errno != ENOENT) {
653                                 errstr(_("Cannot open quotafile %s: %s\n"),
654                                         qfname, strerror(errno));
655                                 free(qfname);
656                                 return -1;
657                         }
658                         /* When file was not found, just skip it */
659                         flags |= FL_NEWFILE;
660                         free(qfname);
661                         qfname = NULL;
662                 }
663         }
664
665         ret = 0;
666         memset(old_info + type, 0, sizeof(old_info[type]));
667         if (is_tree_qfmt(cfmt))
668                 ret = v2_buffer_file(qfname, fd, type, cfmt);
669         else
670                 ret = v1_buffer_file(qfname, fd, type);
671
672         if (!(flags & FL_NEWFILE)) {
673                 free(qfname);
674                 close(fd);
675         }
676         return ret;
677 }
678
679 /* Backup old quotafile and rename new one to right name */
680 static int rename_files(struct mntent *mnt, int type)
681 {
682         char *filename, newfilename[PATH_MAX];
683         struct stat st;
684         mode_t mode = S_IRUSR | S_IWUSR;
685 #ifdef HAVE_EXT2_INCLUDE
686         long ext2_flags = -1;
687         int fd;
688 #endif
689
690         debug(FL_DEBUG, _("Renaming new files to proper names.\n"));
691         if (get_qf_name(mnt, type, cfmt, 0, &filename) < 0)
692                 die(2, _("Cannot get name of old quotafile on %s.\n"), mnt->mnt_dir);
693         if (stat(filename, &st) < 0) {  /* File doesn't exist? */
694                 if (errno == ENOENT) {
695                         debug(FL_DEBUG | FL_VERBOSE, _("Old file not found.\n"));
696                         goto rename_new;
697                 }
698                 errstr(_("Error while searching for old quota file %s: %s\n"),
699                         filename, strerror(errno));
700                 free(filename);
701                 return -1;
702         }
703         mode = st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
704 #ifdef HAVE_EXT2_INCLUDE
705         if ((fd = open(filename, O_RDONLY)) < 0) {
706                 if (errno == ENOENT) {
707                         debug(FL_DEBUG | FL_VERBOSE, _("Old file found removed during check!\n"));
708                         goto rename_new;
709                 }
710                 errstr(_("Error while opening old quota file %s: %s\n"),
711                         filename, strerror(errno));
712                 free(filename);
713                 return -1;
714         }
715         if (ioctl(fd, EXT2_IOC_GETFLAGS, &ext2_flags) < 0)
716                 debug(FL_DEBUG, _("EXT2_IOC_GETFLAGS failed: %s\n"), strerror(errno));
717         else if (ext2_flags & EXT2_IMMUTABLE_FL) {
718                 /* IMMUTABLE flag set probably because system crashed and quota
719                  * was not properly turned off */
720                 debug(FL_DEBUG | FL_VERBOSE, _("Quota file %s has IMMUTABLE flag set. Clearing.\n"), filename);
721                 ext2_flags &= ~EXT2_IMMUTABLE_FL;
722                 if (ioctl(fd, EXT2_IOC_SETFLAGS, &ext2_flags) < 0) {
723                         errstr(_("Failed to remove IMMUTABLE flag from quota file %s: %s\n"), filename, strerror(errno));
724                         free(filename);
725                         close(fd);
726                         return -1;
727                 }
728         }
729         close(fd);
730 #endif
731         if (flags & FL_BACKUPS) {
732                 debug(FL_DEBUG, _("Renaming old quotafile to %s~\n"), filename);
733                 /* Backup old file */
734                 strcpy(newfilename, filename);
735                 /* Make backingup safe */
736                 sstrncat(newfilename, "~", PATH_MAX);
737                 if (newfilename[strlen(newfilename) - 1] != '~')
738                         die(8, _("Name of quota file too long. Contact %s.\n"), MY_EMAIL);
739                 if (rename(filename, newfilename) < 0) {
740                         errstr(_("Cannot rename old quotafile %s to %s: %s\n"),
741                                 filename, newfilename, strerror(errno));
742                         free(filename);
743                         return -1;
744                 }
745         }
746         debug(FL_DEBUG, _("Renaming new quotafile\n"));
747 rename_new:
748         /* Rename new file to right name */
749         strcpy(newfilename, filename);
750         sstrncat(newfilename, ".new", PATH_MAX);
751         if (rename(newfilename, filename) < 0) {
752                 errstr(_("Cannot rename new quotafile %s to name %s: %s\n"),
753                         newfilename, filename, strerror(errno));
754                 free(filename);
755                 return -1;
756         }
757         if (chmod(filename, mode) < 0) {
758                 errstr(_("Cannot change permission of %s: %s\n"), filename, strerror(errno));
759                 free(filename);
760                 return -1;
761         }
762 #ifdef HAVE_EXT2_INCLUDE
763         if (ext2_flags != -1) {
764                 if ((fd = open(filename, O_RDONLY)) < 0) {
765                         errstr(_("Cannot open new quota file %s: %s\n"), filename, strerror(errno));
766                         free(filename);
767                         return -1;
768                 }
769                 if (ioctl(fd, EXT2_IOC_SETFLAGS, &ext2_flags) < 0)
770                         errstr(_("Warning: Cannot set EXT2 flags on %s: %s\n"), filename, strerror(errno));
771                 close(fd);
772         }
773 #endif
774         free(filename);
775         return 0;
776 }
777
778 /*
779  * Dump the quota info that we have in memory now to the appropriate
780  * quota file. As quotafiles doesn't account to quotas we don't have to
781  * bother about accounting new blocks for quota file
782  */
783 static int dump_to_file(struct mntent *mnt, int type)
784 {
785         struct dquot *dquot;
786         uint i;
787         struct quota_handle *h;
788
789         debug(FL_DEBUG, _("Dumping gathered data for %ss.\n"), type2name(type));
790         if (!(h = new_io(mnt, type, cfmt))) {
791                 errstr(_("Cannot initialize IO on new quotafile: %s\n"),
792                         strerror(errno));
793                 return -1;
794         }
795         if (!(flags & FL_NEWFILE)) {
796                 h->qh_info.dqi_bgrace = old_info[type].dqi_bgrace;
797                 h->qh_info.dqi_igrace = old_info[type].dqi_igrace;
798                 if (is_tree_qfmt(cfmt))
799                         v2_merge_info(&h->qh_info, old_info + type);
800                 mark_quotafile_info_dirty(h);
801         }
802         for (i = 0; i < DQUOTHASHSIZE; i++)
803                 for (dquot = dquot_hash[type][i]; dquot; dquot = dquot->dq_next) {
804                         dquot->dq_h = h;
805                         update_grace_times(dquot);
806                         h->qh_ops->commit_dquot(dquot, COMMIT_ALL);
807                 }
808         if (end_io(h) < 0) {
809                 errstr(_("Cannot finish IO on new quotafile: %s\n"), strerror(errno));
810                 return -1;
811         }
812         debug(FL_DEBUG, _("Data dumped.\n"));
813         if (kern_quota_on(mnt->mnt_fsname, type, cfmt) >= 0) {  /* Quota turned on? */
814                 char *filename;
815
816                 if (get_qf_name(mnt, type, cfmt, NF_FORMAT, &filename) < 0)
817                         errstr(_("Cannot find checked quota file for %ss on %s!\n"), type2name(type), mnt->mnt_fsname);
818                 else {
819                         if (quotactl(QCMD((kernel_iface == IFACE_GENERIC) ? Q_QUOTAOFF : Q_6_5_QUOTAOFF, type),
820                                      mnt->mnt_fsname, 0, NULL) < 0)
821                                 errstr(_("Cannot turn %s quotas off on %s: %s\nKernel won't know about changes quotacheck did.\n"),
822                                         type2name(type), mnt->mnt_fsname, strerror(errno));
823                         else {
824                                 int ret;
825
826                                 /* Rename files - if it fails we cannot do anything better than just turn on quotas again */
827                                 rename_files(mnt, type);
828
829                                 if (kernel_iface == IFACE_GENERIC)
830                                         ret = quotactl(QCMD(Q_QUOTAON, type), mnt->mnt_fsname, util2kernfmt(cfmt), filename);
831                                 else
832                                         ret = quotactl(QCMD(Q_6_5_QUOTAON, type), mnt->mnt_fsname, 0, filename);
833                                 if (ret < 0)
834                                         errstr(_("Cannot turn %s quotas on on %s: %s\nKernel won't know about changes quotacheck did.\n"),
835                                                 type2name(type), mnt->mnt_fsname, strerror(errno));
836                         }
837                         free(filename);
838                 }
839         }
840         else
841                 if (rename_files(mnt, type) < 0)
842                         return -1;
843         return 0;
844 }
845
846 /* Substract space used by old quota file from usage */
847 static void sub_quota_file(struct mntent *mnt, int qtype, int ftype)
848 {
849         char *filename;
850         struct stat st;
851         loff_t qspace;
852         struct dquot *d;
853         qid_t id;
854
855         debug(FL_DEBUG, _("Substracting space used by old %s quota file.\n"), type2name(ftype));
856         if (get_qf_name(mnt, ftype, cfmt, 0, &filename) < 0) {
857                 debug(FL_VERBOSE, _("Old %s file not found. Usage will not be substracted.\n"), type2name(ftype));
858                 return;
859         }
860
861         if (stat(filename, &st) < 0) {
862                 debug(FL_VERBOSE, _("Cannot stat old %s quota file: %s\n"), type2name(ftype), strerror(errno));
863                 free(filename);
864                 return;
865         }
866         qspace = getqsize(filename, &st);
867         free(filename);
868         
869         if (qtype == USRQUOTA)
870                 id = st.st_uid;
871         else
872                 id = st.st_gid;
873         if ((d = lookup_dquot(id, qtype)) == NODQUOT) {
874                 errstr(_("Quota structure for %s owning quota file not present! Something is really wrong...\n"), type2name(qtype));
875                 return;
876         }
877         d->dq_dqb.dqb_curinodes--;
878         d->dq_dqb.dqb_curspace -= qspace;
879         debug(FL_DEBUG, _("Substracted %lu bytes.\n"), (unsigned long)qspace);
880 }
881
882 /* Buffer quotafile, run filesystem scan, dump quotafiles */
883 static void check_dir(struct mntent *mnt)
884 {
885         struct stat st;
886         int remounted = 0;
887
888         if (lstat(mnt->mnt_dir, &st) < 0)
889                 die(2, _("Cannot stat mountpoint %s: %s\n"), mnt->mnt_dir, strerror(errno));
890         if (!S_ISDIR(st.st_mode))
891                 die(2, _("Mountpoint %s is not a directory?!\n"), mnt->mnt_dir);
892         cur_dev = st.st_dev;
893         files_done = dirs_done = 0;
894         if (ucheck)
895                 if (process_file(mnt, USRQUOTA) < 0)
896                         ucheck = 0;
897         if (gcheck)
898                 if (process_file(mnt, GRPQUOTA) < 0)
899                         gcheck = 0;
900         if (!ucheck && !gcheck) /* Nothing to check? */
901                 return;
902         if (!(flags & FL_NOREMOUNT)) {
903                 /* Now we try to remount fs read-only to prevent races when scanning filesystem */
904                 if (mount
905                     (NULL, mnt->mnt_dir, mnt->mnt_type, MS_MGC_VAL | MS_REMOUNT | MS_RDONLY,
906                      NULL) < 0 && !(flags & FL_FORCEREMOUNT)) {
907                         if (flags & FL_INTERACTIVE) {
908                                 printf(_("Cannot remount filesystem mounted on %s read-only. Counted values might not be right.\n"), mnt->mnt_dir);
909                                 if (!ask_yn(_("Should I continue"), 0)) {
910                                         printf(_("As you wish... Canceling check of this file.\n"));
911                                         goto out;
912                                 }
913                         }
914                         else {
915                                 errstr(_("Cannot remount filesystem mounted on %s read-only so counted values might not be right.\n\
916 Please stop all programs writing to filesystem or use -m flag to force checking.\n"), mnt->mnt_dir);
917                                 goto out;
918                         }
919                 }
920                 else
921                         remounted = 1;
922                 debug(FL_DEBUG, _("Filesystem remounted read-only\n"));
923         }
924         debug(FL_VERBOSE, _("Scanning %s [%s] "), mnt->mnt_fsname, mnt->mnt_dir);
925 #if defined(EXT2_DIRECT)
926         if (!strcmp(mnt->mnt_type, MNTTYPE_EXT2) || !strcmp(mnt->mnt_type, MNTTYPE_EXT3)) {
927                 if (ext2_direct_scan(mnt->mnt_fsname) < 0)
928                         goto out;
929         }
930         else {
931 #else
932         if (mnt->mnt_dir) {
933 #endif
934                 if (flags & FL_VERYVERBOSE)
935                         putchar('\n');
936                 if (scan_dir(mnt->mnt_dir) < 0)
937                         goto out;
938         }
939         dirs_done++;
940         if (flags & FL_VERBOSE || flags & FL_VERYVERBOSE)
941                 fputs(_("done\n"), stdout);
942         if (ucheck) {
943                 sub_quota_file(mnt, USRQUOTA, USRQUOTA);
944                 sub_quota_file(mnt, USRQUOTA, GRPQUOTA);
945         }
946         if (gcheck) {
947                 sub_quota_file(mnt, GRPQUOTA, USRQUOTA);
948                 sub_quota_file(mnt, GRPQUOTA, GRPQUOTA);
949         }
950         debug(FL_DEBUG | FL_VERBOSE, _("Checked %d directories and %d files\n"), dirs_done,
951               files_done);
952         if (remounted) {
953                 if (mount(NULL, mnt->mnt_dir, mnt->mnt_type, MS_MGC_VAL | MS_REMOUNT, NULL) < 0)
954                         die(4, _("Cannot remount filesystem %s read-write. cannot write new quota files.\n"), mnt->mnt_dir);
955                 debug(FL_DEBUG, _("Filesystem remounted RW.\n"));
956         }
957         if (ucheck)
958                 dump_to_file(mnt, USRQUOTA);
959         if (gcheck)
960                 dump_to_file(mnt, GRPQUOTA);
961 out:
962         remove_list();
963 }
964
965 /* Detect quota format from filename of present files */
966 static int detect_filename_format(struct mntent *mnt, int type)
967 {
968         char *option;
969         struct stat statbuf;
970         char namebuf[PATH_MAX];
971         int journal = 0;
972         int fmt;
973
974         if (type == USRQUOTA) {
975                 if ((option = hasmntopt(mnt, MNTOPT_USRQUOTA)))
976                         option += strlen(MNTOPT_USRQUOTA);
977                 else if (hasmntopt(mnt, MNTOPT_USRJQUOTA)) {
978                         journal = 1;
979                         option += strlen(MNTOPT_USRJQUOTA);
980                 }
981                 else if ((option = hasmntopt(mnt, MNTOPT_QUOTA)))
982                         option += strlen(MNTOPT_QUOTA);
983         }
984         else {
985                 if ((option = hasmntopt(mnt, MNTOPT_GRPQUOTA)))
986                         option += strlen(MNTOPT_GRPQUOTA);
987                 else if (hasmntopt(mnt, MNTOPT_GRPJQUOTA)) {
988                         journal = 1;
989                         option += strlen(MNTOPT_GRPJQUOTA);
990                 }
991         }
992         if (!option)
993                 die(2, _("Cannot find quota option on filesystem %s with quotas!\n"), mnt->mnt_dir);
994         if (journal) {
995                 char fmtbuf[64], *space;
996                 
997                 if (!(option = hasmntopt(mnt, MNTOPT_JQFMT))) {
998 jquota_err:
999                         errstr(_("Cannot detect quota format for journalled quota on %s\n"), mnt->mnt_dir);
1000                         return -1;
1001                 }
1002                 option += strlen(MNTOPT_JQFMT);
1003                 if (*option != '=')
1004                         goto jquota_err;
1005                 space = strchr(option, ',');
1006                 if (!space)
1007                         space = option + strlen(option);
1008                 if (space-option > sizeof(fmtbuf))
1009                         goto jquota_err;
1010                 sstrncpy(fmtbuf, option+1, space-option);
1011                 fmt = name2fmt(fmtbuf);
1012                 if (fmt == QF_ERROR)
1013                         goto jquota_err;
1014                 return fmt;
1015         }
1016         else if (*option == '=')        /* If the file name is specified we can't detect quota format from it... */
1017                 return -1;
1018         snprintf(namebuf, PATH_MAX, "%s/%s.%s", mnt->mnt_dir, basenames[QF_VFSV0], extensions[type]);
1019         if (!stat(namebuf, &statbuf)) {
1020                 int fd = open(namebuf, O_RDONLY);
1021                 if (fd < 0)
1022                         return -1;
1023                 fmt = v2_detect_version(namebuf, fd, type);
1024                 close(fd);
1025                 return fmt;
1026                 
1027         }
1028         if (errno != ENOENT)
1029                 return -1;
1030         snprintf(namebuf, PATH_MAX, "%s/%s.%s", mnt->mnt_dir, basenames[QF_VFSOLD], extensions[type]);
1031         if (!stat(namebuf, &statbuf))
1032                 return QF_VFSOLD;
1033         /* Old quota files don't exist, just create VFSv0 format if available */
1034         if (kern_qfmt_supp(QF_VFSV0))
1035                 return QF_VFSV0;
1036         if (kern_qfmt_supp(QF_VFSOLD))
1037                 return QF_VFSOLD;
1038         return -1;
1039 }
1040
1041 static void check_all(void)
1042 {
1043         struct mntent *mnt;
1044         int checked = 0;
1045         static int warned;
1046
1047         if (init_mounts_scan((flags & FL_ALL) ? 0 : 1, &mntpoint, 0) < 0)
1048                 die(2, _("Cannot initialize mountpoint scan.\n"));
1049         while ((mnt = get_next_mount())) {
1050                 if (flags & FL_ALL && flags & FL_NOROOT && !strcmp(mnt->mnt_dir, "/"))
1051                         continue;
1052                 if (!strcmp(mnt->mnt_type, MNTTYPE_XFS) || nfs_fstype(mnt->mnt_type) ||
1053                     meta_qf_fstype(mnt->mnt_type)) {
1054                         debug(FL_DEBUG | FL_VERBOSE, _("Skipping %s [%s]\n"), mnt->mnt_fsname, mnt->mnt_dir);
1055                         continue;
1056                 }
1057                 cfmt = fmt;
1058                 if (uwant && hasquota(mnt, USRQUOTA, 0))
1059                         ucheck = 1;
1060                 else
1061                         ucheck = 0;
1062                 if (gwant && hasquota(mnt, GRPQUOTA, 0))
1063                         gcheck = 1;
1064                 else
1065                         gcheck = 0;
1066                 if (!ucheck && !gcheck)
1067                         continue;
1068                 if (cfmt == -1) {
1069                         cfmt = detect_filename_format(mnt, ucheck ? USRQUOTA : GRPQUOTA);
1070                         if (cfmt == -1) {
1071                                 errstr(_("Cannot guess format from filename on %s. Please specify format on commandline.\n"),
1072                                         mnt->mnt_fsname);
1073                                 continue;
1074                         }
1075                         debug(FL_DEBUG, _("Detected quota format %s\n"), fmt2name(cfmt));
1076                 }
1077
1078                 if (flags & FL_VERBOSE && !hasmntopt(mnt, MNTOPT_USRJQUOTA) &&
1079                     !hasmntopt(mnt, MNTOPT_GRPJQUOTA) && !warned &&
1080                     (!strcmp(mnt->mnt_type, MNTTYPE_EXT3) ||
1081                      !strcmp(mnt->mnt_type, MNTTYPE_EXT4) ||
1082                      !strcmp(mnt->mnt_type, MNTTYPE_EXT4DEV) ||
1083                      !strcmp(mnt->mnt_type, MNTTYPE_REISER))) {
1084                         struct utsname stats;
1085
1086                         /* Parse Linux kernel version and issue warning if not using
1087                          * journaled quotas. */
1088                         warned = 1;
1089                         if (uname(&stats) < 0)
1090                                 errstr(_("Cannot get system info: %s\n"),
1091                                         strerror(errno));
1092                         else if (!strcmp(stats.sysname, "Linux")) {
1093                                 int v;
1094                                 char *errch;
1095
1096                                 v = strtol(stats.release, &errch, 10);
1097                                 if (*errch == '.' && v >= 2) {
1098                                         v = strtol(errch + 1, &errch, 10);
1099                                         if (*errch == '.' && v >= 6) {
1100                                                 v = strtol(errch + 1, &errch, 10);
1101                                                 if (v >= 11)
1102                                                         errstr(_("Your kernel probably supports journaled quota but you are not using it. Consider switching to journaled quota to avoid running quotacheck after an unclean shutdown.\n"));
1103                                         }
1104                                 }
1105                         }
1106                 }
1107
1108                 checked++;
1109                 check_dir(mnt);
1110         }
1111         end_mounts_scan();
1112         if (!checked && (!(flags & FL_ALL) || flags & (FL_VERBOSE | FL_DEBUG)))
1113                 errstr(_("Cannot find filesystem to check or filesystem not mounted with quota option.\n"));
1114 }
1115
1116 int main(int argc, char **argv)
1117 {
1118         gettexton();
1119         progname = basename(argv[0]);
1120
1121         parse_options(argc, argv);
1122         init_kernel_interface();
1123
1124         check_all();
1125 #ifdef DEBUG_MALLOC
1126         errstr(_("Allocated %d bytes memory\nFree'd %d bytes\nLost %d bytes\n"),
1127                 malloc_mem, free_mem, malloc_mem - free_mem);
1128 #endif
1129         return 0;
1130 }