X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=common-src%2Ffile.c;h=b9e9788b8e097889495e85f795a44b9ac4ed1551;hb=HEAD;hp=ce4d28bd6695362e4426fd9acc5e9c32663dbfd6;hpb=1194fb66aa28d9929c3f2bef3cc6c1c3f40a60a4;p=debian%2Famanda diff --git a/common-src/file.c b/common-src/file.c index ce4d28b..b9e9788 100644 --- a/common-src/file.c +++ b/common-src/file.c @@ -1,6 +1,7 @@ /* * Amanda, The Advanced Maryland Automatic Network Disk Archiver * Copyright (c) 1997-1998 University of Maryland at College Park + * Copyright (c) 2007-2012 Zmanda, Inc. All Rights Reserved. * All Rights Reserved. * * Permission to use, copy, modify, distribute, and sell this software and its @@ -23,49 +24,19 @@ * Author: AMANDA core development group. */ /* - * $Id: file.c,v 1.35 2006/03/09 16:51:41 martinea Exp $ + * $Id: file.c,v 1.40 2006/07/19 17:41:15 martinea Exp $ * * file and directory bashing routines */ #include "amanda.h" #include "util.h" +#include "timestamp.h" +#include "arglist.h" +#include "file.h" -static int mk1dir P((const char *, int, uid_t, gid_t)); - -uid_t client_uid = (uid_t) -1; -gid_t client_gid = (gid_t) -1; - -/* Make a directory (internal function). -** If the directory already exists then we pretend we created it. -** XXX - I'm not sure about the use of the chown() stuff. On most systems -** it will do nothing - only root is permitted to change the owner -** of a file. -*/ -static int mk1dir(dir, mode, uid, gid) -const char *dir; /* directory to create */ -int mode; /* mode for new directory */ -uid_t uid; /* uid for new directory */ -gid_t gid; /* gid for new directory */ -{ - int rc; /* return code */ - - rc = 0; /* assume the best */ - - if(mkdir(dir, mode) == 0) { - chmod(dir, mode); /* mkdir() is affected by the umask */ - chown(dir, uid, gid); /* XXX - no-op on most systems? */ - } else { /* maybe someone beat us to it */ - int serrno; - - serrno = errno; - if(access(dir, F_OK) != 0) rc = -1; - errno = serrno; /* pass back the real error */ - } - - return rc; -} - +static void areads_getbuf(const char *s, int l, int fd); +static char *original_cwd = NULL; /* * Make a directory hierarchy given an entry to be created (by the caller) @@ -73,26 +44,45 @@ gid_t gid; /* gid for new directory */ * the last element, but not the last element. So a (potential) file name * may be passed to mkpdir and all the parents of that file will be created. */ -int mkpdir(file, mode, uid, gid) -char *file; /* file to create parent directories for */ -int mode; /* mode for new directories */ -uid_t uid; /* uid for new directories */ -gid_t gid; /* gid for new directories */ +int +mkpdir( + char * file, /* file to create parent directories for */ + mode_t mode, /* mode for new directories */ + uid_t uid, /* uid for new directories */ + gid_t gid) /* gid for new directories */ { - char *dir = NULL, *p; + char *dir; + char *p; int rc; /* return code */ rc = 0; - dir = stralloc(file); /* make a copy we can play with */ - + /* Remove last member of file, put the result in dir */ + dir = stralloc(file); /* make a copy we can play with */ p = strrchr(dir, '/'); - if(p != dir && p != NULL) { /* got a '/' or a simple name */ - *p = '\0'; + *p = '\0'; + + rc = mkdir(dir, mode); + if (rc != 0) { + if (errno == ENOENT) { /* create parent directory */ + rc = mkpdir(dir, mode, uid, gid); + if (rc != 0) + return rc; + rc = mkdir(dir, mode); + } + if (rc != 0 && errno == EEXIST) { + amfree(dir); + return 0; + } + } - if(access(dir, F_OK) != 0) { /* doesn't exist */ - if(mkpdir(dir, mode, uid, gid) != 0 || - mk1dir(dir, mode, uid, gid) != 0) rc = -1; /* create failed */ + /* mkdir succeded, set permission and ownership */ + if (rc == 0) { + /* mkdir is affected by umask, so set the mode bits manually */ + rc = chmod(dir, mode); + + if (rc == 0 && geteuid() == 0) { + rc = chown(dir, uid, gid); } } @@ -107,12 +97,13 @@ gid_t gid; /* gid for new directories */ ** - stops deleting before topdir, ie: topdir will not be removed ** - if file is not under topdir this routine will not notice */ -int rmpdir(file, topdir) -char *file; /* directory hierarchy to remove */ -char *topdir; /* where to stop removing */ +int +rmpdir( + char * file, /* directory hierarchy to remove */ + char * topdir) /* where to stop removing */ { int rc; - char *p, *dir = NULL; + char *p, *dir; if(strcmp(file, topdir) == 0) return 0; /* all done */ @@ -138,10 +129,10 @@ char *topdir; /* where to stop removing */ dir = stralloc(file); p = strrchr(dir, '/'); - if(p == dir) rc = 0; /* no /'s */ - else { + if (p == NULL || p == dir) { + rc = 0; + } else { *p = '\0'; - rc = rmpdir(dir, topdir); } @@ -151,34 +142,12 @@ char *topdir; /* where to stop removing */ } -/* - *===================================================================== - * Do Amanda setup for all programs. - * - * void amanda_setup (int argc, char **argv, int setup_flags) - * - * entry: setup_flags (see AMANDA_SETUP_FLAG_xxx) - * exit: none - *===================================================================== - */ - -void -amanda_setup (argc, argv, setup_flags) - int argc; - char **argv; - int setup_flags; -{ -} - /* *===================================================================== * Change directory to a "safe" location and set some base environment. * * void safe_cd (void) * - * entry: client_uid and client_gid set to CLIENT_LOGIN information - * exit: none - * * Set a default umask of 0077. * * Create the Amada debug directory (if defined) and the Amanda temp @@ -197,29 +166,29 @@ amanda_setup (argc, argv, setup_flags) */ void -safe_cd() +safe_cd(void) { int cd_ok = 0; struct stat sbuf; - struct passwd *pwent; char *d; - - if(client_uid == (uid_t) -1 && (pwent = getpwnam(CLIENT_LOGIN)) != NULL) { - client_uid = pwent->pw_uid; - client_gid = pwent->pw_gid; - endpwent(); - } + uid_t client_uid = get_client_uid(); + gid_t client_gid = get_client_gid(); (void) umask(0077); + /* stash away the current directory for later reference */ + if (original_cwd == NULL) { + original_cwd = g_get_current_dir(); + } + if (client_uid != (uid_t) -1) { #if defined(AMANDA_DBGDIR) d = stralloc2(AMANDA_DBGDIR, "/."); - (void) mkpdir(d, 02700, client_uid, client_gid); + (void) mkpdir(d, (mode_t)0700, client_uid, client_gid); amfree(d); #endif d = stralloc2(AMANDA_TMPDIR, "/."); - (void) mkpdir(d, 02700, client_uid, client_gid); + (void) mkpdir(d, (mode_t)0700, client_uid, client_gid); amfree(d); } @@ -241,7 +210,9 @@ safe_cd() if(cd_ok) { save_core(); /* save any old core file */ } else { - (void) chdir("/"); /* assume this works */ + if ((cd_ok = chdir("/")) == -1) { + (void)cd_ok; /* Quiet compiler warning if DEBUG disabled */ + } } } @@ -263,13 +234,13 @@ safe_cd() */ void -safe_fd(fd_start, fd_count) - int fd_start; - int fd_count; +safe_fd( + int fd_start, + int fd_count) { int fd; - for(fd = 0; fd < FD_SETSIZE; fd++) { + for(fd = 0; fd < (int)FD_SETSIZE; fd++) { if (fd < 3) { /* * Open three file descriptors. If one of the standard @@ -282,7 +253,7 @@ safe_fd(fd_start, fd_count) */ if (fcntl(fd, F_GETFD) == -1) { if (open("/dev/null", O_RDWR) == -1) { - fprintf(stderr, "/dev/null is inaccessable: %s\n", + g_fprintf(stderr, _("/dev/null is inaccessable: %s\n"), strerror(errno)); exit(1); } @@ -299,7 +270,6 @@ safe_fd(fd_start, fd_count) } } } - } /* @@ -324,7 +294,7 @@ safe_fd(fd_start, fd_count) */ void -save_core() +save_core(void) { struct stat sbuf; @@ -333,7 +303,7 @@ save_core() char suffix[2]; char *old, *new; - ts = construct_datestamp((time_t *)&sbuf.st_mtime); + ts = get_datestamp_from_time(sbuf.st_mtime); suffix[0] = 'z'; suffix[1] = '\0'; old = vstralloc("core", ts, suffix, NULL); @@ -360,133 +330,175 @@ save_core() /* ** Sanitise a file name. ** -** Convert all funny characters to '_' so that we can use, +** Convert all '/', ':', and '\' characters to '_' so that we can use, ** for example, disk names as part of file names. ** Notes: ** - there is a many-to-one mapping between input and output -** XXX - We only look for '/' and ' ' at the moment. May -** XXX - be we should also do all unprintables. +** - Only / and '\0' are disallowed in filenames by POSIX, but Windows +** disallows ':' and '\' as well. Furthermore, we use ':' as a +** delimiter at other points in Amanda. */ -char *sanitise_filename(inp) -char *inp; +char * +sanitise_filename( + char * inp) { char *buf; - int buf_size; + size_t buf_size; char *s, *d; int ch; - buf_size = 2 * strlen(inp) + 1; /* worst case */ + buf_size = strlen(inp) + 1; /* worst case */ buf = alloc(buf_size); d = buf; s = inp; while((ch = *s++) != '\0') { - if(ch == '_') { - if(d >= buf + buf_size) { - amfree(buf); - return NULL; /* cannot happen */ - } - *d++ = '_'; /* convert _ to __ to try */ - /* and ensure unique output */ - } else if(ch == '/' || isspace(ch)) { + if((ch == '/') || (ch == ':') || (ch == '\\')) { ch = '_'; /* convert "bad" to "_" */ } - if(d >= buf + buf_size) { - amfree(buf); - return NULL; /* cannot happen */ - } - *d++ = ch; + *d++ = (char)ch; } - if(d >= buf + buf_size) { - amfree(buf); - return NULL; /* cannot happen */ + assert(d < buf + buf_size); + *d = '\0'; + + return buf; +} + +/* duplicate '_' */ +char * +old_sanitise_filename( + char * inp) +{ + char *buf; + size_t buf_size; + char *s, *d; + int ch; + + buf_size = 2*strlen(inp) + 1; /* worst case */ + buf = alloc(buf_size); + d = buf; + s = inp; + while((ch = *s++) != '\0') { + if(ch == '_') { + *d++ = (char)ch; + } + if(ch == '/') { + ch = '_'; /* convert "bad" to "_" */ + } + *d++ = (char)ch; } + assert(d < buf + buf_size); *d = '\0'; return buf; } +void +canonicalize_pathname(char *pathname, char *result_buf) +{ +#ifdef __CYGWIN__ + cygwin_conv_to_full_posix_path(pathname, result_buf); +#else + strncpy(result_buf, pathname, PATH_MAX-1); + result_buf[PATH_MAX-1] = '\0'; +#endif +} + /* *===================================================================== * Get the next line of input from a stdio file. * - * char *agets (FILE *f) + * char *agets (FILE *stream) * - * entry: f = stdio stream to read - * exit: returns a pointer to an alloc'd string or NULL at EOF - * or error (errno will be zero on EOF). + * entry: stream - stream to read + * exit: returns a pointer to an alloc'd string or NULL + * at EOF or error. The functions ferror(stream) and + * feof(stream) should be checked by caller to determine + * stream status. * - * Notes: the newline, if read, is removed from the string + * Notes: the newline at the end of a line, if read, is removed from + * the string. Quoted newlines are left intact. * the caller is responsible for free'ing the string + * *===================================================================== */ +#define AGETS_LINE_INCR 128 + char * -debug_agets(s, l, file) - const char *s; - int l; - FILE *file; +debug_agets( + const char *sourcefile, + int lineno, + FILE * stream) { - char *line = NULL, *line_ptr; - size_t line_size, size_save; - int line_free, line_len; - char *cp; - char *f; - - malloc_enter(dbmalloc_caller_loc(s, l)); + int ch; + char *line = alloc(AGETS_LINE_INCR); + size_t line_size = 0; + size_t loffset = 0; + int inquote = 0; + int escape = 0; + + (void)sourcefile; /* Quiet unused parameter warning if not debugging */ + (void)lineno; /* Quiet unused parameter warning if not debugging */ + + while ((ch = fgetc(stream)) != EOF) { + if (ch == '\n') { + if (!inquote) { + if (escape) { + escape = 0; + loffset--; /* Consume escape in buffer */ + continue; + } + /* Reached end of line so exit without passing on LF */ + break; + } + } -#define AGETS_LINE_INCR 128 + if (ch == '\\') { + escape = !escape; + } else { + if (ch == '"') { + if (!escape) + inquote = !inquote; + } + escape = 0; + } - line_size = AGETS_LINE_INCR; - line = debug_alloc (s, l, line_size); - line_free = line_size; - line_ptr = line; - line_len = 0; + if ((loffset + 1) >= line_size) { + char *tmpline; - while ((f = fgets(line_ptr, line_free, file)) != NULL) { - /* - * Note that we only have to search what we just read, not - * the whole buffer. - */ - if ((cp = strchr (line_ptr, '\n')) != NULL) { - line_len += cp - line_ptr; - *cp = '\0'; /* zap the newline */ - break; /* got to end of line */ - } - line_len += line_free - 1; /* bytes read minus '\0' */ - size_save = line_size; - if (line_size < 256 * AGETS_LINE_INCR) { - line_size *= 2; - } else { - line_size += 256 * AGETS_LINE_INCR; + /* + * Reallocate input line. + * alloc() never return NULL pointer. + */ + tmpline = alloc(line_size + AGETS_LINE_INCR); + memcpy(tmpline, line, line_size); + amfree(line); + line = tmpline; + line_size = line_size + AGETS_LINE_INCR; } - cp = debug_alloc (s, l, line_size); /* get more space */ - memcpy (cp, line, size_save); /* copy old to new */ - free (line); /* and release the old */ - line = cp; - line_ptr = line + size_save - 1; /* start at the null byte */ - line_free = line_size - line_len; /* and we get to use it */ + line[loffset++] = (char)ch; } + + if ((ch == EOF) && (loffset == 0)) { + amfree(line); /* amfree zeros line... */ + } else { + line[loffset] = '\0'; + } + /* - * Return what we got even if there was not a newline. Only - * report done (NULL) when no data was processed. + * Return what we got even if there was not a newline. + * Only report done (NULL) when no data was processed. */ - if (f == NULL && line_len == 0) { - amfree (line); - line = NULL; /* redundant, but clear */ - if(!ferror(file)) { - errno = 0; /* flag EOF vs error */ - } - } - malloc_leave(dbmalloc_caller_loc(s, l)); return line; } + /* *===================================================================== * Find/create a buffer for a particular file descriptor for use with * areads(). * - * void areads_getbuf (const char *file, int line, int fd) + * void areads_getbuf (const char *file, size_t line, int fd) * * entry: file, line = caller source location * fd = file descriptor to look up @@ -497,27 +509,27 @@ debug_agets(s, l, file) static struct areads_buffer { char *buffer; char *endptr; - ssize_t bufsize; + size_t bufsize; } *areads_buffer = NULL; static int areads_bufcount = 0; -static ssize_t areads_bufsize = BUFSIZ; /* for the test program */ +static size_t areads_bufsize = BUFSIZ; /* for the test program */ static void -areads_getbuf(s, l, fd) - const char *s; - int l; - int fd; +areads_getbuf( + const char *s, + int l, + int fd) { struct areads_buffer *new; - ssize_t size; + size_t size; assert(fd >= 0); if(fd >= areads_bufcount) { - size = (fd + 1) * sizeof(*areads_buffer); + size = (size_t)(fd + 1) * SIZEOF(*areads_buffer); new = (struct areads_buffer *) debug_alloc(s, l, size); memset((char *)new, 0, size); if(areads_buffer) { - size = areads_bufcount * sizeof(*areads_buffer); + size = areads_bufcount * SIZEOF(*areads_buffer); memcpy(new, areads_buffer, size); } amfree(areads_buffer); @@ -545,8 +557,8 @@ areads_getbuf(s, l, fd) */ ssize_t -areads_dataready(fd) - int fd; +areads_dataready( + int fd) { ssize_t r = 0; @@ -568,8 +580,8 @@ areads_dataready(fd) */ void -areads_relbuf(fd) - int fd; +areads_relbuf( + int fd) { if(fd >= 0 && fd < areads_bufcount) { amfree(areads_buffer[fd].buffer); @@ -594,22 +606,20 @@ areads_relbuf(fd) */ char * -debug_areads (s, l, fd) - const char *s; - int l; - int fd; +debug_areads ( + const char *s, + int l, + int fd) { char *nl; char *line; char *buffer; char *endptr; char *newbuf; - ssize_t buflen; - ssize_t size; + size_t buflen; + size_t size; ssize_t r; - malloc_enter(dbmalloc_caller_loc(s, l)); - if(fd < 0) { errno = EBADF; return NULL; @@ -617,7 +627,7 @@ debug_areads (s, l, fd) areads_getbuf(s, l, fd); buffer = areads_buffer[fd].buffer; endptr = areads_buffer[fd].endptr; - buflen = areads_buffer[fd].bufsize - (endptr - buffer); + buflen = areads_buffer[fd].bufsize - (size_t)(endptr - buffer); while((nl = strchr(buffer, '\n')) == NULL) { /* * No newline yet, so get more data. @@ -631,19 +641,17 @@ debug_areads (s, l, fd) newbuf = debug_alloc(s, l, size + 1); memcpy (newbuf, buffer, areads_buffer[fd].bufsize + 1); amfree(areads_buffer[fd].buffer); - buffer = NULL; areads_buffer[fd].buffer = newbuf; areads_buffer[fd].endptr = newbuf + areads_buffer[fd].bufsize; areads_buffer[fd].bufsize = size; buffer = areads_buffer[fd].buffer; endptr = areads_buffer[fd].endptr; - buflen = areads_buffer[fd].bufsize - (endptr - buffer); + buflen = areads_buffer[fd].bufsize - (size_t)(endptr - buffer); } if ((r = read(fd, endptr, buflen)) <= 0) { if(r == 0) { errno = 0; /* flag EOF instead of error */ } - malloc_leave(dbmalloc_caller_loc(s, l)); return NULL; } endptr[r] = '\0'; /* we always leave room for this */ @@ -652,19 +660,145 @@ debug_areads (s, l, fd) } *nl++ = '\0'; line = stralloc(buffer); - size = endptr - nl; /* data still left in buffer */ + size = (size_t)(endptr - nl); /* data still left in buffer */ memmove(buffer, nl, size); areads_buffer[fd].endptr = buffer + size; areads_buffer[fd].endptr[0] = '\0'; - malloc_leave(dbmalloc_caller_loc(s, l)); return line; } +int robust_open(const char * pathname, int flags, mode_t mode) { + int result = -1; + int e_busy_count = 0; + + for (;;) { + if (flags & O_CREAT) { + result = open(pathname, flags, mode); + } else { + result = open(pathname, flags); + } + + if (result < 0) { +#ifdef EBUSY + /* EBUSY is a tricky one; sometimes it is synonymous with + EINTR, but sometimes it means the device is open + elsewhere (e.g., with a tape drive on Linux). We take + the middle path and retry, but with limited + patience. */ + if (errno == EBUSY && e_busy_count < 10) { + e_busy_count ++; + continue; + } else +#endif + if (0 + /* Always retry on EINTR; if the caller did + not specify non-blocking mode, then also retry on + EAGAIN or EWOULDBLOCK. */ +#ifdef EINTR + || errno == EINTR +#endif + || ( 1 +#ifdef O_NONBLOCK + && !(flags & O_NONBLOCK) +#endif + && ( 0 +#ifdef EAGAIN + || errno == EAGAIN +#endif +#ifdef EWOULDBLOCK + || errno == EWOULDBLOCK +#endif + ) ) ) { + /* Try again */ + continue; + } else { + /* Failure. */ + return result; + } + } else { + break; + } + } + +#ifdef F_SETFD + if (result >= 0) { + fcntl(result, F_SETFD, 1); /* Throw away result. */ + } +#endif + + return result; +} + +int robust_close(int fd) { + for (;;) { + int result; + + result = close(fd); + if (result != 0 && (0 +#ifdef EINTR + || errno == EINTR +#endif +#ifdef EBUSY + || errno == EBUSY +#endif +#ifdef EAGAIN + || errno == EAGAIN +#endif +#ifdef EWOULDBLOCK + || errno == EWOULDBLOCK +#endif + )) { + continue; + } else { + return result; + } + } +} + +uid_t +get_client_uid(void) +{ + static uid_t client_uid = (uid_t) -1; + struct passwd *pwent; + + if(client_uid == (uid_t) -1 && (pwent = getpwnam(CLIENT_LOGIN)) != NULL) { + client_uid = pwent->pw_uid; + endpwent(); + } + + return client_uid; +} + +gid_t +get_client_gid(void) +{ + static gid_t client_gid = (gid_t) -1; + struct passwd *pwent; + + if(client_gid == (gid_t) -1 && (pwent = getpwnam(CLIENT_LOGIN)) != NULL) { + client_gid = pwent->pw_gid; + endpwent(); + } + + return client_gid; +} + +char * +get_original_cwd(void) +{ + if (original_cwd == NULL) { + original_cwd = g_get_current_dir(); + } + + return original_cwd; +} + #ifdef TEST -int main(argc, argv) - int argc; - char **argv; +int +main( + int argc, + char ** argv) { int rc; int fd; @@ -673,10 +807,21 @@ int main(argc, argv) char *file; char *line; + /* + * Configure program for internationalization: + * 1) Only set the message locale for now. + * 2) Set textdomain for all amanda related programs to "amanda" + * We don't want to be forced to support dozens of message catalogs + */ + setlocale(LC_MESSAGES, "C"); + textdomain("amanda"); + safe_fd(-1, 0); set_pname("file test"); + dbopen(NULL); + /* Don't die when child closes pipe */ signal(SIGPIPE, SIG_IGN); @@ -693,25 +838,25 @@ int main(argc, argv) name = argv[3]; } - fprintf(stderr, "Create parent directories of %s ...", name); - rc = mkpdir(name, 02777, (uid_t)-1, (gid_t)-1); + g_fprintf(stderr, _("Create parent directories of %s ..."), name); + rc = mkpdir(name, (mode_t)02777, (uid_t)-1, (gid_t)-1); if (rc == 0) - fprintf(stderr, " done\n"); + g_fprintf(stderr, " done\n"); else { - perror("failed"); + perror(_("failed")); return rc; } - fprintf(stderr, "Delete %s back to %s ...", name, top); + g_fprintf(stderr, _("Delete %s back to %s ..."), name, top); rc = rmpdir(name, top); if (rc == 0) - fprintf(stderr, " done\n"); + g_fprintf(stderr, _(" done\n")); else { - perror("failed"); + perror(_("failed")); return rc; } - fprintf(stderr, "areads dump of %s ...", file); + g_fprintf(stderr, _("areads dump of %s ..."), file); if ((fd = open (file, 0)) < 0) { perror(file); return 1; @@ -722,10 +867,13 @@ int main(argc, argv) amfree(line); } aclose(fd); - fprintf(stderr, " done.\n"); + g_fprintf(stderr, _(" done.\n")); - fprintf(stderr, "Finished.\n"); + g_fprintf(stderr, _("Finished.\n")); + + dbclose(); return 0; } #endif +