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