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