Redhat-bugzilla: 430835 * revert make_path code to previous state, which worked better * this can be dropped when permission issues are solved in upstream diff -up cpio-2.9/src/extern.h.dir_perm cpio-2.9/src/extern.h --- cpio-2.9/src/extern.h.dir_perm 2007-06-28 14:59:38.000000000 +0200 +++ cpio-2.9/src/extern.h 2008-03-03 11:57:43.000000000 +0100 @@ -140,8 +140,8 @@ void process_args (int argc, char *argv[ void initialize_buffers (void); /* makepath.c */ -int make_path (char *argpath, uid_t owner, gid_t group, - const char *verbose_fmt_string); +int make_path (char *argpath, int mode, int parent_mode, + uid_t owner, gid_t group, char *verbose_fmt_string); /* tar.c */ void write_out_tar_header (struct cpio_file_stat *file_hdr, int out_des); diff -up cpio-2.9/src/util.c.dir_perm cpio-2.9/src/util.c --- cpio-2.9/src/util.c.dir_perm 2007-06-28 15:04:51.000000000 +0200 +++ cpio-2.9/src/util.c 2008-03-03 11:45:00.000000000 +0100 @@ -618,14 +618,7 @@ create_all_directories (char *name) error (2, 0, _("virtual memory exhausted")); if (dir[0] != '.' || dir[1] != '\0') - { - const char *fmt; - if (warn_option & CPIO_WARN_INTERDIR) - fmt = _("Creating intermediate directory `%s'"); - else - fmt = NULL; - make_path (dir, -1, -1, fmt); - } + make_path (dir, mode, 0700, -1, -1, (char *) NULL); free (dir); } diff -up cpio-2.9/src/makepath.c.dir_perm cpio-2.9/src/makepath.c --- cpio-2.9/src/makepath.c.dir_perm 2007-06-28 15:09:47.000000000 +0200 +++ cpio-2.9/src/makepath.c 2008-03-03 11:45:00.000000000 +0100 @@ -29,15 +29,14 @@ #include #include #include -#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; + + /* 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; + } - char *slash = dirpath; + 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 + ) + { + chown_error_details (dirpath, owner, group); + retval = 1; + } + if (re_protect) { - if (owner != -1) - stats.st_uid = owner; - if (group != -1) - stats.st_gid = group; - - delay_set_stat (dirpath, &stats, invert_permissions); + 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; }