Imported Upstream version 2.5.1
[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 #define sc "hs"
318     if (strncmp(fsent->fstype, sc, SIZEOF(sc)-1) == 0)
319         fsent->fstype = "iso9660";
320 #undef sc
321
322     fsent->freq = 0;
323     fsent->passno = 0;
324
325     return 1;
326 }
327
328 #    else
329 #      if defined(HAVE_MNTTAB_H) /* } { */
330
331 #define GETFSENT_TYPE "SVR3 (SCO UNIX)"
332
333 #include <mnttab.h>
334 #include <sys/fstyp.h>
335 #include <sys/statfs.h>
336
337 #define MNTTAB "/etc/mnttab"
338
339 /*
340  * If these are defined somewhere please let me know.
341  */
342
343 #define MNT_READONLY 0101
344 #define MNT_READWRITE 0100
345
346 static FILE *fstabf = NULL;
347
348 int
349 open_fstab(void)
350 {
351     close_fstab();
352     return (fstabf = fopen(MNTTAB, "r")) != NULL;
353 }
354
355 void
356 close_fstab(void)
357 {
358     if(fstabf)
359         afclose(fstabf);
360     fstabf = NULL;
361 }
362
363 static generic_fsent_t _fsent;
364
365 int
366 get_fstab_nextentry(
367     generic_fsent_t *fsent)
368 {
369     struct statfs fsd;
370     char typebuf[FSTYPSZ];
371     static struct mnttab mnt;
372     char *dp, *ep;
373
374     if(!fread (&mnt, SIZEOF(mnt), 1, fstabf))
375       return 0;
376
377     fsent->fsname  = mnt.mt_dev;
378     fsent->mntdir  = mnt.mt_filsys;
379     fsent->fstype = "";
380
381     if (statfs (fsent->mntdir, &fsd, SIZEOF(fsd), 0) != -1
382         && sysfs (GETFSTYP, fsd.f_fstyp, typebuf) != -1) {
383        dp = typebuf;
384        ep = fsent->fstype = malloc(strlen(typebuf)+2);
385        while (*dp)
386             *ep++ = tolower(*dp++);
387        *ep=0;
388     }
389
390     if ( mnt.mt_ro_flg == MNT_READONLY ) {
391         fsent->mntopts = "ro";
392     } else {
393         fsent->mntopts = "rw";
394     }
395
396     fsent->freq = 0;
397     fsent->passno = 0;
398     return 1;
399 }
400
401 #      else /* } { */
402
403 #define GETFSENT_TYPE "undefined"
404
405 #      endif
406 #    endif
407 #  endif
408 #endif
409 #endif /* } */
410
411 /*
412  *=====================================================================
413  * Convert either a block or character device name to a character (raw)
414  * device name.
415  *
416  * static char *dev2rdev(const char *name);
417  *
418  * entry:       name - device name to convert
419  * exit:        matching character device name if found,
420  *              otherwise returns the input
421  *
422  * The input must be an absolute path.
423  *
424  * The exit string area is always an alloc-d area that the caller is
425  * responsible for releasing.
426  *=====================================================================
427  */
428
429 static char *
430 dev2rdev(
431     char *      name)
432 {
433   char *fname = NULL;
434   struct stat st;
435   char *s;
436   int ch;
437
438   if(stat(name, &st) == 0 && S_ISCHR(st.st_mode)) {
439     /*
440      * If the input is already a character device, just return it.
441      */
442     return stralloc(name);
443   }
444
445   s = name;
446   ch = *s++;
447
448   if(ch == '\0' || ch != '/') return stralloc(name);
449
450   ch = *s++;                                    /* start after first '/' */
451   /*
452    * Break the input path at each '/' and create a new name with an
453    * 'r' before the right part.  For instance:
454    *
455    *   /dev/sd0a -> /dev/rsd0a
456    *   /dev/dsk/c0t0d0s0 -> /dev/rdsk/c0t0d0s0 -> /dev/dsk/rc0t0d0s0
457    */
458   while(ch) {
459     if (ch == '/') {
460       s[-1] = '\0';
461       fname = newvstralloc(fname, name, "/r", s, NULL);
462       s[-1] = (char)ch;
463       if(stat(fname, &st) == 0 && S_ISCHR(st.st_mode)) return fname;
464     }
465     ch = *s++;
466   }
467   amfree(fname);
468   return stralloc(name);                        /* no match */
469 }
470
471 #ifndef IGNORE_FSTAB
472 static int samefile(struct stat[3], struct stat *);
473
474 static int
475 samefile(
476     struct stat stats[3],
477     struct stat *estat)
478 {
479   int i;
480   for(i = 0; i < 3; ++i) {
481     if (stats[i].st_dev == estat->st_dev &&
482         stats[i].st_ino == estat->st_ino)
483       return 1;
484   }
485   return 0;
486 }
487 #endif /* !IGNORE_FSTAB */
488
489 int
490 search_fstab(
491      char *             name,
492      generic_fsent_t *  fsent,
493      int                check_dev)
494 {
495 #ifdef IGNORE_FSTAB
496   /* There is no real mount table so this will always fail and
497    * we are using GNU tar so we can just return here.
498    */
499   (void)name;           /* Quiet unused parameter warning */
500   (void)fsent;          /* Quiet unused parameter warning */
501   (void)check_dev;      /* Quiet unused parameter warning */
502   return 0;
503 #else
504   struct stat stats[3];
505   char *fullname = NULL;
506   char *rdev = NULL;
507   int rc;
508
509   if (!name)
510     return 0;
511
512   memset(stats, 0, SIZEOF(stats));
513   stats[0].st_dev = stats[1].st_dev = stats[2].st_dev = (dev_t)-1;
514
515   if (stat(name, &stats[0]) == -1)
516     stats[0].st_dev = (dev_t)-1;
517   if (name[0] != '/') {
518     fullname = stralloc2(DEV_PREFIX, name);
519     if (stat(fullname, &stats[1]) == -1)
520       stats[1].st_dev = (dev_t)-1;
521     fullname = newstralloc2(fullname, RDEV_PREFIX, name);
522     if (stat(fullname, &stats[2]) == -1)
523       stats[2].st_dev = (dev_t)-1;
524     amfree(fullname);
525   }
526   else if (stat((rdev = dev2rdev(name)), &stats[1]) == -1)
527     stats[1].st_dev = (dev_t)-1;
528
529   amfree(rdev);
530
531   if (!open_fstab())
532     return 0;
533
534   rc = 0;
535   while(get_fstab_nextentry(fsent)) {
536     struct stat mntstat;
537     struct stat fsstat;
538     struct stat fsrstat;
539     int smnt = -1, sfs = -1, sfsr = -1;
540
541     amfree(rdev);
542
543     if(fsent->mntdir != NULL &&
544        (smnt = stat(fsent->mntdir, &mntstat)) == -1)
545       continue;
546
547     if(fsent->fsname != NULL) {
548       sfs = stat(fsent->fsname, &fsstat);
549       sfsr = stat((rdev = dev2rdev(fsent->fsname)), &fsrstat);
550       if(check_dev == 1 && sfs == -1 && sfsr == -1)
551         continue;
552     }
553
554     if((fsent->mntdir != NULL &&
555         smnt != -1 &&
556         samefile(stats, &mntstat)) || 
557        (fsent->fsname != NULL &&
558         sfs != -1 &&
559         samefile(stats, &fsstat)) ||
560        (fsent->fsname != NULL &&
561         sfsr != -1 &&
562         samefile(stats, &fsrstat))) {
563       rc = 1;
564       break;
565     }
566   }
567   amfree(rdev);
568   close_fstab();
569   return rc;
570 #endif /* !IGNORE_FSTAB */
571 }
572
573 int
574 is_local_fstype(
575     generic_fsent_t *   fsent)
576 {
577     if(fsent->fstype == NULL)   /* unknown, assume local */
578         return 1;
579
580     /* just eliminate fstypes known to be remote or unsavable */
581
582     return strcmp(fsent->fstype, "nfs") != 0 && /* NFS */
583            strcmp(fsent->fstype, "afs") != 0 && /* Andrew Filesystem */
584            strcmp(fsent->fstype, "swap") != 0 && /* Swap */
585            strcmp(fsent->fstype, "iso9660") != 0 && /* CDROM */
586            strcmp(fsent->fstype, "hs") != 0 && /* CDROM */
587            strcmp(fsent->fstype, "piofs") != 0; /* an AIX printer thing? */
588 }
589
590
591 char *
592 amname_to_devname(
593     char *      str)
594 {
595     generic_fsent_t fsent;
596
597     if(search_fstab(str, &fsent, 1) && fsent.fsname != NULL)
598         str = fsent.fsname;
599     else if(search_fstab(str, &fsent, 0) && fsent.fsname != NULL)
600         str = fsent.fsname;
601
602     return dev2rdev(str);
603 }
604
605 char *
606 amname_to_dirname(
607     char *      str)
608 {
609     generic_fsent_t fsent;
610
611     if(search_fstab(str, &fsent, 1) && fsent.mntdir != NULL)
612         str = fsent.mntdir;
613     else if(search_fstab(str, &fsent, 0) && fsent.mntdir != NULL)
614         str = fsent.mntdir;
615
616     return stralloc(str);
617 }
618
619 char *amname_to_fstype(
620     char *      str)
621 {
622     generic_fsent_t fsent;
623
624     if (!search_fstab(str, &fsent, 1) && !search_fstab(str, &fsent, 0))
625       return stralloc("");
626
627     return stralloc(fsent.fstype);
628 }
629
630 #ifdef TEST
631
632 void print_entry(generic_fsent_t *fsent);
633
634 void
635 print_entry(
636     generic_fsent_t *   fsent)
637 {
638 #define nchk(s) ((s)? (s) : "<NULL>")
639     printf("%-20.20s %-14.14s %-7.7s %4d %5d %s\n",
640            nchk(fsent->fsname), nchk(fsent->mntdir), nchk(fsent->fstype),
641            fsent->freq, fsent->passno, nchk(fsent->mntopts));
642 }
643
644 int
645 main(
646     int         argc,
647     char **     argv)
648 {
649     generic_fsent_t fsent;
650     char *s;
651     char *name = NULL;
652     unsigned long malloc_hist_1, malloc_size_1;
653     unsigned long malloc_hist_2, malloc_size_2;
654
655     safe_fd(-1, 0);
656
657     set_pname("getfsent");
658
659     dbopen(NULL);
660
661     /* Don't die when child closes pipe */
662     signal(SIGPIPE, SIG_IGN);
663
664     malloc_size_1 = malloc_inuse(&malloc_hist_1);
665
666     if(!open_fstab()) {
667         fprintf(stderr, "getfsent_test: could not open fstab\n");
668         return 1;
669     }
670
671     printf("getfsent (%s)\n",GETFSENT_TYPE);
672     printf("l/r fsname               mntdir         fstype  freq pass# mntopts\n");
673     while(get_fstab_nextentry(&fsent)) {
674         printf("%c  ",is_local_fstype(&fsent)? 'l' : 'r');
675         print_entry(&fsent);
676     }
677     printf("--------\n");
678
679     close_fstab();
680
681     name = newstralloc(name, "/usr");
682     if(search_fstab(name, &fsent, 1) || search_fstab(name, &fsent, 0)) {
683         printf("Found %s mount for %s:\n",
684                is_local_fstype(&fsent)? "local" : "remote", name);
685         print_entry(&fsent);
686     }
687     else 
688         printf("Mount for %s not found\n", name);
689
690     name = newstralloc(name, "/");
691     if(search_fstab(name, &fsent, 1) || search_fstab(name, &fsent, 0)) {
692         printf("Found %s mount for %s:\n",
693                is_local_fstype(&fsent)? "local" : "remote", name);
694         print_entry(&fsent);
695     }
696     else 
697         printf("Mount for %s not found\n", name);
698
699     name = newstralloc(name, "/");
700     s = amname_to_fstype(name);
701     printf("fstype of `%s': %s\n", name, s);
702     amfree(s);
703     name = newstralloc(name, "/dev/root");
704     s = amname_to_fstype(name);
705     printf("fstype of `%s': %s\n", name, s);
706     amfree(s);
707     name = newstralloc(name, "/usr");
708     s = amname_to_fstype(name);
709     printf("fstype of `%s': %s\n", name, s);
710     amfree(s);
711     name = newstralloc(name, "c0t3d0s0");
712     s = amname_to_fstype(name);
713     printf("fstype of `%s': %s\n", name, s);
714     amfree(s);
715
716     name = newstralloc(name, "/tmp/foo");
717     s = amname_to_devname(name);
718     printf("device of `%s': %s\n", name, s);
719     amfree(s);
720     s = amname_to_dirname(name);
721     printf("dirname of `%s': %s\n", name, s);
722     amfree(s);
723     s = amname_to_fstype(name);
724     printf("fstype of `%s': %s\n", name, s);
725     amfree(s);
726
727     name = newstralloc(name, "./foo");
728     s = amname_to_devname(name);
729     printf("device of `%s': %s\n", name, s);
730     amfree(s);
731     s = amname_to_dirname(name);
732     printf("dirname of `%s': %s\n", name, s);
733     amfree(s);
734     s = amname_to_fstype(name);
735     printf("fstype of `%s': %s\n", name, s);
736     amfree(s);
737
738     while (--argc > 0) {
739         name = newstralloc(name, *++argv);
740         s = amname_to_devname(name);
741         printf("device of `%s': %s\n", name, s);
742         amfree(s);
743         s = amname_to_dirname(name);
744         printf("dirname of `%s': %s\n", name, s);
745         amfree(s);
746         s = amname_to_fstype(name);
747         printf("fstype of `%s': %s\n", name, s);
748         amfree(s);
749     }
750
751     amfree(name);
752
753     malloc_size_2 = malloc_inuse(&malloc_hist_2);
754
755     if(malloc_size_1 != malloc_size_2) {
756         malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2);
757     }
758
759     return 0;
760 }
761
762 #endif