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.40 2006/07/19 17:41:15 martinea Exp $
28 * file and directory bashing routines
34 static int mk1dir(const char *, mode_t, uid_t, gid_t);
35 static void areads_getbuf(const char *s, int l, int fd);
37 uid_t client_uid = (uid_t) -1;
38 gid_t client_gid = (gid_t) -1;
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
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 */
53 int rc; /* return code */
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);
59 } else { /* maybe someone beat us to it */
63 if(access(dir, F_OK) != 0)
65 errno = serrno; /* pass back the real error */
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.
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 */
87 int rc; /* return code */
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 */
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 */
107 /* Remove as much of a directory hierarchy as possible.
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
115 char * file, /* directory hierarchy to remove */
116 char * topdir) /* where to stop removing */
121 if(strcmp(file, topdir) == 0) return 0; /* all done */
124 if (rc != 0) switch(errno) {
126 #if ENOTEMPTY != EEXIST /* AIX makes these the same */
130 case EEXIST: /* directory not empty */
131 return 0; /* cant do much more */
132 case ENOENT: /* it has already gone */
135 case ENOTDIR: /* it was a file */
140 if(rc != 0) return -1; /* unexpected error */
142 dir = stralloc(file);
144 p = strrchr(dir, '/');
145 if (p == NULL || p == dir) {
149 rc = rmpdir(dir, topdir);
159 *=====================================================================
160 * Change directory to a "safe" location and set some base environment.
162 * void safe_cd (void)
164 * entry: client_uid and client_gid set to CLIENT_LOGIN information
167 * Set a default umask of 0077.
169 * Create the Amada debug directory (if defined) and the Amanda temp
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.
176 * If that is all OK, call save_core().
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 *=====================================================================
189 struct passwd *pwent;
192 if(client_uid == (uid_t) -1 && (pwent = getpwnam(CLIENT_LOGIN)) != NULL) {
193 client_uid = pwent->pw_uid;
194 client_gid = pwent->pw_gid;
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);
206 d = stralloc2(AMANDA_TMPDIR, "/.");
207 (void) mkpdir(d, (mode_t)02700, client_uid, client_gid);
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 */
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 */
227 save_core(); /* save any old core file */
229 if ((cd_ok = chdir("/")) == -1) {
230 (void)cd_ok; /* Quiet compiler warning if DEBUG disabled */
236 *=====================================================================
237 * Close all file descriptors except stdin, stdout and stderr. Make
238 * sure they are open.
240 * void safe_fd (fd_start, fd_count)
242 * entry: fd_start - start of fd-s to leave alone (or -1)
243 * fd_count - count of fd-s to leave alone
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 *=====================================================================
259 for(fd = 0; fd < FD_SETSIZE; fd++) {
262 * Open three file descriptors. If one of the standard
263 * descriptors is not open it will be pointed to /dev/null...
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
270 if (fcntl(fd, F_GETFD) == -1) {
271 if (open("/dev/null", O_RDWR) == -1) {
272 fprintf(stderr, "/dev/null is inaccessable: %s\n",
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).
284 if (fd < fd_start || fd >= fd_start + fd_count) {
292 *=====================================================================
293 * Save an existing core file.
295 * void save_core (void)
302 * "core" to "coreYYYYMMDD",
303 * "coreYYYYMMDD" to "coreYYYYMMDDa",
304 * "coreYYYYMMDDa" to "coreYYYYMMDDb",
307 * ... where YYYYMMDD is the modification time of the original file.
308 * If it gets that far, an old "coreYYYYMMDDz" is thrown away.
309 *=====================================================================
317 if(stat("core", &sbuf) != -1) {
322 ts = construct_datestamp((time_t *)&sbuf.st_mtime);
325 old = vstralloc("core", ts, suffix, NULL);
327 while(ts[0] != '\0') {
330 if(suffix[0] == 'a') {
332 } else if(suffix[0] == '\0') {
337 old = vstralloc("core", ts, suffix, NULL);
338 (void)rename(old, new); /* it either works ... */
347 ** Sanitise a file name.
349 ** Convert all '/' characters to '_' so that we can use,
350 ** for example, disk names as part of file names.
352 ** - there is a many-to-one mapping between input and output
353 ** - Only / and '\0' are disallowed in filenames by POSIX...
364 buf_size = strlen(inp) + 1; /* worst case */
365 buf = alloc(buf_size);
368 while((ch = *s++) != '\0') {
370 ch = '_'; /* convert "bad" to "_" */
374 assert(d < buf + buf_size);
382 old_sanitise_filename(
390 buf_size = 2*strlen(inp) + 1; /* worst case */
391 buf = alloc(buf_size);
394 while((ch = *s++) != '\0') {
399 ch = '_'; /* convert "bad" to "_" */
403 assert(d < buf + buf_size);
410 *=====================================================================
411 * Get the next line of input from a stdio file.
413 * char *agets (FILE *stream)
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
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
425 *=====================================================================
428 #define AGETS_LINE_INCR 128
432 const char *sourcefile,
437 char *line = alloc(AGETS_LINE_INCR);
438 size_t line_size = 0;
443 (void)sourcefile; /* Quiet unused parameter warning if not debugging */
444 (void)lineno; /* Quiet unused parameter warning if not debugging */
446 while ((ch = fgetc(stream)) != EOF) {
451 loffset--; /* Consume escape in buffer */
454 /* Reached end of line so exit without passing on LF */
469 if ((loffset + 1) >= line_size) {
473 * Reallocate input line.
474 * alloc() never return NULL pointer.
476 tmpline = alloc(line_size + AGETS_LINE_INCR);
477 memcpy(tmpline, line, line_size);
480 line_size = line_size + AGETS_LINE_INCR;
482 line[loffset++] = (char)ch;
485 if ((ch == EOF) && (loffset == 0)) {
486 amfree(line); /* amfree zeros line... */
488 line[loffset] = '\0';
492 * Return what we got even if there was not a newline.
493 * Only report done (NULL) when no data was processed.
500 *=====================================================================
501 * Find/create a buffer for a particular file descriptor for use with
504 * void areads_getbuf (const char *file, size_t line, int fd)
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 *=====================================================================
512 static struct areads_buffer {
516 } *areads_buffer = NULL;
517 static int areads_bufcount = 0;
518 static size_t areads_bufsize = BUFSIZ; /* for the test program */
526 struct areads_buffer *new;
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);
535 size = areads_bufcount * SIZEOF(*areads_buffer);
536 memcpy(new, areads_buffer, size);
538 amfree(areads_buffer);
540 areads_bufcount = fd + 1;
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;
552 *=====================================================================
553 * Return the amount of data still in an areads buffer.
555 * ssize_t areads_dataready (int fd)
557 * entry: fd = file descriptor to release buffer for
558 * exit: returns number of bytes of data ready to process
559 *=====================================================================
568 if(fd >= 0 && fd < areads_bufcount && areads_buffer[fd].buffer != NULL) {
569 r = (ssize_t) (areads_buffer[fd].endptr - areads_buffer[fd].buffer);
575 *=====================================================================
576 * Release a buffer for a particular file descriptor used by areads().
578 * void areads_relbuf (int fd)
580 * entry: fd = file descriptor to release buffer for
582 *=====================================================================
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;
597 *=====================================================================
598 * Get the next line of input from a file descriptor.
600 * char *areads (int fd)
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).
606 * Notes: the newline, if read, is removed from the string
607 * the caller is responsible for free'ing the string
608 *=====================================================================
626 malloc_enter(dbmalloc_caller_loc(s, l));
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) {
638 * No newline yet, so get more data.
641 if ((size = areads_buffer[fd].bufsize) < 256 * areads_bufsize) {
644 size += 256 * areads_bufsize;
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);
656 if ((r = read(fd, endptr, buflen)) <= 0) {
658 errno = 0; /* flag EOF instead of error */
660 malloc_leave(dbmalloc_caller_loc(s, l));
663 endptr[r] = '\0'; /* we always leave room for this */
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));
693 set_pname("file test");
697 /* Don't die when child closes pipe */
698 signal(SIGPIPE, SIG_IGN);
700 name = "/tmp/a/b/c/d/e";
701 if (argc > 2 && argv[1][0] != '\0') {
705 if (argc > 3 && argv[2][0] != '\0') {
709 if (argc > 4 && argv[3][0] != '\0') {
713 fprintf(stderr, "Create parent directories of %s ...", name);
714 rc = mkpdir(name, (mode_t)02777, (uid_t)-1, (gid_t)-1);
716 fprintf(stderr, " done\n");
722 fprintf(stderr, "Delete %s back to %s ...", name, top);
723 rc = rmpdir(name, top);
725 fprintf(stderr, " done\n");
731 fprintf(stderr, "areads dump of %s ...", file);
732 if ((fd = open (file, 0)) < 0) {
736 areads_bufsize = 1; /* force buffer overflow */
737 while ((line = areads(fd)) != NULL) {
742 fprintf(stderr, " done.\n");
744 fprintf(stderr, "Finished.\n");