📄 file.c
字号:
/* * Amanda, The Advanced Maryland Automatic Network Disk Archiver * Copyright (c) 1997-1998 University of Maryland at College Park * All Rights Reserved. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of U.M. not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. U.M. makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M. * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Author: AMANDA core development group. *//* * $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(const char *, mode_t, uid_t, gid_t);static void areads_getbuf(const char *s, int l, int fd);static char *original_cwd = NULL;/* Make a directory (internal function). * If the directory already exists then we pretend we created it. * * The uid and gid are used only if we are running as root. */static intmk1dir( const char *dir, /* directory to create */ mode_t mode, /* mode for new directory */ uid_t uid, /* uid for new directory */ gid_t gid) /* gid for new directory */{ int rc; /* return code */ rc = mkdir(dir, mode); if(rc != 0) { int serrno; serrno = errno; if(access(dir, F_OK) == 0) rc = 0; /* someone just beat us to it, so it's OK */ errno = serrno; } /* mkdir is affected by umask, so set the mode bits manually */ if (rc == 0) { rc = chmod(dir, mode); } if (rc == 0 && geteuid() == 0) { rc = chown(dir, uid, gid); } return rc;}/* * Make a directory hierarchy given an entry to be created (by the caller) * in the new target. In other words, create all the directories down to * 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. */intmkpdir( 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; char *p; int rc; /* return code */ rc = 0; 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'; 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 */ } } amfree(dir); return rc;}/* Remove as much of a directory hierarchy as possible.** Notes:** - assumes that rmdir() on a non-empty directory will fail!** - stops deleting before topdir, ie: topdir will not be removed** - if file is not under topdir this routine will not notice*/intrmpdir( char * file, /* directory hierarchy to remove */ char * topdir) /* where to stop removing */{ int rc; char *p, *dir; if(strcmp(file, topdir) == 0) return 0; /* all done */ rc = rmdir(file); if (rc != 0) switch(errno) {#ifdef ENOTEMPTY#if ENOTEMPTY != EEXIST /* AIX makes these the same */ case ENOTEMPTY:#endif#endif case EEXIST: /* directory not empty */ return 0; /* cant do much more */ case ENOENT: /* it has already gone */ rc = 0; /* ignore */ break; case ENOTDIR: /* it was a file */ rc = unlink(file); break; } if(rc != 0) return -1; /* unexpected error */ dir = stralloc(file); p = strrchr(dir, '/'); if (p == NULL || p == dir) { rc = 0; } else { *p = '\0'; rc = rmpdir(dir, topdir); } amfree(dir); return rc;}/* *===================================================================== * Change directory to a "safe" location and set some base environment. * * void safe_cd (void) * * Set a default umask of 0077. * * Create the Amada debug directory (if defined) and the Amanda temp * directory. * * Try to chdir to the Amanda debug directory first, but it must be owned * by the Amanda user and not allow rwx to group or other. Otherwise, * try the same thing to the Amanda temp directory. * * If that is all OK, call save_core(). * * Otherwise, cd to "/" so if we take a signal we cannot drop core * unless the system administrator has made special arrangements (e.g. * pre-created a core file with the right ownership and permissions). *===================================================================== */voidsafe_cd(void){ int cd_ok = 0; struct stat sbuf; char *d; 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, (mode_t)0700, client_uid, client_gid); amfree(d);#endif d = stralloc2(AMANDA_TMPDIR, "/."); (void) mkpdir(d, (mode_t)0700, client_uid, client_gid); amfree(d); }#if defined(AMANDA_DBGDIR) if (chdir(AMANDA_DBGDIR) != -1 && stat(".", &sbuf) != -1 && (sbuf.st_mode & 0777) == 0700 /* drwx------ */ && sbuf.st_uid == client_uid) { /* owned by Amanda user */ cd_ok = 1; /* this is a good place to be */ }#endif if (! cd_ok && chdir(AMANDA_TMPDIR) != -1 && stat(".", &sbuf) != -1 && (sbuf.st_mode & 0777) == 0700 /* drwx------ */ && sbuf.st_uid == client_uid) { /* owned by Amanda user */ cd_ok = 1; /* this is a good place to be */ } if(cd_ok) { save_core(); /* save any old core file */ } else { if ((cd_ok = chdir("/")) == -1) { (void)cd_ok; /* Quiet compiler warning if DEBUG disabled */ } }}/* *===================================================================== * Close all file descriptors except stdin, stdout and stderr. Make * sure they are open. * * void safe_fd (fd_start, fd_count) * * entry: fd_start - start of fd-s to leave alone (or -1) * fd_count - count of fd-s to leave alone * exit: none * * On exit, all three standard file descriptors will be open and pointing * someplace (either what we were handed or /dev/null) and all other * file descriptors (up to FD_SETSIZE) will be closed. *===================================================================== */voidsafe_fd( int fd_start, int fd_count){ int fd; for(fd = 0; fd < (int)FD_SETSIZE; fd++) { if (fd < 3) { /* * Open three file descriptors. If one of the standard * descriptors is not open it will be pointed to /dev/null... * * This avoids, for instance, someone running us with stderr * closed so that when we open some other file, messages * sent to stderr do not accidentally get written to the * wrong file. */ if (fcntl(fd, F_GETFD) == -1) { if (open("/dev/null", O_RDWR) == -1) { g_fprintf(stderr, _("/dev/null is inaccessable: %s\n"), strerror(errno)); exit(1); } } } else { /* * Make sure nobody spoofs us with a lot of extra open files * that would cause an open we do to get a very high file * descriptor, which in turn might be used as an index into * an array (e.g. an fd_set). */ if (fd < fd_start || fd >= fd_start + fd_count) { close(fd); } } }}/* *===================================================================== * Save an existing core file. * * void save_core (void) * * entry: none * exit: none * * Renames: * * "core" to "coreYYYYMMDD", * "coreYYYYMMDD" to "coreYYYYMMDDa", * "coreYYYYMMDDa" to "coreYYYYMMDDb", * ... * * ... where YYYYMMDD is the modification time of the original file. * If it gets that far, an old "coreYYYYMMDDz" is thrown away. *===================================================================== */voidsave_core(void){ struct stat sbuf; if(stat("core", &sbuf) != -1) { char *ts; char suffix[2]; char *old, *new; ts = get_datestamp_from_time(sbuf.st_mtime); suffix[0] = 'z'; suffix[1] = '\0'; old = vstralloc("core", ts, suffix, NULL); new = NULL; while(ts[0] != '\0') { amfree(new); new = old; if(suffix[0] == 'a') { suffix[0] = '\0'; } else if(suffix[0] == '\0') { ts[0] = '\0'; } else { suffix[0]--; } old = vstralloc("core", ts, suffix, NULL); (void)rename(old, new); /* it either works ... */ } amfree(ts); amfree(old); amfree(new); }}/*** Sanitise a file name.** ** 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** - 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( char * inp){ char *buf; size_t buf_size; char *s, *d; int ch; buf_size = strlen(inp) + 1; /* worst case */ buf = alloc(buf_size); d = buf; s = inp; while((ch = *s++) != '\0') { if((ch == '/') || (ch == ':') || (ch == '\\')) { ch = '_'; /* convert "bad" to "_" */ } *d++ = (char)ch; } 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;}voidcanonicalize_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 *stream) * * 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 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 128char *debug_agets( const char *sourcefile, int lineno,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -