2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1997-1998 University of Maryland at College Park
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.
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.
23 * Author: AMANDA core development group.
26 * $Id: file.c,v 1.35 2006/03/09 16:51:41 martinea Exp $
28 * file and directory bashing routines
34 static int mk1dir P((const char *, int, uid_t, gid_t));
36 uid_t client_uid = (uid_t) -1;
37 gid_t client_gid = (gid_t) -1;
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
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 */
51 int rc; /* return code */
53 rc = 0; /* assume the best */
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 */
62 if(access(dir, F_OK) != 0) rc = -1;
63 errno = serrno; /* pass back the real error */
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.
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 */
83 int rc; /* return code */
87 dir = stralloc(file); /* make a copy we can play with */
89 p = strrchr(dir, '/');
90 if(p != dir && p != NULL) { /* got a '/' or a simple name */
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 */
104 /* Remove as much of a directory hierarchy as possible.
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
110 int rmpdir(file, topdir)
111 char *file; /* directory hierarchy to remove */
112 char *topdir; /* where to stop removing */
115 char *p, *dir = NULL;
117 if(strcmp(file, topdir) == 0) return 0; /* all done */
120 if (rc != 0) switch(errno) {
122 #if ENOTEMPTY != EEXIST /* AIX makes these the same */
126 case EEXIST: /* directory not empty */
127 return 0; /* cant do much more */
128 case ENOENT: /* it has already gone */
131 case ENOTDIR: /* it was a file */
136 if(rc != 0) return -1; /* unexpected error */
138 dir = stralloc(file);
140 p = strrchr(dir, '/');
141 if(p == dir) rc = 0; /* no /'s */
145 rc = rmpdir(dir, topdir);
155 *=====================================================================
156 * Do Amanda setup for all programs.
158 * void amanda_setup (int argc, char **argv, int setup_flags)
160 * entry: setup_flags (see AMANDA_SETUP_FLAG_xxx)
162 *=====================================================================
166 amanda_setup (argc, argv, setup_flags)
174 *=====================================================================
175 * Change directory to a "safe" location and set some base environment.
177 * void safe_cd (void)
179 * entry: client_uid and client_gid set to CLIENT_LOGIN information
182 * Set a default umask of 0077.
184 * Create the Amada debug directory (if defined) and the Amanda temp
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.
191 * If that is all OK, call save_core().
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 *=====================================================================
204 struct passwd *pwent;
207 if(client_uid == (uid_t) -1 && (pwent = getpwnam(CLIENT_LOGIN)) != NULL) {
208 client_uid = pwent->pw_uid;
209 client_gid = pwent->pw_gid;
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);
221 d = stralloc2(AMANDA_TMPDIR, "/.");
222 (void) mkpdir(d, 02700, client_uid, client_gid);
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 */
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 */
242 save_core(); /* save any old core file */
244 (void) chdir("/"); /* assume this works */
249 *=====================================================================
250 * Close all file descriptors except stdin, stdout and stderr. Make
251 * sure they are open.
253 * void safe_fd (fd_start, fd_count)
255 * entry: fd_start - start of fd-s to leave alone (or -1)
256 * fd_count - count of fd-s to leave alone
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 *=====================================================================
266 safe_fd(fd_start, fd_count)
272 for(fd = 0; fd < FD_SETSIZE; fd++) {
275 * Open three file descriptors. If one of the standard
276 * descriptors is not open it will be pointed to /dev/null...
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
283 if (fcntl(fd, F_GETFD) == -1) {
284 if (open("/dev/null", O_RDWR) == -1) {
285 fprintf(stderr, "/dev/null is inaccessable: %s\n",
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).
297 if (fd < fd_start || fd >= fd_start + fd_count) {
306 *=====================================================================
307 * Save an existing core file.
309 * void save_core (void)
316 * "core" to "coreYYYYMMDD",
317 * "coreYYYYMMDD" to "coreYYYYMMDDa",
318 * "coreYYYYMMDDa" to "coreYYYYMMDDb",
321 * ... where YYYYMMDD is the modification time of the original file.
322 * If it gets that far, an old "coreYYYYMMDDz" is thrown away.
323 *=====================================================================
331 if(stat("core", &sbuf) != -1) {
336 ts = construct_datestamp((time_t *)&sbuf.st_mtime);
339 old = vstralloc("core", ts, suffix, NULL);
341 while(ts[0] != '\0') {
344 if(suffix[0] == 'a') {
346 } else if(suffix[0] == '\0') {
351 old = vstralloc("core", ts, suffix, NULL);
352 (void)rename(old, new); /* it either works ... */
361 ** Sanitise a file name.
363 ** Convert all funny characters to '_' so that we can use,
364 ** for example, disk names as part of file names.
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.
370 char *sanitise_filename(inp)
378 buf_size = 2 * strlen(inp) + 1; /* worst case */
379 buf = alloc(buf_size);
382 while((ch = *s++) != '\0') {
384 if(d >= buf + buf_size) {
386 return NULL; /* cannot happen */
388 *d++ = '_'; /* convert _ to __ to try */
389 /* and ensure unique output */
390 } else if(ch == '/' || isspace(ch)) {
391 ch = '_'; /* convert "bad" to "_" */
393 if(d >= buf + buf_size) {
395 return NULL; /* cannot happen */
399 if(d >= buf + buf_size) {
401 return NULL; /* cannot happen */
409 *=====================================================================
410 * Get the next line of input from a stdio file.
412 * char *agets (FILE *f)
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).
418 * Notes: the newline, if read, is removed from the string
419 * the caller is responsible for free'ing the string
420 *=====================================================================
424 debug_agets(s, l, file)
429 char *line = NULL, *line_ptr;
430 size_t line_size, size_save;
431 int line_free, line_len;
435 malloc_enter(dbmalloc_caller_loc(s, l));
437 #define AGETS_LINE_INCR 128
439 line_size = AGETS_LINE_INCR;
440 line = debug_alloc (s, l, line_size);
441 line_free = line_size;
445 while ((f = fgets(line_ptr, line_free, file)) != NULL) {
447 * Note that we only have to search what we just read, not
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 */
455 line_len += line_free - 1; /* bytes read minus '\0' */
456 size_save = line_size;
457 if (line_size < 256 * AGETS_LINE_INCR) {
460 line_size += 256 * AGETS_LINE_INCR;
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 */
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 */
470 * Return what we got even if there was not a newline. Only
471 * report done (NULL) when no data was processed.
473 if (f == NULL && line_len == 0) {
475 line = NULL; /* redundant, but clear */
477 errno = 0; /* flag EOF vs error */
480 malloc_leave(dbmalloc_caller_loc(s, l));
485 *=====================================================================
486 * Find/create a buffer for a particular file descriptor for use with
489 * void areads_getbuf (const char *file, int line, int fd)
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 *=====================================================================
497 static struct areads_buffer {
501 } *areads_buffer = NULL;
502 static int areads_bufcount = 0;
503 static ssize_t areads_bufsize = BUFSIZ; /* for the test program */
506 areads_getbuf(s, l, fd)
511 struct areads_buffer *new;
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);
520 size = areads_bufcount * sizeof(*areads_buffer);
521 memcpy(new, areads_buffer, size);
523 amfree(areads_buffer);
525 areads_bufcount = fd + 1;
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;
537 *=====================================================================
538 * Return the amount of data still in an areads buffer.
540 * ssize_t areads_dataready (int fd)
542 * entry: fd = file descriptor to release buffer for
543 * exit: returns number of bytes of data ready to process
544 *=====================================================================
553 if(fd >= 0 && fd < areads_bufcount && areads_buffer[fd].buffer != NULL) {
554 r = (ssize_t) (areads_buffer[fd].endptr - areads_buffer[fd].buffer);
560 *=====================================================================
561 * Release a buffer for a particular file descriptor used by areads().
563 * void areads_relbuf (int fd)
565 * entry: fd = file descriptor to release buffer for
567 *=====================================================================
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;
582 *=====================================================================
583 * Get the next line of input from a file descriptor.
585 * char *areads (int fd)
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).
591 * Notes: the newline, if read, is removed from the string
592 * the caller is responsible for free'ing the string
593 *=====================================================================
597 debug_areads (s, l, fd)
611 malloc_enter(dbmalloc_caller_loc(s, l));
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) {
623 * No newline yet, so get more data.
626 if ((size = areads_buffer[fd].bufsize) < 256 * areads_bufsize) {
629 size += 256 * areads_bufsize;
631 newbuf = debug_alloc(s, l, size + 1);
632 memcpy (newbuf, buffer, areads_buffer[fd].bufsize + 1);
633 amfree(areads_buffer[fd].buffer);
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);
642 if ((r = read(fd, endptr, buflen)) <= 0) {
644 errno = 0; /* flag EOF instead of error */
646 malloc_leave(dbmalloc_caller_loc(s, l));
649 endptr[r] = '\0'; /* we always leave room for this */
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));
678 set_pname("file test");
680 /* Don't die when child closes pipe */
681 signal(SIGPIPE, SIG_IGN);
683 name = "/tmp/a/b/c/d/e";
684 if (argc > 2 && argv[1][0] != '\0') {
688 if (argc > 3 && argv[2][0] != '\0') {
692 if (argc > 4 && argv[3][0] != '\0') {
696 fprintf(stderr, "Create parent directories of %s ...", name);
697 rc = mkpdir(name, 02777, (uid_t)-1, (gid_t)-1);
699 fprintf(stderr, " done\n");
705 fprintf(stderr, "Delete %s back to %s ...", name, top);
706 rc = rmpdir(name, top);
708 fprintf(stderr, " done\n");
714 fprintf(stderr, "areads dump of %s ...", file);
715 if ((fd = open (file, 0)) < 0) {
719 areads_bufsize = 1; /* force buffer overflow */
720 while ((line = areads(fd)) != NULL) {
725 fprintf(stderr, " done.\n");
727 fprintf(stderr, "Finished.\n");