62cf171bab4da3dc2c53ce53c4a3dece1ddabc62
[debian/amanda] / common-src / file.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1997-1998 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  * Author: AMANDA core development group.
24  */
25 /*
26  * $Id: file.c,v 1.40.2.1 2006/11/24 18:05:05 martinea Exp $
27  *
28  * file and directory bashing routines
29  */
30
31 #include "amanda.h"
32 #include "util.h"
33
34 void amanda_setup(int argc, char **argv, int setup_flags);
35 static int mk1dir(const char *, mode_t, uid_t, gid_t);
36 static void areads_getbuf(const char *s, int l, int fd);
37
38 uid_t client_uid = (uid_t) -1;
39 gid_t client_gid = (gid_t) -1;
40
41 /* Make a directory (internal function).
42 ** If the directory already exists then we pretend we created it.
43 ** XXX - I'm not sure about the use of the chown() stuff.  On most systems
44 **       it will do nothing - only root is permitted to change the owner
45 **       of a file.
46 */
47 static int
48 mk1dir(
49     const char *dir, /* directory to create */
50     mode_t      mode,   /* mode for new directory */
51     uid_t       uid,    /* uid for new directory */
52     gid_t       gid)    /* gid for new directory */
53 {
54     int rc;     /* return code */
55
56     if((rc = mkdir(dir, mode)) == 0) {
57         if ((rc = chmod(dir, mode)) == 0) { /* mkdir() affected by the umask */
58             rc = chown(dir, uid, gid);
59         }
60     } else {                    /* maybe someone beat us to it */
61         int serrno;
62
63         serrno = errno;
64         if(access(dir, F_OK) != 0)
65             rc = -1;
66         errno = serrno; /* pass back the real error */
67     }
68
69     return rc;
70 }
71
72
73 /*
74  * Make a directory hierarchy given an entry to be created (by the caller)
75  * in the new target.  In other words, create all the directories down to
76  * the last element, but not the last element.  So a (potential) file name
77  * may be passed to mkpdir and all the parents of that file will be created.
78  */
79 int
80 mkpdir(
81     char *      file,   /* file to create parent directories for */
82     mode_t      mode,   /* mode for new directories */
83     uid_t       uid,    /* uid for new directories */
84     gid_t       gid)    /* gid for new directories */
85 {
86     char *dir;
87     char *p;
88     int rc;     /* return code */
89
90     rc = 0;
91
92     dir = stralloc(file);       /* make a copy we can play with */
93     p = strrchr(dir, '/');
94     if(p != dir && p != NULL) { /* got a '/' or a simple name */
95         *p = '\0';
96
97         if(access(dir, F_OK) != 0) {    /* doesn't exist */
98             if(mkpdir(dir, mode, uid, gid) != 0 ||
99                mk1dir(dir, mode, uid, gid) != 0) rc = -1; /* create failed */
100         }
101     }
102
103     amfree(dir);
104     return rc;
105 }
106
107
108 /* Remove as much of a directory hierarchy as possible.
109 ** Notes:
110 **  - assumes that rmdir() on a non-empty directory will fail!
111 **  - stops deleting before topdir, ie: topdir will not be removed
112 **  - if file is not under topdir this routine will not notice
113 */
114 int
115 rmpdir(
116     char *      file,   /* directory hierarchy to remove */
117     char *      topdir) /* where to stop removing */
118 {
119     int rc;
120     char *p, *dir;
121
122     if(strcmp(file, topdir) == 0) return 0; /* all done */
123
124     rc = rmdir(file);
125     if (rc != 0) switch(errno) {
126 #ifdef ENOTEMPTY
127 #if ENOTEMPTY != EEXIST                 /* AIX makes these the same */
128         case ENOTEMPTY:
129 #endif
130 #endif
131         case EEXIST:    /* directory not empty */
132             return 0; /* cant do much more */
133         case ENOENT:    /* it has already gone */
134             rc = 0; /* ignore */
135             break;
136         case ENOTDIR:   /* it was a file */
137             rc = unlink(file);
138             break;
139         }
140
141     if(rc != 0) return -1; /* unexpected error */
142
143     dir = stralloc(file);
144
145     p = strrchr(dir, '/');
146     if (p == NULL || p == dir) {
147         rc = 0;
148     } else {
149         *p = '\0';
150         rc = rmpdir(dir, topdir);
151     }
152
153     amfree(dir);
154
155     return rc;
156 }
157
158
159 /*
160  *=====================================================================
161  * Do Amanda setup for all programs.
162  *
163  * void amanda_setup (int argc, char **argv, int setup_flags)
164  *
165  * entry:       setup_flags (see AMANDA_SETUP_FLAG_xxx)
166  * exit:        none
167  *=====================================================================
168  */
169
170 void
171 amanda_setup (
172     int         argc,
173     char **     argv,
174     int         setup_flags)
175 {
176     (void)argc;         /* Quiet unused parameter warning */
177     (void)argv;         /* Quiet unused parameter warning */
178     (void)setup_flags;  /* Quiet unused parameter warning */
179 }
180
181 /*
182  *=====================================================================
183  * Change directory to a "safe" location and set some base environment.
184  *
185  * void safe_cd (void)
186  *
187  * entry:       client_uid and client_gid set to CLIENT_LOGIN information
188  * exit:        none
189  *
190  * Set a default umask of 0077.
191  *
192  * Create the Amada debug directory (if defined) and the Amanda temp
193  * directory.
194  *
195  * Try to chdir to the Amanda debug directory first, but it must be owned
196  * by the Amanda user and not allow rwx to group or other.  Otherwise,
197  * try the same thing to the Amanda temp directory.
198  *
199  * If that is all OK, call save_core().
200  *
201  * Otherwise, cd to "/" so if we take a signal we cannot drop core
202  * unless the system administrator has made special arrangements (e.g.
203  * pre-created a core file with the right ownership and permissions).
204  *=====================================================================
205  */
206
207 void
208 safe_cd(void)
209 {
210     int                 cd_ok = 0;
211     struct stat         sbuf;
212     struct passwd       *pwent;
213     char                *d;
214
215     if(client_uid == (uid_t) -1 && (pwent = getpwnam(CLIENT_LOGIN)) != NULL) {
216         client_uid = pwent->pw_uid;
217         client_gid = pwent->pw_gid;
218         endpwent();
219     }
220
221     (void) umask(0077);
222
223     if (client_uid != (uid_t) -1) {
224 #if defined(AMANDA_DBGDIR)
225         d = stralloc2(AMANDA_DBGDIR, "/.");
226         (void) mkpdir(d, (mode_t)02700, client_uid, client_gid);
227         amfree(d);
228 #endif
229         d = stralloc2(AMANDA_TMPDIR, "/.");
230         (void) mkpdir(d, (mode_t)02700, client_uid, client_gid);
231         amfree(d);
232     }
233
234 #if defined(AMANDA_DBGDIR)
235     if (chdir(AMANDA_DBGDIR) != -1
236         && stat(".", &sbuf) != -1
237         && (sbuf.st_mode & 0777) == 0700        /* drwx------ */
238         && sbuf.st_uid == client_uid) {         /* owned by Amanda user */
239         cd_ok = 1;                              /* this is a good place to be */
240     }
241 #endif
242     if (! cd_ok
243         && chdir(AMANDA_TMPDIR) != -1
244         && stat(".", &sbuf) != -1
245         && (sbuf.st_mode & 0777) == 0700        /* drwx------ */
246         && sbuf.st_uid == client_uid) {         /* owned by Amanda user */
247         cd_ok = 1;                              /* this is a good place to be */
248     }
249     if(cd_ok) {
250         save_core();                            /* save any old core file */
251     } else {
252         if ((cd_ok = chdir("/")) == -1) {
253             (void)cd_ok;        /* Quiet compiler warning if DEBUG disabled */
254         }
255     }
256 }
257
258 /*
259  *=====================================================================
260  * Close all file descriptors except stdin, stdout and stderr.  Make
261  * sure they are open.
262  *
263  * void safe_fd (fd_start, fd_count)
264  *
265  * entry:       fd_start - start of fd-s to leave alone (or -1)
266  *              fd_count - count of fd-s to leave alone
267  * exit:        none
268  *
269  * On exit, all three standard file descriptors will be open and pointing
270  * someplace (either what we were handed or /dev/null) and all other
271  * file descriptors (up to FD_SETSIZE) will be closed.
272  *=====================================================================
273  */
274
275 void
276 safe_fd(
277     int         fd_start,
278     int         fd_count)
279 {
280     int                 fd;
281
282     for(fd = 0; fd < FD_SETSIZE; fd++) {
283         if (fd < 3) {
284             /*
285              * Open three file descriptors.  If one of the standard
286              * descriptors is not open it will be pointed to /dev/null...
287              *
288              * This avoids, for instance, someone running us with stderr
289              * closed so that when we open some other file, messages
290              * sent to stderr do not accidentally get written to the
291              * wrong file.
292              */
293             if (fcntl(fd, F_GETFD) == -1) {
294                 if (open("/dev/null", O_RDWR) == -1) {
295                    fprintf(stderr, "/dev/null is inaccessable: %s\n",
296                            strerror(errno));
297                    exit(1);
298                 }
299             }
300         } else {
301             /*
302              * Make sure nobody spoofs us with a lot of extra open files
303              * that would cause an open we do to get a very high file
304              * descriptor, which in turn might be used as an index into
305              * an array (e.g. an fd_set).
306              */
307             if (fd < fd_start || fd >= fd_start + fd_count) {
308                 close(fd);
309             }
310         }
311     }
312 }
313
314 /*
315  *=====================================================================
316  * Save an existing core file.
317  *
318  * void save_core (void)
319  *
320  * entry:       none
321  * exit:        none
322  *
323  * Renames:
324  *
325  *      "core"          to "coreYYYYMMDD",
326  *      "coreYYYYMMDD"  to "coreYYYYMMDDa",
327  *      "coreYYYYMMDDa" to "coreYYYYMMDDb",
328  *      ...
329  *
330  * ... where YYYYMMDD is the modification time of the original file.
331  * If it gets that far, an old "coreYYYYMMDDz" is thrown away.
332  *=====================================================================
333  */
334
335 void
336 save_core(void)
337 {
338     struct stat sbuf;
339
340     if(stat("core", &sbuf) != -1) {
341         char *ts;
342         char suffix[2];
343         char *old, *new;
344
345         ts = construct_datestamp((time_t *)&sbuf.st_mtime);
346         suffix[0] = 'z';
347         suffix[1] = '\0';
348         old = vstralloc("core", ts, suffix, NULL);
349         new = NULL;
350         while(ts[0] != '\0') {
351             amfree(new);
352             new = old;
353             if(suffix[0] == 'a') {
354                 suffix[0] = '\0';
355             } else if(suffix[0] == '\0') {
356                 ts[0] = '\0';
357             } else {
358                 suffix[0]--;
359             }
360             old = vstralloc("core", ts, suffix, NULL);
361             (void)rename(old, new);         /* it either works ... */
362         }
363         amfree(ts);
364         amfree(old);
365         amfree(new);
366     }
367 }
368
369 /*
370 ** Sanitise a file name.
371 ** 
372 ** Convert all '/' characters to '_' so that we can use,
373 ** for example, disk names as part of file names.
374 ** Notes: 
375 **  - there is a many-to-one mapping between input and output
376 **  - Only / and '\0' are disallowed in filenames by POSIX...
377 */
378 char *
379 sanitise_filename(
380     char *      inp)
381 {
382     char *buf;
383     size_t buf_size;
384     char *s, *d;
385     int ch;
386
387     buf_size = strlen(inp) + 1;         /* worst case */
388     buf = alloc(buf_size);
389     d = buf;
390     s = inp;
391     while((ch = *s++) != '\0') {
392         if(ch == '/') {
393             ch = '_';   /* convert "bad" to "_" */
394         }
395         *d++ = (char)ch;
396     }
397     assert(d < buf + buf_size);
398     *d = '\0';
399
400     return buf;
401 }
402
403 /* duplicate '_' */
404 char *
405 old_sanitise_filename(
406     char *      inp)
407 {
408     char *buf;
409     size_t buf_size;
410     char *s, *d;
411     int ch;
412
413     buf_size = 2*strlen(inp) + 1;               /* worst case */
414     buf = alloc(buf_size);
415     d = buf;
416     s = inp;
417     while((ch = *s++) != '\0') {
418         if(ch == '_') {
419             *d++ = (char)ch;
420         }
421         if(ch == '/') {
422             ch = '_';   /* convert "bad" to "_" */
423         }
424         *d++ = (char)ch;
425     }
426     assert(d < buf + buf_size);
427     *d = '\0';
428
429     return buf;
430 }
431
432 /*
433  *=====================================================================
434  * Get the next line of input from a stdio file.
435  *
436  * char *agets (FILE *stream)
437  *
438  * entry:       stream  -  stream to read
439  * exit:        returns a pointer to an alloc'd string or NULL
440  *              at EOF or error.  The functions ferror(stream) and
441  *              feof(stream) should be checked by caller to determine
442  *              stream status.
443  *
444  * Notes:       the newline at the end of a line, if read, is removed from
445  *              the string. Quoted newlines are left intact.
446  *              the caller is responsible for free'ing the string
447  *
448  *=====================================================================
449  */
450
451 #define AGETS_LINE_INCR 128
452
453 char *
454 debug_agets(
455     const char *sourcefile,
456     int         lineno,
457     FILE *      stream)
458 {
459     int ch;
460     char *line = alloc(AGETS_LINE_INCR);
461     size_t line_size = 0;
462     size_t loffset = 0;
463     int inquote = 0;
464     int escape = 0;
465
466     (void)sourcefile;   /* Quiet unused parameter warning if not debugging */
467     (void)lineno;       /* Quiet unused parameter warning if not debugging */
468
469     while ((ch = fgetc(stream)) != EOF) {
470         if (ch == '\n') {
471             if (!inquote) {
472                 if (escape) {
473                     escape = 0;
474                     loffset--;  /* Consume escape in buffer */
475                     continue;
476                 }
477                 /* Reached end of line so exit without passing on LF */
478                 break;
479             }
480         }
481
482         if (ch == '\\') {
483             escape = 1;
484         } else {
485             if (ch == '"') {
486                 if (!escape) 
487                     inquote = !inquote;
488             }
489             escape = 0;
490         }
491
492         if ((loffset + 1) >= line_size) {
493             char *tmpline;
494
495             /*
496              * Reallocate input line.
497              * alloc() never return NULL pointer.
498              */
499             tmpline = alloc(line_size + AGETS_LINE_INCR);
500             memcpy(tmpline, line, line_size);
501             amfree(line);
502             line = tmpline;
503             line_size = line_size + AGETS_LINE_INCR;
504         }
505         line[loffset++] = (char)ch;
506     }
507
508     if ((ch == EOF) && (loffset == 0)) {
509         amfree(line); /* amfree zeros line... */
510     } else {
511         line[loffset] = '\0';
512     }
513
514     /*
515      * Return what we got even if there was not a newline.
516      * Only report done (NULL) when no data was processed.
517      */
518     return line;
519 }
520
521
522 /*
523  *=====================================================================
524  * Find/create a buffer for a particular file descriptor for use with
525  * areads().
526  *
527  * void areads_getbuf (const char *file, size_t line, int fd)
528  *
529  * entry:       file, line = caller source location
530  *              fd = file descriptor to look up
531  * exit:        returns a pointer to the buffer, possibly new
532  *=====================================================================
533  */
534
535 static struct areads_buffer {
536     char *buffer;
537     char *endptr;
538     size_t bufsize;
539 } *areads_buffer = NULL;
540 static int areads_bufcount = 0;
541 static size_t areads_bufsize = BUFSIZ;          /* for the test program */
542
543 static void
544 areads_getbuf(
545     const char *s,
546     int         l,
547     int         fd)
548 {
549     struct areads_buffer *new;
550     size_t size;
551
552     assert(fd >= 0);
553     if(fd >= areads_bufcount) {
554         size = (size_t)(fd + 1) * SIZEOF(*areads_buffer);
555         new = (struct areads_buffer *) debug_alloc(s, l, size);
556         memset((char *)new, 0, size);
557         if(areads_buffer) {
558             size = areads_bufcount * SIZEOF(*areads_buffer);
559             memcpy(new, areads_buffer, size);
560         }
561         amfree(areads_buffer);
562         areads_buffer = new;
563         areads_bufcount = fd + 1;
564     }
565     if(areads_buffer[fd].buffer == NULL) {
566         areads_buffer[fd].bufsize = areads_bufsize;
567         areads_buffer[fd].buffer = debug_alloc(s, l,
568                                                areads_buffer[fd].bufsize + 1);
569         areads_buffer[fd].buffer[0] = '\0';
570         areads_buffer[fd].endptr = areads_buffer[fd].buffer;
571     }
572 }
573
574 /*
575  *=====================================================================
576  * Return the amount of data still in an areads buffer.
577  *
578  * ssize_t areads_dataready (int fd)
579  *
580  * entry:       fd = file descriptor to release buffer for
581  * exit:        returns number of bytes of data ready to process
582  *=====================================================================
583  */
584
585 ssize_t
586 areads_dataready(
587     int fd)
588 {
589     ssize_t r = 0;
590
591     if(fd >= 0 && fd < areads_bufcount && areads_buffer[fd].buffer != NULL) {
592         r = (ssize_t) (areads_buffer[fd].endptr - areads_buffer[fd].buffer);
593     }
594     return r;
595 }
596
597 /*
598  *=====================================================================
599  * Release a buffer for a particular file descriptor used by areads().
600  *
601  * void areads_relbuf (int fd)
602  *
603  * entry:       fd = file descriptor to release buffer for
604  * exit:        none
605  *=====================================================================
606  */
607
608 void
609 areads_relbuf(
610     int fd)
611 {
612     if(fd >= 0 && fd < areads_bufcount) {
613         amfree(areads_buffer[fd].buffer);
614         areads_buffer[fd].endptr = NULL;
615         areads_buffer[fd].bufsize = 0;
616     }
617 }
618
619 /*
620  *=====================================================================
621  * Get the next line of input from a file descriptor.
622  *
623  * char *areads (int fd)
624  *
625  * entry:       fd = file descriptor to read
626  * exit:        returns a pointer to an alloc'd string or NULL at EOF
627  *              or error (errno will be zero on EOF).
628  *
629  * Notes:       the newline, if read, is removed from the string
630  *              the caller is responsible for free'ing the string
631  *=====================================================================
632  */
633
634 char *
635 debug_areads (
636     const char *s,
637     int         l,
638     int         fd)
639 {
640     char *nl;
641     char *line;
642     char *buffer;
643     char *endptr;
644     char *newbuf;
645     size_t buflen;
646     size_t size;
647     ssize_t r;
648
649     malloc_enter(dbmalloc_caller_loc(s, l));
650
651     if(fd < 0) {
652         errno = EBADF;
653         return NULL;
654     }
655     areads_getbuf(s, l, fd);
656     buffer = areads_buffer[fd].buffer;
657     endptr = areads_buffer[fd].endptr;
658     buflen = areads_buffer[fd].bufsize - (size_t)(endptr - buffer);
659     while((nl = strchr(buffer, '\n')) == NULL) {
660         /*
661          * No newline yet, so get more data.
662          */
663         if (buflen == 0) {
664             if ((size = areads_buffer[fd].bufsize) < 256 * areads_bufsize) {
665                 size *= 2;
666             } else {
667                 size += 256 * areads_bufsize;
668             }
669             newbuf = debug_alloc(s, l, size + 1);
670             memcpy (newbuf, buffer, areads_buffer[fd].bufsize + 1);
671             amfree(areads_buffer[fd].buffer);
672             areads_buffer[fd].buffer = newbuf;
673             areads_buffer[fd].endptr = newbuf + areads_buffer[fd].bufsize;
674             areads_buffer[fd].bufsize = size;
675             buffer = areads_buffer[fd].buffer;
676             endptr = areads_buffer[fd].endptr;
677             buflen = areads_buffer[fd].bufsize - (size_t)(endptr - buffer);
678         }
679         if ((r = read(fd, endptr, buflen)) <= 0) {
680             if(r == 0) {
681                 errno = 0;              /* flag EOF instead of error */
682             }
683             malloc_leave(dbmalloc_caller_loc(s, l));
684             return NULL;
685         }
686         endptr[r] = '\0';               /* we always leave room for this */
687         endptr += r;
688         buflen -= r;
689     }
690     *nl++ = '\0';
691     line = stralloc(buffer);
692     size = (size_t)(endptr - nl);       /* data still left in buffer */
693     memmove(buffer, nl, size);
694     areads_buffer[fd].endptr = buffer + size;
695     areads_buffer[fd].endptr[0] = '\0';
696     malloc_leave(dbmalloc_caller_loc(s, l));
697     return line;
698 }
699
700 #ifdef TEST
701
702 int
703 main(
704     int         argc,
705     char **     argv)
706 {
707         int rc;
708         int fd;
709         char *name;
710         char *top;
711         char *file;
712         char *line;
713
714         safe_fd(-1, 0);
715
716         set_pname("file test");
717
718         dbopen(NULL);
719
720         /* Don't die when child closes pipe */
721         signal(SIGPIPE, SIG_IGN);
722
723         name = "/tmp/a/b/c/d/e";
724         if (argc > 2 && argv[1][0] != '\0') {
725                 name = argv[1];
726         }
727         top = "/tmp";
728         if (argc > 3 && argv[2][0] != '\0') {
729                 name = argv[2];
730         }
731         file = "/etc/hosts";
732         if (argc > 4 && argv[3][0] != '\0') {
733                 name = argv[3];
734         }
735
736         fprintf(stderr, "Create parent directories of %s ...", name);
737         rc = mkpdir(name, (mode_t)02777, (uid_t)-1, (gid_t)-1);
738         if (rc == 0)
739                 fprintf(stderr, " done\n");
740         else {
741                 perror("failed");
742                 return rc;
743         }
744
745         fprintf(stderr, "Delete %s back to %s ...", name, top);
746         rc = rmpdir(name, top);
747         if (rc == 0)
748                 fprintf(stderr, " done\n");
749         else {
750                 perror("failed");
751                 return rc;
752         }
753
754         fprintf(stderr, "areads dump of %s ...", file);
755         if ((fd = open (file, 0)) < 0) {
756                 perror(file);
757                 return 1;
758         }
759         areads_bufsize = 1;                     /* force buffer overflow */
760         while ((line = areads(fd)) != NULL) {
761                 puts(line);
762                 amfree(line);
763         }
764         aclose(fd);
765         fprintf(stderr, " done.\n");
766
767         fprintf(stderr, "Finished.\n");
768         return 0;
769 }
770
771 #endif