source: trunk/packs/maint/os-checkfiles.c @ 14896

Revision 14896, 13.9 KB checked in by rbasch, 25 years ago (diff)
Add support for hard links, i.e. the 'h' entries now output by os-statfiles.
Line 
1/* Copyright 1999 by the Massachusetts Institute of Technology.
2 *
3 * Permission to use, copy, modify, and distribute this
4 * software and its documentation for any purpose and without
5 * fee is hereby granted, provided that the above copyright
6 * notice appear in all copies and that both that copyright
7 * notice and this permission notice appear in supporting
8 * documentation, and that the name of M.I.T. not be used in
9 * advertising or publicity pertaining to distribution of the
10 * software without specific, written prior permission.
11 * M.I.T. makes no representations about the suitability of
12 * this software for any purpose.  It is provided "as is"
13 * without express or implied warranty.
14 */
15
16/* This program checks a list of files against stat information
17 * as produced by the os-statfiles program, and recreates regular
18 * files, symlinks, and directories whose stat information does
19 * not match the given data.  Files are recreated by copying from
20 * a given root directory, presumably an Athena /os hierarchy.
21 */
22
23static const char rcsid[] = "$Id: os-checkfiles.c,v 1.2 2000-06-28 20:26:07 rbasch Exp $";
24
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28#include <time.h>
29#include <unistd.h>
30#include <sys/types.h>
31#include <sys/stat.h>
32#include <dirent.h>
33#include <limits.h>
34#include <fcntl.h>
35#include <errno.h>
36#include "array.h"
37
38#define MAXLINE ((PATH_MAX * 2) + 256)
39
40char *progname;
41
42char *osroot = "/os";
43Array *exceptlist = NULL;
44int noop = 0;
45int verbose = 0;
46
47void usage();
48
49void do_file(const char *path, const struct stat *statp, mode_t mode,
50             off_t size, uid_t uid, gid_t gid, int flags, time_t since);
51
52void do_symlink(const char *from, const struct stat *statp, const char *to);
53
54void do_hardlink(const char *path, const struct stat *statp, const char *to);
55
56void do_directory(const char *path, const struct stat *statp, mode_t mode,
57                  uid_t uid, gid_t gid);
58
59void nuke(const char *path, const struct stat *statp);
60
61void nukedir(const char *path);
62
63void copyfile(const char *from, const char *to);
64
65void set_stat(const char *path, mode_t mode, uid_t uid, gid_t gid);
66
67Array *make_list(const char *file);
68
69int in_list(const Array *list, const char *what);
70
71int compare_string(const void *p1, const void *p2);
72
73
74void usage()
75{
76  fprintf(stderr, "Usage: %s [<options>] <statfile>\n", progname);
77  fprintf(stderr, "Options:\n");
78  fprintf(stderr, "        -n               No-op mode\n");
79  fprintf(stderr, "        -o <osroot>      OS root, default is /os\n");
80  fprintf(stderr, "        -r <target>      Target root, default is /\n");
81  fprintf(stderr, "        -t <file>        Timestamp file\n");
82  fprintf(stderr, "        -v               Verbose output mode\n");
83  fprintf(stderr, "        -x <file>        File containing exception list\n");
84  exit(1);
85}
86
87int main(int argc, char **argv)
88{
89  const char *statfile = NULL;
90  const char *timefile = NULL;
91  const char *exceptfile = NULL;
92  const char *target = "/";
93  FILE *statfp;
94  char inbuf[MAXLINE];
95  char *path;
96  char linkpath[PATH_MAX+1];
97  struct stat sb, *statp;
98  unsigned long long mode, uid, gid, size;
99  int c, len, flags;
100  time_t since;
101
102  progname = argv[0];
103
104  while ((c = getopt(argc, argv, "no:r:t:vx:")) != EOF)
105    {
106      switch(c)
107        {
108        case 'n':
109          noop = 1;
110          break;
111        case 'o':
112          osroot = optarg;
113          break;
114        case 'r':
115          target = optarg;
116          break;
117        case 't':
118          timefile = optarg;
119          break;
120        case 'v':
121          verbose = 1;
122          break;
123        case 'x':
124          exceptfile = optarg;
125          break;
126        case '?':
127          usage();
128          break;
129        }
130    }
131
132  if (optind + 1 != argc)
133    usage();
134
135  statfile = argv[optind++];
136  statfp = fopen(statfile, "r");
137  if (statfp == NULL)
138    {
139      fprintf(stderr, "%s: Cannot open %s: %s\n", progname, statfile,
140              strerror(errno));
141      exit(1);
142    }
143
144  if (stat(osroot, &sb) != 0 || !S_ISDIR(sb.st_mode))
145    {
146      fprintf(stderr, "%s: Invalid operating system root %s\n",
147              progname, osroot);
148      exit(1);
149    }
150
151  /* If given a timestamp file against which to check, record its
152   * modification time, otherwise just use the current time.
153   */
154  if (timefile != NULL)
155    {
156      if (stat(timefile, &sb) != 0)
157        {
158          fprintf(stderr, "%s: Cannot stat %s: %s\n",
159                  progname, timefile, strerror(errno));
160          exit(1);
161        }
162      since = sb.st_mtime;
163    }
164  else
165    time(&since);
166
167  if (exceptfile != NULL)
168    exceptlist = make_list(exceptfile);
169
170  /* Chdir to the target root. */
171  if (chdir(target) != 0)
172    {
173      fprintf(stderr, "%s: Cannot chdir to %s: %s\n",
174              progname, target, strerror(errno));
175      exit(1);
176    }
177
178  /* Main loop -- read entries from the stat file. */
179  while (fgets(inbuf, sizeof(inbuf), statfp) != NULL)
180    {
181      len = strlen(inbuf);
182      if (inbuf[len-1] != '\n')
183        {
184          fprintf(stderr, "%s: Invalid entry '%s', aborting\n",
185                  progname, inbuf);
186          exit(1);
187        }
188      inbuf[--len] = '\0';
189
190      /* Get the entry path, always the last field in the line.
191       * Skip it if it is in the exception list.
192       * Note now whether we can stat it.
193       */
194      for (path = &inbuf[len-1]; path > &inbuf[0] && *path != ' '; --path);
195      if (path <= &inbuf[0])
196        {
197          fprintf(stderr, "%s: Invalid entry '%s', aborting\n",
198                  progname, inbuf);
199          exit(1);
200        }
201      ++path;
202
203      if (in_list(exceptlist, path))
204        {
205          if (verbose)
206            printf("Skipping exception %s\n", path);
207          continue;
208        }
209
210      statp = (lstat(path, &sb) == 0 ? &sb : NULL);
211
212      switch (inbuf[0])
213        {
214        case 'l':
215          if (sscanf(&inbuf[2], "%s", linkpath) != 1)
216            {
217              fprintf(stderr,
218                      "%s: Invalid symlink entry '%s', aborting\n",
219                      progname, inbuf);
220              exit(1);
221            }
222          do_symlink(path, statp, linkpath);
223          break;
224        case 'f':
225          if (sscanf(&inbuf[2], "%llo %llu %llu %llu %x", &mode, &size,
226                     &uid, &gid, &flags) != 5)
227            {
228              fprintf(stderr,
229                      "%s: Invalid file entry '%s', aborting\n",
230                      progname, inbuf);
231              exit(1);
232            }
233          do_file(path, statp, mode, size, uid, gid, flags, since);
234          break;
235        case 'h':
236          if (sscanf(&inbuf[2], "%s", linkpath) != 1)
237            {
238              fprintf(stderr,
239                      "%s: Invalid hard link entry '%s', aborting\n",
240                      progname, inbuf);
241              exit(1);
242            }
243          do_hardlink(path, statp, linkpath);
244          break;
245        case 'd':
246          if (sscanf(&inbuf[2], "%llo %llu %llu", &mode, &uid, &gid) != 3)
247            {
248              fprintf(stderr,
249                      "%s: Invalid directory entry '%s', aborting\n",
250                      progname, inbuf);
251              exit(1);
252            }
253          do_directory(path, statp, mode, uid, gid);
254          break;
255        default:
256          fprintf(stderr, "%s: Unrecognized type '%c', aborting.\n",
257                  progname, inbuf[0]);
258          exit(1);
259        }
260    }
261  exit(0);
262}
263
264void do_file(const char *path, const struct stat *statp, mode_t mode,
265             off_t size, uid_t uid, gid_t gid, int flags, time_t since)
266{
267  struct stat sb;
268  char ospath[PATH_MAX+1];
269
270  if (statp == NULL
271      || !S_ISREG(statp->st_mode)
272      || statp->st_mtime > since
273      || statp->st_size != size)
274    {
275      printf("Replace regular file %s\n", path);
276      if (!noop)
277        {
278          /* Remove the existing path, if any. */
279          nuke(path, statp);
280
281          /* Copy the file into place. */
282          sprintf(ospath, "%s/%s", osroot, path);
283          copyfile(ospath, path);
284
285          /* Make sure new copy has expected size. */
286          if (lstat(path, &sb) != 0)
287            {
288              fprintf(stderr, "%s: Cannot stat %s after copy: %s\n",
289                      progname, path, strerror(errno));
290              exit(1);
291            }
292          if (sb.st_size != size)
293            {
294              fprintf(stderr,
295                      "%s: Size mismatch after copy of %s (%llu, %llu)\n",
296                      progname, path, (unsigned long long) size,
297                      (unsigned long long) sb.st_size);
298              exit(1);
299            }
300
301          set_stat(path, mode, uid, gid);
302        }
303    }
304
305  else if (statp->st_mode != mode
306           || statp->st_uid != uid
307           || statp->st_gid != gid)
308    {
309      /* Here to fix file permissions and ownership. */
310      printf("Set permission/ownership of regular file %s\n", path);
311      if (!noop)
312        set_stat(path, mode, uid, gid);
313    }
314
315  return;
316}
317
318void do_symlink(const char *from, const struct stat *statp, const char *to)
319{
320  char linkbuf[PATH_MAX+1];
321  int len;
322  int result = 0;
323
324  if (statp != NULL && S_ISLNK(statp->st_mode))
325    {
326      len = readlink(from, linkbuf, sizeof(linkbuf));
327      if (len > 0)
328        {
329          linkbuf[len] = '\0';
330          if (strcmp(linkbuf, to) == 0)
331            result = 1;
332        }
333    }
334  if (!result)
335    {
336      printf("Relink (symbolic) %s to %s\n", from, to);
337      if (!noop)
338        {
339          nuke(from, statp);
340          if (symlink(to, from) != 0)
341            {
342              fprintf(stderr, "%s: Cannot create symlink %s: %s\n",
343                      progname, from, strerror(errno));
344              exit(1);
345            }
346        }
347    }
348}
349
350/* Handle a hard link. */
351void do_hardlink(const char *path, const struct stat *statp, const char *to)
352{
353  struct stat to_stat;
354
355  if (lstat(to, &to_stat) != 0)
356    {
357      fprintf(stderr,
358              "%s: Warning: %s (link to %s) does not exist\n",
359              progname, to, path);
360      return;
361    }
362  if (S_ISDIR(to_stat.st_mode))
363    {
364      fprintf(stderr,
365              "%s: Warning: %s (link to %s) is a directory\n",
366              progname, to, path);
367      return;
368    }
369
370  if (statp == NULL
371      || statp->st_ino != to_stat.st_ino
372      || statp->st_dev != to_stat.st_dev)
373    {
374      printf("Relink (hard) %s to %s\n", path, to);
375      if (!noop)
376        {
377          if (statp != NULL)
378            nuke(path, statp);
379          if (link(to, path) != 0)
380            {
381              fprintf(stderr, "%s: Cannot create hard link %s to %s: %s\n",
382                      progname, path, to, strerror(errno));
383              exit(1);
384            }
385        }
386    }
387}
388
389void do_directory(const char *path, const struct stat *statp, mode_t mode,
390                  uid_t uid, gid_t gid)
391{
392  if (statp == NULL || !S_ISDIR(statp->st_mode))
393    {
394      /* Path doesn't exist, or is not a directory. Recreate it. */
395      printf("Recreate directory %s\n", path);
396      if (!noop)
397        {
398          if (statp != NULL)
399            nuke(path, statp);
400          if (mkdir(path, S_IRWXU) != 0)
401            {
402              fprintf(stderr, "%s: Cannot mkdir %s: %s\n",
403                      progname, path, strerror(errno));
404              exit(1);
405            }
406        }
407    }
408  else if (statp->st_mode == mode
409           && statp->st_uid == uid
410           && statp->st_gid == gid)
411    return;
412
413  /* Here when path is a directory, but needs permissions and ownership
414   * to be set.
415   */
416  printf("Set permission/ownership of directory %s\n", path);
417  if (!noop)
418    set_stat(path, mode, uid, gid);
419  return;
420}
421
422void nuke(const char *path, const struct stat *statp)
423{
424  if (statp == NULL)
425    return;
426  if (S_ISDIR(statp->st_mode))
427    nukedir(path);
428  else if (unlink(path) != 0)
429    {
430      fprintf(stderr, "%s: Cannot unlink %s: %s\n",
431              progname, path, strerror(errno));
432      exit(1);
433    }
434  return;
435}
436
437
438void nukedir(const char *path)
439{
440  DIR *dirp;
441  struct dirent *dp;
442  char pathbuf[PATH_MAX+1];
443  struct stat sb;
444
445  dirp = opendir(path);
446  if (dirp == NULL)
447    {
448      fprintf(stderr, "%s: Cannot open directory %s: %s\n",
449              progname, path, strerror(errno));
450      exit(1);
451    }
452  while ((dp = readdir(dirp)) != NULL)
453    {
454      if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0)
455        continue;
456      sprintf(pathbuf, "%s/%s", path, dp->d_name);
457      if (lstat(pathbuf, &sb) != 0)
458        {
459          fprintf(stderr, "%s: Cannot stat %s: %s\n",
460                  progname, pathbuf, strerror(errno));
461          continue;
462        }
463      /* Recurse if we encounter a subdirectory. */
464      if (S_ISDIR(sb.st_mode))
465        nukedir(pathbuf);
466      else if (unlink(pathbuf) != 0)
467        {
468          fprintf(stderr, "%s: Cannot unlink %s: %s\n",
469                  progname, pathbuf, strerror(errno));
470          exit(1);
471        }
472    }
473  closedir(dirp);
474  if (rmdir(path) != 0)
475    {
476      fprintf(stderr, "%s: Cannot rmdir %s: %s\n",
477              progname, path, strerror(errno));
478      exit(1);
479    }
480  return;
481}
482
483/* Copy a file.  The target file must not exist. */
484void copyfile(const char *from, const char *to)
485{
486  char buf[4096];
487  FILE *infp, *outfp;
488  int n, fd, status;
489
490  infp = fopen(from, "r");
491  if (infp == NULL)
492    {
493      fprintf(stderr, "%s: Cannot open %s: %s\n",
494              progname, from, strerror(errno));
495      return;
496    }
497  fd = open(to, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
498  if (fd < 0)
499    {
500      fprintf(stderr, "%s: Cannot create %s: %s\n",
501              progname, to, strerror(errno));
502      fclose(infp);
503      exit(1);
504    }
505  outfp = fdopen(fd, "w");
506  if (outfp == NULL)
507    {
508      fprintf(stderr, "%s: fdopen failed for %s: %s\n",
509              progname, to, strerror(errno));
510      fclose(infp);
511      close(fd);
512      exit(1);
513    }
514
515  while ((n = fread(buf, 1, sizeof(buf), infp)) > 0)
516    {
517      if (fwrite(buf, 1, n, outfp) != n)
518        {
519          fprintf(stderr, "%s: Write failed for %s: %s\n",
520                  progname, to, strerror(errno));
521          fclose(infp);
522          fclose(outfp);
523          exit(1);
524        }
525    }
526  status = ferror(infp);
527  if ((fclose(infp) != 0) || (status != 0))
528    {
529      fprintf(stderr, "%s: Error reading %s: %s\n",
530              progname, from, strerror(errno));
531      fclose(outfp);
532      exit(1);
533    }
534  status = ferror(outfp);
535  if ((fclose(outfp) != 0) || (status != 0))
536    {
537      fprintf(stderr, "%s: Error writing %s: %s\n",
538              progname, to, strerror(errno));
539      exit(1);
540    }
541  return;
542}
543
544void set_stat(const char *path, mode_t mode, uid_t uid, gid_t gid)
545{
546  if (chmod(path, mode) != 0)
547    {
548      fprintf(stderr, "%s: Cannot chmod %s: %s\n",
549              progname, path, strerror(errno));
550      exit(1);
551    }
552  if (chown(path, uid, gid) != 0)
553    {
554      fprintf(stderr, "%s: Cannot chown %s: %s\n",
555              progname, path, strerror(errno));
556      exit(1);
557    }
558}
559
560/* Read a file into an array, one element per line. */
561Array *make_list(const char *file)
562{
563  FILE *f;
564  Array *list;
565  char buffer[MAXLINE];
566
567  f = fopen(file, "r");
568  if (f == NULL)
569    return NULL;
570
571  list = array_new();
572  while (fgets(buffer, sizeof(buffer), f))
573    {
574      buffer[strlen(buffer) - 1] = '\0';
575      array_add(list, strdup(buffer));
576    }
577
578  fclose(f);
579  array_sort(list, compare_string);
580  return list;
581}
582
583/* Check if the given string is an element in the given array.
584 * Returns 1 if found in the array, 0 otherwise.
585 */
586int in_list(const Array *list, const char *what)
587{
588  if (list == NULL)
589    return 0;
590
591  return (array_search(list, what) != NULL);
592}
593
594int compare_string(const void *p1, const void *p2)
595{
596  return strcmp(*((char **)p1), *((char **)p2));
597}
Note: See TracBrowser for help on using the repository browser.