2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1997-1998 University of Maryland at College Park
4 * Copyright (c) 2007-2012 Zmanda, Inc. All Rights Reserved.
7 * Permission to use, copy, modify, distribute, and sell this software and its
8 * documentation for any purpose is hereby granted without fee, provided that
9 * the above copyright notice appear in all copies and that both that
10 * copyright notice and this permission notice appear in supporting
11 * documentation, and that the name of U.M. not be used in advertising or
12 * publicity pertaining to distribution of the software without specific,
13 * written prior permission. U.M. makes no representations about the
14 * suitability of this software for any purpose. It is provided "as is"
15 * without express or implied warranty.
17 * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
19 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
21 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
22 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
24 * Author: AMANDA core development group.
27 * $Id: file.c,v 1.40 2006/07/19 17:41:15 martinea Exp $
29 * file and directory bashing routines
34 #include "timestamp.h"
38 static void areads_getbuf(const char *s, int l, int fd);
39 static char *original_cwd = NULL;
42 * Make a directory hierarchy given an entry to be created (by the caller)
43 * in the new target. In other words, create all the directories down to
44 * the last element, but not the last element. So a (potential) file name
45 * may be passed to mkpdir and all the parents of that file will be created.
49 char * file, /* file to create parent directories for */
50 mode_t mode, /* mode for new directories */
51 uid_t uid, /* uid for new directories */
52 gid_t gid) /* gid for new directories */
56 int rc; /* return code */
60 /* Remove last member of file, put the result in dir */
61 dir = stralloc(file); /* make a copy we can play with */
62 p = strrchr(dir, '/');
65 rc = mkdir(dir, mode);
67 if (errno == ENOENT) { /* create parent directory */
68 rc = mkpdir(dir, mode, uid, gid);
71 rc = mkdir(dir, mode);
73 if (rc != 0 && errno == EEXIST) {
79 /* mkdir succeded, set permission and ownership */
81 /* mkdir is affected by umask, so set the mode bits manually */
82 rc = chmod(dir, mode);
84 if (rc == 0 && geteuid() == 0) {
85 rc = chown(dir, uid, gid);
94 /* Remove as much of a directory hierarchy as possible.
96 ** - assumes that rmdir() on a non-empty directory will fail!
97 ** - stops deleting before topdir, ie: topdir will not be removed
98 ** - if file is not under topdir this routine will not notice
102 char * file, /* directory hierarchy to remove */
103 char * topdir) /* where to stop removing */
108 if(strcmp(file, topdir) == 0) return 0; /* all done */
111 if (rc != 0) switch(errno) {
113 #if ENOTEMPTY != EEXIST /* AIX makes these the same */
117 case EEXIST: /* directory not empty */
118 return 0; /* cant do much more */
119 case ENOENT: /* it has already gone */
122 case ENOTDIR: /* it was a file */
127 if(rc != 0) return -1; /* unexpected error */
129 dir = stralloc(file);
131 p = strrchr(dir, '/');
132 if (p == NULL || p == dir) {
136 rc = rmpdir(dir, topdir);
146 *=====================================================================
147 * Change directory to a "safe" location and set some base environment.
149 * void safe_cd (void)
151 * Set a default umask of 0077.
153 * Create the Amada debug directory (if defined) and the Amanda temp
156 * Try to chdir to the Amanda debug directory first, but it must be owned
157 * by the Amanda user and not allow rwx to group or other. Otherwise,
158 * try the same thing to the Amanda temp directory.
160 * If that is all OK, call save_core().
162 * Otherwise, cd to "/" so if we take a signal we cannot drop core
163 * unless the system administrator has made special arrangements (e.g.
164 * pre-created a core file with the right ownership and permissions).
165 *=====================================================================
174 uid_t client_uid = get_client_uid();
175 gid_t client_gid = get_client_gid();
179 /* stash away the current directory for later reference */
180 if (original_cwd == NULL) {
181 original_cwd = g_get_current_dir();
184 if (client_uid != (uid_t) -1) {
185 #if defined(AMANDA_DBGDIR)
186 d = stralloc2(AMANDA_DBGDIR, "/.");
187 (void) mkpdir(d, (mode_t)0700, client_uid, client_gid);
190 d = stralloc2(AMANDA_TMPDIR, "/.");
191 (void) mkpdir(d, (mode_t)0700, client_uid, client_gid);
195 #if defined(AMANDA_DBGDIR)
196 if (chdir(AMANDA_DBGDIR) != -1
197 && stat(".", &sbuf) != -1
198 && (sbuf.st_mode & 0777) == 0700 /* drwx------ */
199 && sbuf.st_uid == client_uid) { /* owned by Amanda user */
200 cd_ok = 1; /* this is a good place to be */
204 && chdir(AMANDA_TMPDIR) != -1
205 && stat(".", &sbuf) != -1
206 && (sbuf.st_mode & 0777) == 0700 /* drwx------ */
207 && sbuf.st_uid == client_uid) { /* owned by Amanda user */
208 cd_ok = 1; /* this is a good place to be */
211 save_core(); /* save any old core file */
213 if ((cd_ok = chdir("/")) == -1) {
214 (void)cd_ok; /* Quiet compiler warning if DEBUG disabled */
220 *=====================================================================
221 * Close all file descriptors except stdin, stdout and stderr. Make
222 * sure they are open.
224 * void safe_fd (fd_start, fd_count)
226 * entry: fd_start - start of fd-s to leave alone (or -1)
227 * fd_count - count of fd-s to leave alone
230 * On exit, all three standard file descriptors will be open and pointing
231 * someplace (either what we were handed or /dev/null) and all other
232 * file descriptors (up to FD_SETSIZE) will be closed.
233 *=====================================================================
243 for(fd = 0; fd < (int)FD_SETSIZE; fd++) {
246 * Open three file descriptors. If one of the standard
247 * descriptors is not open it will be pointed to /dev/null...
249 * This avoids, for instance, someone running us with stderr
250 * closed so that when we open some other file, messages
251 * sent to stderr do not accidentally get written to the
254 if (fcntl(fd, F_GETFD) == -1) {
255 if (open("/dev/null", O_RDWR) == -1) {
256 g_fprintf(stderr, _("/dev/null is inaccessable: %s\n"),
263 * Make sure nobody spoofs us with a lot of extra open files
264 * that would cause an open we do to get a very high file
265 * descriptor, which in turn might be used as an index into
266 * an array (e.g. an fd_set).
268 if (fd < fd_start || fd >= fd_start + fd_count) {
276 *=====================================================================
277 * Save an existing core file.
279 * void save_core (void)
286 * "core" to "coreYYYYMMDD",
287 * "coreYYYYMMDD" to "coreYYYYMMDDa",
288 * "coreYYYYMMDDa" to "coreYYYYMMDDb",
291 * ... where YYYYMMDD is the modification time of the original file.
292 * If it gets that far, an old "coreYYYYMMDDz" is thrown away.
293 *=====================================================================
301 if(stat("core", &sbuf) != -1) {
306 ts = get_datestamp_from_time(sbuf.st_mtime);
309 old = vstralloc("core", ts, suffix, NULL);
311 while(ts[0] != '\0') {
314 if(suffix[0] == 'a') {
316 } else if(suffix[0] == '\0') {
321 old = vstralloc("core", ts, suffix, NULL);
322 (void)rename(old, new); /* it either works ... */
331 ** Sanitise a file name.
333 ** Convert all '/', ':', and '\' characters to '_' so that we can use,
334 ** for example, disk names as part of file names.
336 ** - there is a many-to-one mapping between input and output
337 ** - Only / and '\0' are disallowed in filenames by POSIX, but Windows
338 ** disallows ':' and '\' as well. Furthermore, we use ':' as a
339 ** delimiter at other points in Amanda.
350 buf_size = strlen(inp) + 1; /* worst case */
351 buf = alloc(buf_size);
354 while((ch = *s++) != '\0') {
355 if((ch == '/') || (ch == ':') || (ch == '\\')) {
356 ch = '_'; /* convert "bad" to "_" */
360 assert(d < buf + buf_size);
368 old_sanitise_filename(
376 buf_size = 2*strlen(inp) + 1; /* worst case */
377 buf = alloc(buf_size);
380 while((ch = *s++) != '\0') {
385 ch = '_'; /* convert "bad" to "_" */
389 assert(d < buf + buf_size);
396 canonicalize_pathname(char *pathname, char *result_buf)
399 cygwin_conv_to_full_posix_path(pathname, result_buf);
401 strncpy(result_buf, pathname, PATH_MAX-1);
402 result_buf[PATH_MAX-1] = '\0';
407 *=====================================================================
408 * Get the next line of input from a stdio file.
410 * char *agets (FILE *stream)
412 * entry: stream - stream to read
413 * exit: returns a pointer to an alloc'd string or NULL
414 * at EOF or error. The functions ferror(stream) and
415 * feof(stream) should be checked by caller to determine
418 * Notes: the newline at the end of a line, if read, is removed from
419 * the string. Quoted newlines are left intact.
420 * the caller is responsible for free'ing the string
422 *=====================================================================
425 #define AGETS_LINE_INCR 128
429 const char *sourcefile,
434 char *line = alloc(AGETS_LINE_INCR);
435 size_t line_size = 0;
440 (void)sourcefile; /* Quiet unused parameter warning if not debugging */
441 (void)lineno; /* Quiet unused parameter warning if not debugging */
443 while ((ch = fgetc(stream)) != EOF) {
448 loffset--; /* Consume escape in buffer */
451 /* Reached end of line so exit without passing on LF */
466 if ((loffset + 1) >= line_size) {
470 * Reallocate input line.
471 * alloc() never return NULL pointer.
473 tmpline = alloc(line_size + AGETS_LINE_INCR);
474 memcpy(tmpline, line, line_size);
477 line_size = line_size + AGETS_LINE_INCR;
479 line[loffset++] = (char)ch;
482 if ((ch == EOF) && (loffset == 0)) {
483 amfree(line); /* amfree zeros line... */
485 line[loffset] = '\0';
489 * Return what we got even if there was not a newline.
490 * Only report done (NULL) when no data was processed.
497 *=====================================================================
498 * Find/create a buffer for a particular file descriptor for use with
501 * void areads_getbuf (const char *file, size_t line, int fd)
503 * entry: file, line = caller source location
504 * fd = file descriptor to look up
505 * exit: returns a pointer to the buffer, possibly new
506 *=====================================================================
509 static struct areads_buffer {
513 } *areads_buffer = NULL;
514 static int areads_bufcount = 0;
515 static size_t areads_bufsize = BUFSIZ; /* for the test program */
523 struct areads_buffer *new;
527 if(fd >= areads_bufcount) {
528 size = (size_t)(fd + 1) * SIZEOF(*areads_buffer);
529 new = (struct areads_buffer *) debug_alloc(s, l, size);
530 memset((char *)new, 0, size);
532 size = areads_bufcount * SIZEOF(*areads_buffer);
533 memcpy(new, areads_buffer, size);
535 amfree(areads_buffer);
537 areads_bufcount = fd + 1;
539 if(areads_buffer[fd].buffer == NULL) {
540 areads_buffer[fd].bufsize = areads_bufsize;
541 areads_buffer[fd].buffer = debug_alloc(s, l,
542 areads_buffer[fd].bufsize + 1);
543 areads_buffer[fd].buffer[0] = '\0';
544 areads_buffer[fd].endptr = areads_buffer[fd].buffer;
549 *=====================================================================
550 * Return the amount of data still in an areads buffer.
552 * ssize_t areads_dataready (int fd)
554 * entry: fd = file descriptor to release buffer for
555 * exit: returns number of bytes of data ready to process
556 *=====================================================================
565 if(fd >= 0 && fd < areads_bufcount && areads_buffer[fd].buffer != NULL) {
566 r = (ssize_t) (areads_buffer[fd].endptr - areads_buffer[fd].buffer);
572 *=====================================================================
573 * Release a buffer for a particular file descriptor used by areads().
575 * void areads_relbuf (int fd)
577 * entry: fd = file descriptor to release buffer for
579 *=====================================================================
586 if(fd >= 0 && fd < areads_bufcount) {
587 amfree(areads_buffer[fd].buffer);
588 areads_buffer[fd].endptr = NULL;
589 areads_buffer[fd].bufsize = 0;
594 *=====================================================================
595 * Get the next line of input from a file descriptor.
597 * char *areads (int fd)
599 * entry: fd = file descriptor to read
600 * exit: returns a pointer to an alloc'd string or NULL at EOF
601 * or error (errno will be zero on EOF).
603 * Notes: the newline, if read, is removed from the string
604 * the caller is responsible for free'ing the string
605 *=====================================================================
627 areads_getbuf(s, l, fd);
628 buffer = areads_buffer[fd].buffer;
629 endptr = areads_buffer[fd].endptr;
630 buflen = areads_buffer[fd].bufsize - (size_t)(endptr - buffer);
631 while((nl = strchr(buffer, '\n')) == NULL) {
633 * No newline yet, so get more data.
636 if ((size = areads_buffer[fd].bufsize) < 256 * areads_bufsize) {
639 size += 256 * areads_bufsize;
641 newbuf = debug_alloc(s, l, size + 1);
642 memcpy (newbuf, buffer, areads_buffer[fd].bufsize + 1);
643 amfree(areads_buffer[fd].buffer);
644 areads_buffer[fd].buffer = newbuf;
645 areads_buffer[fd].endptr = newbuf + areads_buffer[fd].bufsize;
646 areads_buffer[fd].bufsize = size;
647 buffer = areads_buffer[fd].buffer;
648 endptr = areads_buffer[fd].endptr;
649 buflen = areads_buffer[fd].bufsize - (size_t)(endptr - buffer);
651 if ((r = read(fd, endptr, buflen)) <= 0) {
653 errno = 0; /* flag EOF instead of error */
657 endptr[r] = '\0'; /* we always leave room for this */
662 line = stralloc(buffer);
663 size = (size_t)(endptr - nl); /* data still left in buffer */
664 memmove(buffer, nl, size);
665 areads_buffer[fd].endptr = buffer + size;
666 areads_buffer[fd].endptr[0] = '\0';
670 int robust_open(const char * pathname, int flags, mode_t mode) {
672 int e_busy_count = 0;
675 if (flags & O_CREAT) {
676 result = open(pathname, flags, mode);
678 result = open(pathname, flags);
683 /* EBUSY is a tricky one; sometimes it is synonymous with
684 EINTR, but sometimes it means the device is open
685 elsewhere (e.g., with a tape drive on Linux). We take
686 the middle path and retry, but with limited
688 if (errno == EBUSY && e_busy_count < 10) {
694 /* Always retry on EINTR; if the caller did
695 not specify non-blocking mode, then also retry on
696 EAGAIN or EWOULDBLOCK. */
702 && !(flags & O_NONBLOCK)
709 || errno == EWOULDBLOCK
725 fcntl(result, F_SETFD, 1); /* Throw away result. */
732 int robust_close(int fd) {
737 if (result != 0 && (0
748 || errno == EWOULDBLOCK
761 static uid_t client_uid = (uid_t) -1;
762 struct passwd *pwent;
764 if(client_uid == (uid_t) -1 && (pwent = getpwnam(CLIENT_LOGIN)) != NULL) {
765 client_uid = pwent->pw_uid;
775 static gid_t client_gid = (gid_t) -1;
776 struct passwd *pwent;
778 if(client_gid == (gid_t) -1 && (pwent = getpwnam(CLIENT_LOGIN)) != NULL) {
779 client_gid = pwent->pw_gid;
787 get_original_cwd(void)
789 if (original_cwd == NULL) {
790 original_cwd = g_get_current_dir();
811 * Configure program for internationalization:
812 * 1) Only set the message locale for now.
813 * 2) Set textdomain for all amanda related programs to "amanda"
814 * We don't want to be forced to support dozens of message catalogs
816 setlocale(LC_MESSAGES, "C");
817 textdomain("amanda");
821 set_pname("file test");
825 /* Don't die when child closes pipe */
826 signal(SIGPIPE, SIG_IGN);
828 name = "/tmp/a/b/c/d/e";
829 if (argc > 2 && argv[1][0] != '\0') {
833 if (argc > 3 && argv[2][0] != '\0') {
837 if (argc > 4 && argv[3][0] != '\0') {
841 g_fprintf(stderr, _("Create parent directories of %s ..."), name);
842 rc = mkpdir(name, (mode_t)02777, (uid_t)-1, (gid_t)-1);
844 g_fprintf(stderr, " done\n");
850 g_fprintf(stderr, _("Delete %s back to %s ..."), name, top);
851 rc = rmpdir(name, top);
853 g_fprintf(stderr, _(" done\n"));
859 g_fprintf(stderr, _("areads dump of %s ..."), file);
860 if ((fd = open (file, 0)) < 0) {
864 areads_bufsize = 1; /* force buffer overflow */
865 while ((line = areads(fd)) != NULL) {
870 g_fprintf(stderr, _(" done.\n"));
872 g_fprintf(stderr, _("Finished.\n"));