Imported Upstream version 2.5.2p1
[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.38 2006/07/19 17:41:14 martinea 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 #endif
38
39 #include "getfsent.h"
40
41 static char *dev2rdev(char *);
42
43 /*
44  * You are in a twisty maze of passages, all alike.
45  * Geesh.
46  */
47
48 #if defined(HAVE_FSTAB_H) && !defined(HAVE_MNTENT_H) /* { */
49 /*
50 ** BSD (GETFSENT_BSD)
51 */
52 #define GETFSENT_TYPE "BSD (Ultrix, AIX)"
53
54 #include <fstab.h>
55
56 int
57 open_fstab(void)
58 {
59     return setfsent();
60 }
61
62 void
63 close_fstab(void)
64 {
65     endfsent();
66 }
67
68
69 int
70 get_fstab_nextentry(
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
110 open_fstab(void)
111 {
112     close_fstab();
113     return (fstabf = fopen(VFSTAB, "r")) != NULL;
114 }
115
116 void
117 close_fstab(void)
118 {
119     if(fstabf)
120         afclose(fstabf);
121     fstabf = NULL;
122 }
123
124 int
125 get_fstab_nextentry(
126     generic_fsent_t *   fsent)
127 {
128     struct vfstab sys_fsent;
129
130     memset(&sys_fsent, 0, SIZEOF(sys_fsent));
131     if(getvfsent(fstabf, &sys_fsent) != 0)
132         return 0;
133
134     fsent->fsname  = sys_fsent.vfs_special;
135     fsent->fstype  = sys_fsent.vfs_fstype;
136     fsent->mntdir  = sys_fsent.vfs_mountp;
137     fsent->mntopts = sys_fsent.vfs_mntopts;
138     fsent->freq    = 1; /* N/A */
139     fsent->passno  = sys_fsent.vfs_fsckpass? atoi(sys_fsent.vfs_fsckpass) : 0;
140     return 1;
141 }
142
143 #else
144 #  if defined(HAVE_MNTENT_H) /* } { */
145
146 /*
147 ** System V.3 (GETFSENT_SVR3, GETFSENT_LINUX)
148 */
149 #define GETFSENT_TYPE "SVR3 (NeXTstep, Irix, Linux, HP-UX)"
150
151 #include <mntent.h>
152
153 #if defined(HAVE_ENDMNTENT)
154 #define AMCLOSE_MNTENT(x)       endmntent(x)
155 #else
156 #define AMCLOSE_MNTENT(x)       fclose(x)
157 #endif
158
159 static FILE *fstabf1 = NULL;            /* /proc/mounts */
160 static FILE *fstabf2 = NULL;            /* MOUNTED */
161 static FILE *fstabf3 = NULL;            /* MNTTAB */
162
163 int
164 open_fstab(void)
165 {
166     close_fstab();
167 #if defined(HAVE_SETMNTENT)
168     fstabf1 = setmntent("/proc/mounts", "r");
169 # if defined(MOUNTED)
170     fstabf2 = setmntent(MOUNTED, "r");
171 # endif
172 # if defined(MNTTAB)
173     fstabf3 = setmntent(MNTTAB, "r");
174 # endif
175 #else
176 # if defined(MNTTAB)
177     fstabf3 = fopen(MNTTAB, "r");
178 # endif
179 #endif
180     return (fstabf1 != NULL || fstabf2 != NULL || fstabf3 != NULL);
181 }
182
183 void
184 close_fstab(void)
185 {
186     if (fstabf1) {
187         AMCLOSE_MNTENT(fstabf1);
188         fstabf1 = NULL;
189     }
190     if (fstabf2) {
191         AMCLOSE_MNTENT(fstabf2);
192         fstabf2 = NULL;
193     }
194     if (fstabf3) {
195         AMCLOSE_MNTENT(fstabf3);
196         fstabf3 = NULL;
197     }
198 }
199
200 int
201 get_fstab_nextentry(
202     generic_fsent_t *   fsent)
203 {
204     struct mntent *sys_fsent = NULL;
205
206     if(fstabf1) {
207         sys_fsent = getmntent(fstabf1);
208         if(!sys_fsent) {
209             AMCLOSE_MNTENT(fstabf1);
210             fstabf1 = NULL;
211         }
212     }
213     if(!sys_fsent && fstabf2) {
214         sys_fsent = getmntent(fstabf2);
215         if(!sys_fsent) {
216             AMCLOSE_MNTENT(fstabf2);
217             fstabf2 = NULL;
218         }
219     }
220     if(!sys_fsent && fstabf3) {
221         sys_fsent = getmntent(fstabf3);
222         if(!sys_fsent) {
223             AMCLOSE_MNTENT(fstabf3);
224             fstabf3 = NULL;
225         }
226     }
227     if(!sys_fsent) {
228         return 0;
229     }
230
231     fsent->fsname  = sys_fsent->mnt_fsname;
232     fsent->fstype  = sys_fsent->mnt_type;
233     fsent->mntdir  = sys_fsent->mnt_dir;
234     fsent->mntopts = sys_fsent->mnt_opts;
235     fsent->freq    = sys_fsent->mnt_freq;
236     fsent->passno  = sys_fsent->mnt_passno;
237     return 1;
238 }
239
240 #  else
241 #    if defined(HAVE_SYS_MNTTAB_H) || defined(STATFS_SCO_OS5) /* } { */
242
243 /* we won't actually include mnttab.h, since it contains nothing useful.. */
244
245 #define GETFSENT_TYPE "SVR3 (Interactive UNIX)"
246
247 #include <stdio.h>
248 #include <string.h>
249 #include <ctype.h>
250
251 #define FSTAB "/etc/fstab"
252
253 static FILE *fstabf = NULL;
254
255 int
256 open_fstab(void)
257 {
258     close_fstab();
259     return (fstabf = fopen(FSTAB, "r")) != NULL;
260 }
261
262 void
263 close_fstab(void)
264 {
265     if(fstabf)
266         afclose(fstabf);
267     fstabf = NULL;
268 }
269
270 static generic_fsent_t _fsent;
271
272 int
273 get_fstab_nextentry(
274     generic_fsent_t *   fsent)
275 {
276     static char *lfsnam = NULL;
277     static char *opts = NULL;
278     static char *cp = NULL;
279     char *s;
280     int ch;
281
282     amfree(cp);
283     for (; (cp = agets(fstabf)) != NULL; free(cp)) {
284         if (cp[0] == '\0')
285             continue;
286         fsent->fsname = strtok(cp, " \t");
287         if ( fsent->fsname && *fsent->fsname != '#' )
288             break;
289     }
290     if (cp == NULL) return 0;
291
292     fsent->mntdir = strtok((char *)NULL, " \t");
293     fsent->mntopts = strtok((char *)NULL, " \t");
294     if ( *fsent->mntopts != '-' )  {
295         fsent->fstype = fsent->mntopts;
296         fsent->mntopts = "rw";
297     } else {
298         fsent->fstype = "";
299         if (strcmp(fsent->mntopts, "-r") == 0) {
300             fsent->mntopts = "ro";
301         }
302     }
303     if ((s = strchr(fsent->fstype, ',')) != NULL) {
304         *s++ = '\0';
305         strappend(fsent->mntopts, ",");
306         strappend(fsent->mntopts, s);
307     }
308
309     lfsnam = newstralloc(lfsnam, fsent->fstype);
310     s = lfsnam;
311     while((ch = *s++) != '\0') {
312         if(isupper(ch)) ch = tolower(ch);
313         s[-1] = ch;
314     }
315     fsent->fstype = lfsnam;
316
317     if (strncmp_const(fsent->fstype, "hs") == 0)
318         fsent->fstype = "iso9660";
319
320     fsent->freq = 0;
321     fsent->passno = 0;
322
323     return 1;
324 }
325
326 #    else
327 #      if defined(HAVE_MNTTAB_H) /* } { */
328
329 #define GETFSENT_TYPE "SVR3 (SCO UNIX)"
330
331 #include <mnttab.h>
332 #include <sys/fstyp.h>
333 #include <sys/statfs.h>
334
335 #define MNTTAB "/etc/mnttab"
336
337 /*
338  * If these are defined somewhere please let me know.
339  */
340
341 #define MNT_READONLY 0101
342 #define MNT_READWRITE 0100
343
344 static FILE *fstabf = NULL;
345
346 int
347 open_fstab(void)
348 {
349     close_fstab();
350     return (fstabf = fopen(MNTTAB, "r")) != NULL;
351 }
352
353 void
354 close_fstab(void)
355 {
356     if(fstabf)
357         afclose(fstabf);
358     fstabf = NULL;
359 }
360
361 static generic_fsent_t _fsent;
362
363 int
364 get_fstab_nextentry(
365     generic_fsent_t *fsent)
366 {
367     struct statfs fsd;
368     char typebuf[FSTYPSZ];
369     static struct mnttab mnt;
370     char *dp, *ep;
371
372     if(!fread (&mnt, SIZEOF(mnt), 1, fstabf))
373       return 0;
374
375     fsent->fsname  = mnt.mt_dev;
376     fsent->mntdir  = mnt.mt_filsys;
377     fsent->fstype = "";
378
379     if (statfs (fsent->mntdir, &fsd, SIZEOF(fsd), 0) != -1
380         && sysfs (GETFSTYP, fsd.f_fstyp, typebuf) != -1) {
381        dp = typebuf;
382        ep = fsent->fstype = malloc(strlen(typebuf)+2);
383        while (*dp)
384             *ep++ = tolower(*dp++);
385        *ep=0;
386     }
387
388     if ( mnt.mt_ro_flg == MNT_READONLY ) {
389         fsent->mntopts = "ro";
390     } else {
391         fsent->mntopts = "rw";
392     }
393
394     fsent->freq = 0;
395     fsent->passno = 0;
396     return 1;
397 }
398
399 #      else /* } { */
400
401 #define GETFSENT_TYPE "undefined"
402
403 #      endif
404 #    endif
405 #  endif
406 #endif
407 #endif /* } */
408
409 /*
410  *=====================================================================
411  * Convert either a block or character device name to a character (raw)
412  * device name.
413  *
414  * static char *dev2rdev(const char *name);
415  *
416  * entry:       name - device name to convert
417  * exit:        matching character device name if found,
418  *              otherwise returns the input
419  *
420  * The input must be an absolute path.
421  *
422  * The exit string area is always an alloc-d area that the caller is
423  * responsible for releasing.
424  *=====================================================================
425  */
426
427 static char *
428 dev2rdev(
429     char *      name)
430 {
431   char *fname = NULL;
432   struct stat st;
433   char *s;
434   int ch;
435
436   if(stat(name, &st) == 0 && S_ISCHR(st.st_mode)) {
437     /*
438      * If the input is already a character device, just return it.
439      */
440     return stralloc(name);
441   }
442
443   s = name;
444   ch = *s++;
445
446   if(ch == '\0' || ch != '/') return stralloc(name);
447
448   ch = *s++;                                    /* start after first '/' */
449   /*
450    * Break the input path at each '/' and create a new name with an
451    * 'r' before the right part.  For instance:
452    *
453    *   /dev/sd0a -> /dev/rsd0a
454    *   /dev/dsk/c0t0d0s0 -> /dev/rdsk/c0t0d0s0 -> /dev/dsk/rc0t0d0s0
455    */
456   while(ch) {
457     if (ch == '/') {
458       s[-1] = '\0';
459       fname = newvstralloc(fname, name, "/r", s, NULL);
460       s[-1] = (char)ch;
461       if(stat(fname, &st) == 0 && S_ISCHR(st.st_mode)) return fname;
462     }
463     ch = *s++;
464   }
465   amfree(fname);
466   return stralloc(name);                        /* no match */
467 }
468
469 #ifndef IGNORE_FSTAB
470 static int samefile(struct stat[3], struct stat *);
471
472 static int
473 samefile(
474     struct stat stats[3],
475     struct stat *estat)
476 {
477   int i;
478   for(i = 0; i < 3; ++i) {
479     if (stats[i].st_dev == estat->st_dev &&
480         stats[i].st_ino == estat->st_ino)
481       return 1;
482   }
483   return 0;
484 }
485 #endif /* !IGNORE_FSTAB */
486
487 int
488 search_fstab(
489      char *             name,
490      generic_fsent_t *  fsent,
491      int                check_dev)
492 {
493 #ifdef IGNORE_FSTAB
494   /* There is no real mount table so this will always fail and
495    * we are using GNU tar so we can just return here.
496    */
497   (void)name;           /* Quiet unused parameter warning */
498   (void)fsent;          /* Quiet unused parameter warning */
499   (void)check_dev;      /* Quiet unused parameter warning */
500   return 0;
501 #else
502   struct stat stats[3];
503   char *fullname = NULL;
504   char *rdev = NULL;
505   int rc;
506
507   if (!name)
508     return 0;
509
510   memset(stats, 0, SIZEOF(stats));
511   stats[0].st_dev = stats[1].st_dev = stats[2].st_dev = (dev_t)-1;
512
513   if (stat(name, &stats[0]) == -1)
514     stats[0].st_dev = (dev_t)-1;
515   if (name[0] != '/') {
516     fullname = stralloc2(DEV_PREFIX, name);
517     if (stat(fullname, &stats[1]) == -1)
518       stats[1].st_dev = (dev_t)-1;
519     fullname = newstralloc2(fullname, RDEV_PREFIX, name);
520     if (stat(fullname, &stats[2]) == -1)
521       stats[2].st_dev = (dev_t)-1;
522     amfree(fullname);
523   }
524   else if (stat((rdev = dev2rdev(name)), &stats[1]) == -1)
525     stats[1].st_dev = (dev_t)-1;
526
527   amfree(rdev);
528
529   if (!open_fstab())
530     return 0;
531
532   rc = 0;
533   while(get_fstab_nextentry(fsent)) {
534     struct stat mntstat;
535     struct stat fsstat;
536     struct stat fsrstat;
537     int smnt = -1, sfs = -1, sfsr = -1;
538
539     amfree(rdev);
540
541     if(fsent->mntdir != NULL &&
542        (smnt = stat(fsent->mntdir, &mntstat)) == -1)
543       continue;
544
545     if(fsent->fsname != NULL) {
546       sfs = stat(fsent->fsname, &fsstat);
547       sfsr = stat((rdev = dev2rdev(fsent->fsname)), &fsrstat);
548       if(check_dev == 1 && sfs == -1 && sfsr == -1)
549         continue;
550     }
551
552     if((fsent->mntdir != NULL &&
553         smnt != -1 &&
554         samefile(stats, &mntstat)) || 
555        (fsent->fsname != NULL &&
556         sfs != -1 &&
557         samefile(stats, &fsstat)) ||
558        (fsent->fsname != NULL &&
559         sfsr != -1 &&
560         samefile(stats, &fsrstat))) {
561       rc = 1;
562       break;
563     }
564   }
565   amfree(rdev);
566   close_fstab();
567   return rc;
568 #endif /* !IGNORE_FSTAB */
569 }
570
571 int
572 is_local_fstype(
573     generic_fsent_t *   fsent)
574 {
575     if(fsent->fstype == NULL)   /* unknown, assume local */
576         return 1;
577
578     /* just eliminate fstypes known to be remote or unsavable */
579
580     return strcmp(fsent->fstype, "nfs") != 0 && /* NFS */
581            strcmp(fsent->fstype, "afs") != 0 && /* Andrew Filesystem */
582            strcmp(fsent->fstype, "swap") != 0 && /* Swap */
583            strcmp(fsent->fstype, "iso9660") != 0 && /* CDROM */
584            strcmp(fsent->fstype, "hs") != 0 && /* CDROM */
585            strcmp(fsent->fstype, "piofs") != 0; /* an AIX printer thing? */
586 }
587
588
589 char *
590 amname_to_devname(
591     char *      str)
592 {
593     generic_fsent_t fsent;
594
595     if(search_fstab(str, &fsent, 1) && fsent.fsname != NULL)
596         str = fsent.fsname;
597     else if(search_fstab(str, &fsent, 0) && fsent.fsname != NULL)
598         str = fsent.fsname;
599
600     return dev2rdev(str);
601 }
602
603 char *
604 amname_to_dirname(
605     char *      str)
606 {
607     generic_fsent_t fsent;
608
609     if(search_fstab(str, &fsent, 1) && fsent.mntdir != NULL)
610         str = fsent.mntdir;
611     else if(search_fstab(str, &fsent, 0) && fsent.mntdir != NULL)
612         str = fsent.mntdir;
613
614     return stralloc(str);
615 }
616
617 char *amname_to_fstype(
618     char *      str)
619 {
620     generic_fsent_t fsent;
621
622     if (!search_fstab(str, &fsent, 1) && !search_fstab(str, &fsent, 0))
623       return stralloc("");
624
625     return stralloc(fsent.fstype);
626 }
627
628 #ifdef TEST
629
630 void print_entry(generic_fsent_t *fsent);
631
632 void
633 print_entry(
634     generic_fsent_t *   fsent)
635 {
636 #define nchk(s) ((s)? (s) : "<NULL>")
637     printf("%-20.20s %-14.14s %-7.7s %4d %5d %s\n",
638            nchk(fsent->fsname), nchk(fsent->mntdir), nchk(fsent->fstype),
639            fsent->freq, fsent->passno, nchk(fsent->mntopts));
640 }
641
642 int
643 main(
644     int         argc,
645     char **     argv)
646 {
647     generic_fsent_t fsent;
648     char *s;
649     char *name = NULL;
650     unsigned long malloc_hist_1, malloc_size_1;
651     unsigned long malloc_hist_2, malloc_size_2;
652
653     safe_fd(-1, 0);
654
655     set_pname("getfsent");
656
657     dbopen(NULL);
658
659     /* Don't die when child closes pipe */
660     signal(SIGPIPE, SIG_IGN);
661
662     malloc_size_1 = malloc_inuse(&malloc_hist_1);
663
664     if(!open_fstab()) {
665         fprintf(stderr, "getfsent_test: could not open fstab\n");
666         return 1;
667     }
668
669     printf("getfsent (%s)\n",GETFSENT_TYPE);
670     printf("l/r fsname               mntdir         fstype  freq pass# mntopts\n");
671     while(get_fstab_nextentry(&fsent)) {
672         printf("%c  ",is_local_fstype(&fsent)? 'l' : 'r');
673         print_entry(&fsent);
674     }
675     printf("--------\n");
676
677     close_fstab();
678
679     name = newstralloc(name, "/usr");
680     if(search_fstab(name, &fsent, 1) || search_fstab(name, &fsent, 0)) {
681         printf("Found %s mount for %s:\n",
682                is_local_fstype(&fsent)? "local" : "remote", name);
683         print_entry(&fsent);
684     }
685     else 
686         printf("Mount for %s not found\n", name);
687
688     name = newstralloc(name, "/");
689     if(search_fstab(name, &fsent, 1) || search_fstab(name, &fsent, 0)) {
690         printf("Found %s mount for %s:\n",
691                is_local_fstype(&fsent)? "local" : "remote", name);
692         print_entry(&fsent);
693     }
694     else 
695         printf("Mount for %s not found\n", name);
696
697     name = newstralloc(name, "/");
698     s = amname_to_fstype(name);
699     printf("fstype of `%s': %s\n", name, s);
700     amfree(s);
701     name = newstralloc(name, "/dev/root");
702     s = amname_to_fstype(name);
703     printf("fstype of `%s': %s\n", name, s);
704     amfree(s);
705     name = newstralloc(name, "/usr");
706     s = amname_to_fstype(name);
707     printf("fstype of `%s': %s\n", name, s);
708     amfree(s);
709     name = newstralloc(name, "c0t3d0s0");
710     s = amname_to_fstype(name);
711     printf("fstype of `%s': %s\n", name, s);
712     amfree(s);
713
714     name = newstralloc(name, "/tmp/foo");
715     s = amname_to_devname(name);
716     printf("device of `%s': %s\n", name, s);
717     amfree(s);
718     s = amname_to_dirname(name);
719     printf("dirname of `%s': %s\n", name, s);
720     amfree(s);
721     s = amname_to_fstype(name);
722     printf("fstype of `%s': %s\n", name, s);
723     amfree(s);
724
725     name = newstralloc(name, "./foo");
726     s = amname_to_devname(name);
727     printf("device of `%s': %s\n", name, s);
728     amfree(s);
729     s = amname_to_dirname(name);
730     printf("dirname of `%s': %s\n", name, s);
731     amfree(s);
732     s = amname_to_fstype(name);
733     printf("fstype of `%s': %s\n", name, s);
734     amfree(s);
735
736     while (--argc > 0) {
737         name = newstralloc(name, *++argv);
738         s = amname_to_devname(name);
739         printf("device of `%s': %s\n", name, s);
740         amfree(s);
741         s = amname_to_dirname(name);
742         printf("dirname of `%s': %s\n", name, s);
743         amfree(s);
744         s = amname_to_fstype(name);
745         printf("fstype of `%s': %s\n", name, s);
746         amfree(s);
747     }
748
749     amfree(name);
750
751     malloc_size_2 = malloc_inuse(&malloc_hist_2);
752
753     if(malloc_size_1 != malloc_size_2) {
754         malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2);
755     }
756
757     return 0;
758 }
759
760 #endif