516cf4eed228abafea1d7b3cabbd4e884c98bac5
[debian/amanda] / client-src / getfsent.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1998, 2001 University of Maryland at College Park
4  * All Rights Reserved.
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and its
7  * documentation for any purpose is hereby granted without fee, provided that
8  * the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation, and that the name of U.M. not be used in advertising or
11  * publicity pertaining to distribution of the software without specific,
12  * written prior permission.  U.M. makes no representations about the
13  * suitability of this software for any purpose.  It is provided "as is"
14  * without express or implied warranty.
15  *
16  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  *
23  * Authors: the Amanda Development Team.  Its members are listed in a
24  * file named AUTHORS, in the root directory of this distribution.
25  */
26 /*
27  * $Id: getfsent.c,v 1.34 2006/01/14 04:37:18 paddy_s Exp $
28  *
29  * generic version of code to read fstab
30  */
31
32 #include "amanda.h"
33
34 #ifdef TEST
35 #  include <stdio.h>
36 #  include <sys/types.h>
37 #  undef P
38 #  define P(x)  x
39 #endif
40
41 #include "getfsent.h"
42
43 static char *dev2rdev P((char *));
44 static int samefile P((struct stat[3], struct stat *));
45
46 /*
47  * You are in a twisty maze of passages, all alike.
48  * Geesh.
49  */
50
51 #if defined(HAVE_FSTAB_H) && !defined(HAVE_MNTENT_H) /* { */
52 /*
53 ** BSD (GETFSENT_BSD)
54 */
55 #define GETFSENT_TYPE "BSD (Ultrix, AIX)"
56
57 #include <fstab.h>
58
59 int open_fstab()
60 {
61     return setfsent();
62 }
63
64 void close_fstab()
65 {
66     endfsent();
67 }
68
69
70 int get_fstab_nextentry(fsent)
71 generic_fsent_t *fsent;
72 {
73     struct fstab *sys_fsent = getfsent();
74     static char *xfsname = NULL, *xmntdir = NULL;
75     static char *xfstype = NULL, *xmntopts = NULL;
76
77     if(!sys_fsent)
78         return 0;
79     fsent->fsname  = xfsname  = newstralloc(xfsname,  sys_fsent->fs_spec);
80     fsent->mntdir  = xmntdir  = newstralloc(xmntdir,  sys_fsent->fs_file);
81     fsent->freq    = sys_fsent->fs_freq;
82     fsent->passno  = sys_fsent->fs_passno;
83 #ifdef STATFS_ULTRIX
84     fsent->fstype  = xfstype  = newstralloc(xfstype,  sys_fsent->fs_name);
85     fsent->mntopts = xmntopts = newstralloc(xmntopts, sys_fsent->fs_opts);
86 #else
87 #if defined(_AIX)
88     fsent->fstype  = xfstype  = newstralloc(xfstype,  "unknown");
89     fsent->mntopts = xmntopts = newstralloc(xmntopts, sys_fsent->fs_type);
90 #else
91     fsent->fstype  = xfstype  = newstralloc(xfstype,  sys_fsent->fs_vfstype);
92     fsent->mntopts = xmntopts = newstralloc(xmntopts, sys_fsent->fs_mntops);
93 #endif
94 #endif
95     return 1;
96 }
97
98 #else
99 #if defined(HAVE_SYS_VFSTAB_H) /* } { */
100 /*
101 ** SVR4 (GETFSENT_SOLARIS)
102 */
103 #define GETFSENT_TYPE "SVR4 (Solaris)"
104
105 #include <sys/vfstab.h>
106
107 static FILE *fstabf = NULL;
108
109 int open_fstab()
110 {
111     close_fstab();
112     return (fstabf = fopen(VFSTAB, "r")) != NULL;
113 }
114
115 void close_fstab()
116 {
117     if(fstabf)
118         afclose(fstabf);
119     fstabf = NULL;
120 }
121
122 int get_fstab_nextentry(fsent)
123 generic_fsent_t *fsent;
124 {
125     struct vfstab sys_fsent;
126
127     if(getvfsent(fstabf, &sys_fsent) != 0)
128         return 0;
129
130     fsent->fsname  = sys_fsent.vfs_special;
131     fsent->fstype  = sys_fsent.vfs_fstype;
132     fsent->mntdir  = sys_fsent.vfs_mountp;
133     fsent->mntopts = sys_fsent.vfs_mntopts;
134     fsent->freq    = 1; /* N/A */
135     fsent->passno  = sys_fsent.vfs_fsckpass? atoi(sys_fsent.vfs_fsckpass) : 0;
136     return 1;
137 }
138
139 #else
140 #  if defined(HAVE_MNTENT_H) /* } { */
141
142 /*
143 ** System V.3 (GETFSENT_SVR3, GETFSENT_LINUX)
144 */
145 #define GETFSENT_TYPE "SVR3 (NeXTstep, Irix, Linux, HP-UX)"
146
147 #include <mntent.h>
148
149 #if defined(HAVE_ENDMNTENT)
150 #define AMCLOSE_MNTENT(x)       endmntent(x)
151 #else
152 #define AMCLOSE_MNTENT(x)       fclose(x)
153 #endif
154
155 static FILE *fstabf1 = NULL;            /* /proc/mounts */
156 static FILE *fstabf2 = NULL;            /* MOUNTED */
157 static FILE *fstabf3 = NULL;            /* MNTTAB */
158
159 int open_fstab()
160 {
161     close_fstab();
162 #if defined(HAVE_SETMNTENT)
163     fstabf1 = setmntent("/proc/mounts", "r");
164 # if defined(MOUNTED)
165     fstabf2 = setmntent(MOUNTED, "r");
166 # endif
167 # if defined(MNTTAB)
168     fstabf3 = setmntent(MNTTAB, "r");
169 # endif
170 #else
171 # if defined(MNTTAB)
172     fstabf3 = fopen(MNTTAB, "r");
173 # endif
174 #endif
175     return (fstabf1 != NULL || fstabf2 != NULL || fstabf3 != NULL);
176 }
177
178 void close_fstab()
179 {
180     if (fstabf1) {
181         AMCLOSE_MNTENT(fstabf1);
182         fstabf1 = NULL;
183     }
184     if (fstabf2) {
185         AMCLOSE_MNTENT(fstabf2);
186         fstabf2 = NULL;
187     }
188     if (fstabf3) {
189         AMCLOSE_MNTENT(fstabf3);
190         fstabf3 = NULL;
191     }
192 }
193
194 int get_fstab_nextentry(fsent)
195 generic_fsent_t *fsent;
196 {
197     struct mntent *sys_fsent = NULL;
198
199     if(fstabf1) {
200         sys_fsent = getmntent(fstabf1);
201         if(!sys_fsent) {
202             AMCLOSE_MNTENT(fstabf1);
203             fstabf1 = NULL;
204         }
205     }
206     if(!sys_fsent && fstabf2) {
207         sys_fsent = getmntent(fstabf2);
208         if(!sys_fsent) {
209             AMCLOSE_MNTENT(fstabf2);
210             fstabf2 = NULL;
211         }
212     }
213     if(!sys_fsent && fstabf3) {
214         sys_fsent = getmntent(fstabf3);
215         if(!sys_fsent) {
216             AMCLOSE_MNTENT(fstabf3);
217             fstabf3 = NULL;
218         }
219     }
220     if(!sys_fsent) {
221         return 0;
222     }
223
224     fsent->fsname  = sys_fsent->mnt_fsname;
225     fsent->fstype  = sys_fsent->mnt_type;
226     fsent->mntdir  = sys_fsent->mnt_dir;
227     fsent->mntopts = sys_fsent->mnt_opts;
228     fsent->freq    = sys_fsent->mnt_freq;
229     fsent->passno  = sys_fsent->mnt_passno;
230     return 1;
231 }
232
233 #  else
234 #    if defined(HAVE_SYS_MNTTAB_H) || defined(STATFS_SCO_OS5) /* } { */
235
236 /* we won't actually include mnttab.h, since it contains nothing useful.. */
237
238 #define GETFSENT_TYPE "SVR3 (Interactive UNIX)"
239
240 #include <stdio.h>
241 #include <string.h>
242 #include <ctype.h>
243
244 #define FSTAB "/etc/fstab"
245
246 static FILE *fstabf = NULL;
247
248 int open_fstab()
249 {
250     close_fstab();
251     return (fstabf = fopen(FSTAB, "r")) != NULL;
252 }
253
254 void close_fstab()
255 {
256     if(fstabf)
257         afclose(fstabf);
258     fstabf = NULL;
259 }
260
261 static generic_fsent_t _fsent;
262
263 int get_fstab_nextentry(fsent)
264 generic_fsent_t *fsent;
265 {
266     static char *lfsnam = NULL;
267     static char *opts = NULL;
268     static char *cp = NULL;
269     char *s;
270     int ch;
271
272     amfree(cp);
273     for (; (cp = agets(fstabf)) != NULL; free(cp)) {
274         fsent->fsname = strtok(cp, " \t");
275         if ( fsent->fsname && *fsent->fsname != '#' )
276             break;
277     }
278     if (cp == NULL) return 0;
279
280     fsent->mntdir = strtok((char *)NULL, " \t");
281     fsent->mntopts = strtok((char *)NULL, " \t");
282     if ( *fsent->mntopts != '-' )  {
283         fsent->fstype = fsent->mntopts;
284         fsent->mntopts = "rw";
285     } else {
286         fsent->fstype = "";
287         if (strcmp(fsent->mntopts, "-r") == 0) {
288             fsent->mntopts = "ro";
289         }
290     }
291     if ((s = strchr(fsent->fstype, ',')) != NULL) {
292         *s++ = '\0';
293         strappend(fsent->mntopts, ",");
294         strappend(fsent->mntopts, s);
295     }
296
297     lfsnam = newstralloc(lfsnam, fsent->fstype);
298     s = lfsnam;
299     while((ch = *s++) != '\0') {
300         if(isupper(ch)) ch = tolower(ch);
301         s[-1] = ch;
302     }
303     fsent->fstype = lfsnam;
304
305 #define sc "hs"
306     if (strncmp(fsent->fstype, sc, sizeof(sc)-1) == 0)
307         fsent->fstype = "iso9660";
308 #undef sc
309
310     fsent->freq = 0;
311     fsent->passno = 0;
312
313     return 1;
314 }
315
316 #    else
317 #      if defined(HAVE_MNTTAB_H) /* } { */
318
319 #define GETFSENT_TYPE "SVR3 (SCO UNIX)"
320
321 #include <mnttab.h>
322 #include <sys/fstyp.h>
323 #include <sys/statfs.h>
324
325 #define MNTTAB "/etc/mnttab"
326
327 /*
328  * If these are defined somewhere please let me know.
329  */
330
331 #define MNT_READONLY 0101
332 #define MNT_READWRITE 0100
333
334 static FILE *fstabf = NULL;
335
336 int open_fstab()
337 {
338     close_fstab();
339     return (fstabf = fopen(MNTTAB, "r")) != NULL;
340 }
341
342 void close_fstab()
343 {
344     if(fstabf)
345         afclose(fstabf);
346     fstabf = NULL;
347 }
348
349 static generic_fsent_t _fsent;
350
351 int get_fstab_nextentry(fsent)
352 generic_fsent_t *fsent;
353 {
354     struct statfs fsd;
355     char typebuf[FSTYPSZ];
356     static struct mnttab mnt;
357     char *dp, *ep;
358
359     if(!fread (&mnt, sizeof mnt, 1, fstabf))
360       return 0;
361
362     fsent->fsname  = mnt.mt_dev;
363     fsent->mntdir  = mnt.mt_filsys;
364     fsent->fstype = "";
365
366     if (statfs (fsent->mntdir, &fsd, sizeof fsd, 0) != -1
367         && sysfs (GETFSTYP, fsd.f_fstyp, typebuf) != -1) {
368        dp = typebuf;
369        ep = fsent->fstype = malloc(strlen(typebuf)+2);
370        while (*dp)
371             *ep++ = tolower(*dp++);
372        *ep=0;
373     }
374
375     if ( mnt.mt_ro_flg == MNT_READONLY ) {
376         fsent->mntopts = "ro";
377     } else {
378         fsent->mntopts = "rw";
379     }
380
381     fsent->freq = 0;
382     fsent->passno = 0;
383     return 1;
384 }
385
386 #      else /* } { */
387
388 #define GETFSENT_TYPE "undefined"
389
390 #      endif
391 #    endif
392 #  endif
393 #endif
394 #endif /* } */
395
396 /*
397  *=====================================================================
398  * Convert either a block or character device name to a character (raw)
399  * device name.
400  *
401  * static char *dev2rdev(const char *name);
402  *
403  * entry:       name - device name to convert
404  * exit:        matching character device name if found,
405  *              otherwise returns the input
406  *
407  * The input must be an absolute path.
408  *
409  * The exit string area is always an alloc-d area that the caller is
410  * responsible for releasing.
411  *=====================================================================
412  */
413
414 static char *dev2rdev(name)
415 char *name;
416 {
417   char *fname = NULL;
418   struct stat st;
419   char *s;
420   int ch;
421
422   if(stat(name, &st) == 0 && S_ISCHR(st.st_mode)) {
423     /*
424      * If the input is already a character device, just return it.
425      */
426     return stralloc(name);
427   }
428
429   s = name;
430   ch = *s++;
431
432   if(ch == '\0' || ch != '/') return stralloc(name);
433
434   ch = *s++;                                    /* start after first '/' */
435   /*
436    * Break the input path at each '/' and create a new name with an
437    * 'r' before the right part.  For instance:
438    *
439    *   /dev/sd0a -> /dev/rsd0a
440    *   /dev/dsk/c0t0d0s0 -> /dev/rdsk/c0t0d0s0 -> /dev/dsk/rc0t0d0s0
441    */
442   while(ch) {
443     if (ch == '/') {
444       s[-1] = '\0';
445       fname = newvstralloc(fname, name, "/r", s, NULL);
446       s[-1] = ch;
447       if(stat(fname, &st) == 0 && S_ISCHR(st.st_mode)) return fname;
448     }
449     ch = *s++;
450   }
451   amfree(fname);
452   return stralloc(name);                        /* no match */
453 }
454
455 static int samefile(stats, estat)
456 struct stat stats[3], *estat;
457 {
458   int i;
459   for(i = 0; i < 3; ++i) {
460     if (stats[i].st_dev == estat->st_dev &&
461         stats[i].st_ino == estat->st_ino)
462       return 1;
463   }
464   return 0;
465 }
466
467 int search_fstab(name, fsent, check_dev)
468      char *name;
469      generic_fsent_t *fsent;
470      int check_dev;
471 {
472 #ifdef IGNORE_FSTAB
473   /* There is no real mount table so this will always fail and
474    * we are using GNU tar so we can just return here.
475    */
476   return 0;
477 #else
478   struct stat stats[3];
479   char *fullname = NULL;
480   char *rdev = NULL;
481   int rc;
482
483   if (!name)
484     return 0;
485
486   stats[0].st_dev = stats[1].st_dev = stats[2].st_dev = -1;
487
488   if (stat(name, &stats[0]) == -1)
489     stats[0].st_dev = -1;
490   if (name[0] != '/') {
491     fullname = stralloc2(DEV_PREFIX, name);
492     if (stat(fullname, &stats[1]) == -1)
493       stats[1].st_dev = -1;
494     fullname = newstralloc2(fullname, RDEV_PREFIX, name);
495     if (stat(fullname, &stats[2]) == -1)
496       stats[2].st_dev = -1;
497     amfree(fullname);
498   }
499   else if (stat((rdev = dev2rdev(name)), &stats[1]) == -1)
500     stats[1].st_dev = -1;
501
502   amfree(rdev);
503
504   if (!open_fstab())
505     return 0;
506
507   rc = 0;
508   while(get_fstab_nextentry(fsent)) {
509     struct stat mntstat;
510     struct stat fsstat;
511     struct stat fsrstat;
512     int smnt = -1, sfs = -1, sfsr = -1;
513
514     amfree(rdev);
515
516     if(fsent->mntdir != NULL &&
517        (smnt = stat(fsent->mntdir, &mntstat)) == -1)
518       continue;
519
520     if(fsent->fsname != NULL) {
521       sfs = stat(fsent->fsname, &fsstat);
522       sfsr = stat((rdev = dev2rdev(fsent->fsname)), &fsrstat);
523       if(check_dev == 1 && sfs == -1 && sfsr == -1)
524         continue;
525     }
526
527     if((fsent->mntdir != NULL &&
528         smnt != -1 &&
529         samefile(stats, &mntstat)) || 
530        (fsent->fsname != NULL &&
531         sfs != -1 &&
532         samefile(stats, &fsstat)) ||
533        (fsent->fsname != NULL &&
534         sfsr != -1 &&
535         samefile(stats, &fsrstat))) {
536       rc = 1;
537       break;
538     }
539   }
540   amfree(rdev);
541   close_fstab();
542   return rc;
543 #endif /* !IGNORE_FSTAB */
544 }
545
546 int is_local_fstype(fsent)
547 generic_fsent_t *fsent;
548 {
549     if(fsent->fstype == NULL)   /* unknown, assume local */
550         return 1;
551
552     /* just eliminate fstypes known to be remote or unsavable */
553
554     return strcmp(fsent->fstype, "nfs") != 0 && /* NFS */
555            strcmp(fsent->fstype, "afs") != 0 && /* Andrew Filesystem */
556            strcmp(fsent->fstype, "swap") != 0 && /* Swap */
557            strcmp(fsent->fstype, "iso9660") != 0 && /* CDROM */
558            strcmp(fsent->fstype, "hs") != 0 && /* CDROM */
559            strcmp(fsent->fstype, "piofs") != 0; /* an AIX printer thing? */
560 }
561
562
563 char *amname_to_devname(str)
564 char *str;
565 {
566     generic_fsent_t fsent;
567
568     if(search_fstab(str, &fsent, 1) && fsent.fsname != NULL)
569         str = fsent.fsname;
570     else if(search_fstab(str, &fsent, 0) && fsent.fsname != NULL)
571         str = fsent.fsname;
572
573     return dev2rdev(str);
574 }
575
576 char *amname_to_dirname(str)
577 char *str;
578 {
579     generic_fsent_t fsent;
580
581     if(search_fstab(str, &fsent, 1) && fsent.mntdir != NULL)
582         str = fsent.mntdir;
583     else if(search_fstab(str, &fsent, 0) && fsent.mntdir != NULL)
584         str = fsent.mntdir;
585
586     return stralloc(str);
587 }
588
589 char *amname_to_fstype(str)
590 char *str;
591 {
592     generic_fsent_t fsent;
593
594     if (!search_fstab(str, &fsent, 1) && !search_fstab(str, &fsent, 0))
595       return stralloc("");
596
597     return stralloc(fsent.fstype);
598 }
599
600 #ifdef TEST
601
602 void
603 print_entry(fsent)
604 generic_fsent_t *fsent;
605 {
606 #define nchk(s) ((s)? (s) : "<NULL>")
607     printf("%-20.20s %-14.14s %-7.7s %4d %5d %s\n",
608            nchk(fsent->fsname), nchk(fsent->mntdir), nchk(fsent->fstype),
609            fsent->freq, fsent->passno, nchk(fsent->mntopts));
610 }
611
612 int main(argc, argv)
613     int argc;
614     char **argv;
615 {
616     generic_fsent_t fsent;
617     char *s;
618     char *name = NULL;
619     unsigned long malloc_hist_1, malloc_size_1;
620     unsigned long malloc_hist_2, malloc_size_2;
621
622     safe_fd(-1, 0);
623
624     set_pname("getfsent");
625
626     /* Don't die when child closes pipe */
627     signal(SIGPIPE, SIG_IGN);
628
629     malloc_size_1 = malloc_inuse(&malloc_hist_1);
630
631     if(!open_fstab()) {
632         fprintf(stderr, "getfsent_test: could not open fstab\n");
633         return 1;
634     }
635
636     printf("getfsent (%s)\n",GETFSENT_TYPE);
637     printf("l/r fsname               mntdir         fstype  freq pass# mntopts\n");
638     while(get_fstab_nextentry(&fsent)) {
639         printf("%c  ",is_local_fstype(&fsent)? 'l' : 'r');
640         print_entry(&fsent);
641     }
642     printf("--------\n");
643
644     close_fstab();
645
646     name = newstralloc(name, "/usr");
647     if(search_fstab(name, &fsent, 1) || search_fstab(name, &fsent, 0)) {
648         printf("Found %s mount for %s:\n",
649                is_local_fstype(&fsent)? "local" : "remote", name);
650         print_entry(&fsent);
651     }
652     else 
653         printf("Mount for %s not found\n", name);
654
655     name = newstralloc(name, "/");
656     if(search_fstab(name, &fsent, 1) || search_fstab(name, &fsent, 0)) {
657         printf("Found %s mount for %s:\n",
658                is_local_fstype(&fsent)? "local" : "remote", name);
659         print_entry(&fsent);
660     }
661     else 
662         printf("Mount for %s not found\n", name);
663
664     name = newstralloc(name, "/");
665     s = amname_to_fstype(name);
666     printf("fstype of `%s': %s\n", name, s);
667     amfree(s);
668     name = newstralloc(name, "/dev/root");
669     s = amname_to_fstype(name);
670     printf("fstype of `%s': %s\n", name, s);
671     amfree(s);
672     name = newstralloc(name, "/usr");
673     s = amname_to_fstype(name);
674     printf("fstype of `%s': %s\n", name, s);
675     amfree(s);
676     name = newstralloc(name, "c0t3d0s0");
677     s = amname_to_fstype(name);
678     printf("fstype of `%s': %s\n", name, s);
679     amfree(s);
680
681     name = newstralloc(name, "/tmp/foo");
682     s = amname_to_devname(name);
683     printf("device of `%s': %s\n", name, s);
684     amfree(s);
685     s = amname_to_dirname(name);
686     printf("dirname of `%s': %s\n", name, s);
687     amfree(s);
688     s = amname_to_fstype(name);
689     printf("fstype of `%s': %s\n", name, s);
690     amfree(s);
691
692     name = newstralloc(name, "./foo");
693     s = amname_to_devname(name);
694     printf("device of `%s': %s\n", name, s);
695     amfree(s);
696     s = amname_to_dirname(name);
697     printf("dirname of `%s': %s\n", name, s);
698     amfree(s);
699     s = amname_to_fstype(name);
700     printf("fstype of `%s': %s\n", name, s);
701     amfree(s);
702
703     while (--argc > 0) {
704         name = newstralloc(name, *++argv);
705         s = amname_to_devname(name);
706         printf("device of `%s': %s\n", name, s);
707         amfree(s);
708         s = amname_to_dirname(name);
709         printf("dirname of `%s': %s\n", name, s);
710         amfree(s);
711         s = amname_to_fstype(name);
712         printf("fstype of `%s': %s\n", name, s);
713         amfree(s);
714     }
715
716     amfree(name);
717
718     malloc_size_2 = malloc_inuse(&malloc_hist_2);
719
720     if(malloc_size_1 != malloc_size_2) {
721         malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2);
722     }
723
724     return 0;
725 }
726
727 #endif