diff options
Diffstat (limited to 'src/makepath.c')
-rw-r--r-- | src/makepath.c | 157 |
1 files changed, 118 insertions, 39 deletions
diff --git a/src/makepath.c b/src/makepath.c index 7631772..ff4c25f 100644 --- a/src/makepath.c +++ b/src/makepath.c @@ -1,9 +1,9 @@ /* makepath.c -- Ensure that a directory path exists. - Copyright (C) 1990, 2006, 2007, 2010 Free Software Foundation, Inc. + Copyright (C) 1990, 2006 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3, or (at your option) + the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, @@ -29,15 +29,14 @@ #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> -#include "cpiohdr.h" -#include "dstring.h" -#include "extern.h" /* Ensure that the directory ARGPATH exists. Remove any trailing slashes from ARGPATH before calling this function. - Make all directory components that don't already exist with - permissions 700. + Make any leading directories that don't already exist, with + permissions PARENT_MODE. + If the last element of ARGPATH does not exist, create it as + a new directory with permissions MODE. If OWNER and GROUP are non-negative, make them the UID and GID of created directories. If VERBOSE_FMT_STRING is nonzero, use it as a printf format @@ -49,26 +48,48 @@ int make_path (char *argpath, + int mode, + int parent_mode, uid_t owner, gid_t group, - const char *verbose_fmt_string) + char *verbose_fmt_string) { char *dirpath; /* A copy we can scribble NULs on. */ struct stat stats; int retval = 0; - mode_t tmpmode; - mode_t invert_permissions; - int we_are_root = getuid () == 0; + int oldmask = umask (0); dirpath = alloca (strlen (argpath) + 1); - strcpy (dirpath, argpath); if (stat (dirpath, &stats)) { - tmpmode = MODE_RWX & ~ newdir_umask; - invert_permissions = we_are_root ? 0 : MODE_WXUSR & ~ tmpmode; + char *slash; + int tmp_mode; /* Initial perms for leading dirs. */ + int re_protect; /* Should leading dirs be unwritable? */ + struct ptr_list + { + char *dirname_end; + struct ptr_list *next; + }; + struct ptr_list *p, *leading_dirs = NULL; - char *slash = dirpath; + /* If leading directories shouldn't be writable or executable, + or should have set[ug]id or sticky bits set and we are setting + their owners, we need to fix their permissions after making them. */ + if (((parent_mode & 0300) != 0300) + || (owner != (uid_t) -1 && group != (gid_t) -1 + && (parent_mode & 07000) != 0)) + { + tmp_mode = 0700; + re_protect = 1; + } + else + { + tmp_mode = parent_mode; + re_protect = 0; + } + + slash = dirpath; while (*slash == '/') slash++; while ((slash = strchr (slash, '/'))) @@ -91,9 +112,10 @@ make_path (char *argpath, *(slash -1) = '\0'; } #endif - if (mkdir (dirpath, tmpmode ^ invert_permissions)) + if (mkdir (dirpath, tmp_mode)) { error (0, errno, _("cannot make directory `%s'"), dirpath); + umask (oldmask); return 1; } else @@ -101,18 +123,24 @@ make_path (char *argpath, if (verbose_fmt_string != NULL) error (0, 0, verbose_fmt_string, dirpath); - if (stat (dirpath, &stats)) - stat_error (dirpath); - else + if (owner != (uid_t) -1 && group != (gid_t) -1 + && chown (dirpath, owner, group) +#ifdef AFS + && errno != EPERM +#endif + ) { - if (owner != -1) - stats.st_uid = owner; - if (group != -1) - stats.st_gid = group; - - delay_set_stat (dirpath, &stats, invert_permissions); + chown_error_details (dirpath, owner, group); + retval = 1; + } + if (re_protect) + { + struct ptr_list *new = (struct ptr_list *) + alloca (sizeof (struct ptr_list)); + new->dirname_end = slash; + new->next = leading_dirs; + leading_dirs = new; } - #ifdef HPUX_CDF if (iscdf) { @@ -129,6 +157,7 @@ make_path (char *argpath, else if (!S_ISDIR (stats.st_mode)) { error (0, 0, _("`%s' exists but is not a directory"), dirpath); + umask (oldmask); return 1; } @@ -143,7 +172,7 @@ make_path (char *argpath, /* We're done making leading directories. Make the final component of the path. */ - if (mkdir (dirpath, tmpmode ^ invert_permissions)) + if (mkdir (dirpath, mode)) { /* In some cases, if the final component in dirpath was `.' then we just got an EEXIST error from that last mkdir(). If that's @@ -153,24 +182,51 @@ make_path (char *argpath, (!S_ISDIR (stats.st_mode) ) ) { error (0, errno, _("cannot make directory `%s'"), dirpath); + umask (oldmask); return 1; } } - else if (stat (dirpath, &stats)) - stat_error (dirpath); - else - { - if (owner != -1) - stats.st_uid = owner; - if (group != -1) - stats.st_gid = group; - - delay_set_stat (dirpath, &stats, invert_permissions); - } - if (verbose_fmt_string != NULL) error (0, 0, verbose_fmt_string, dirpath); + if (owner != (uid_t) -1 && group != (gid_t) -1) + { + if (chown (dirpath, owner, group) +#ifdef AFS + && errno != EPERM +#endif + ) + { + chown_error_details (dirpath, owner, group); + retval = 1; + } + } + /* chown may have turned off some permission bits we wanted. */ + if ((mode & 07000) != 0 && chmod (dirpath, mode)) + { + chmod_error_details (dirpath, mode); + retval = 1; + } + + /* If the mode for leading directories didn't include owner "wx" + privileges, we have to reset their protections to the correct + value. */ + for (p = leading_dirs; p != NULL; p = p->next) + { + *p->dirname_end = '\0'; +#if 0 + /* cpio always calls make_path with parent mode 0700, so + we don't have to do this. If we ever do have to do this, + we have to stat the directory first to get the setuid + bit so we don't break HP CDF's. */ + if (chmod (dirpath, parent_mode)) + { + chmod_error_details (dirpath, parent_mode); + retval = 1; + } +#endif + + } } else { @@ -179,10 +235,33 @@ make_path (char *argpath, if (!S_ISDIR (stats.st_mode)) { error (0, 0, _("`%s' exists but is not a directory"), dirpath); + umask (oldmask); return 1; } + /* chown must precede chmod because on some systems, + chown clears the set[ug]id bits for non-superusers, + resulting in incorrect permissions. + On System V, users can give away files with chown and then not + be able to chmod them. So don't give files away. */ + + if (owner != (uid_t) -1 && group != (gid_t) -1 + && chown (dirpath, owner, group) +#ifdef AFS + && errno != EPERM +#endif + ) + { + chown_error_details (dirpath, owner, group); + retval = 1; + } + if (chmod (dirpath, mode)) + { + chmod_error_details (dirpath, mode); + retval = 1; + } } + umask (oldmask); return retval; } |