--- cpio-2.9/src/copyin.c +++ cpio-2.9/src/copyin.c @@ -186,12 +186,11 @@ static int try_existing_file (struct cpio_file_stat* file_hdr, int in_file_des, - int *existing_dir, mode_t *existing_mode) + int *existing_dir) { struct stat file_stat; *existing_dir = false; - *existing_mode = 0; if (lstat (file_hdr->c_name, &file_stat) == 0) { if (S_ISDIR (file_stat.st_mode) @@ -201,7 +200,6 @@ we are trying to create, don't complain about it. */ *existing_dir = true; - *existing_mode = file_stat.st_mode; return 0; } else if (!unconditional_flag @@ -569,7 +567,7 @@ } static void -copyin_directory (struct cpio_file_stat *file_hdr, int existing_dir, mode_t existing_mode) +copyin_directory (struct cpio_file_stat *file_hdr, int existing_dir) { int res; /* Result of various function calls. */ #ifdef HPUX_CDF @@ -612,22 +610,14 @@ cdf_flag = 1; } #endif - res = mkdir (file_hdr->c_name, file_hdr->c_mode & ~077); + res = mkdir (file_hdr->c_name, file_hdr->c_mode); } else - { - if (!no_chown_flag && (existing_mode & 077) != 0 - && chmod (file_hdr->c_name, existing_mode & 07700) < 0) - { - error (0, errno, "%s: chmod", file_hdr->c_name); - return; - } - res = 0; - } + res = 0; if (res < 0 && create_dir_flag) { create_all_directories (file_hdr->c_name); - res = mkdir (file_hdr->c_name, file_hdr->c_mode & ~077); + res = mkdir (file_hdr->c_name, file_hdr->c_mode); } if (res < 0) { @@ -702,12 +692,12 @@ return; } - res = mknod (file_hdr->c_name, file_hdr->c_mode & ~077, + res = mknod (file_hdr->c_name, file_hdr->c_mode, makedev (file_hdr->c_rdev_maj, file_hdr->c_rdev_min)); if (res < 0 && create_dir_flag) { create_all_directories (file_hdr->c_name); - res = mknod (file_hdr->c_name, file_hdr->c_mode & ~077, + res = mknod (file_hdr->c_name, file_hdr->c_mode, makedev (file_hdr->c_rdev_maj, file_hdr->c_rdev_min)); } if (res < 0) @@ -782,10 +772,9 @@ copyin_file (struct cpio_file_stat* file_hdr, int in_file_des) { int existing_dir=0; - mode_t existing_mode; if (!to_stdout_option - && try_existing_file (file_hdr, in_file_des, &existing_dir, &existing_mode) < 0) + && try_existing_file (file_hdr, in_file_des, &existing_dir) < 0) return; /* Do the real copy or link. */ @@ -796,7 +785,7 @@ break; case CP_IFDIR: - copyin_directory(file_hdr, existing_dir, existing_mode); + copyin_directory (file_hdr, existing_dir); break; case CP_IFCHR: @@ -1576,8 +1565,6 @@ if (dot_flag) fputc ('\n', stderr); - apply_delayed_set_stat (); - if (append_flag) return; --- cpio-2.9/src/copypass.c +++ cpio-2.9/src/copypass.c @@ -239,23 +239,15 @@ cdf_flag = 1; } #endif - res = mkdir (output_name.ds_string, in_file_stat.st_mode & ~077); + res = mkdir (output_name.ds_string, in_file_stat.st_mode); } else - { - if (!no_chown_flag && (out_file_stat.st_mode & 077) != 0 - && chmod (output_name.ds_string, out_file_stat.st_mode & 07700) < 0) - { - error (0, errno, "%s: chmod", output_name.ds_string); - continue; - } - res = 0; - } + res = 0; if (res < 0 && create_dir_flag) { create_all_directories (output_name.ds_string); - res = mkdir (output_name.ds_string, in_file_stat.st_mode & ~077); + res = mkdir (output_name.ds_string, in_file_stat.st_mode); } if (res < 0) { @@ -298,12 +290,12 @@ if (link_res < 0) { - res = mknod (output_name.ds_string, in_file_stat.st_mode & ~077, + res = mknod (output_name.ds_string, in_file_stat.st_mode, in_file_stat.st_rdev); if (res < 0 && create_dir_flag) { create_all_directories (output_name.ds_string); - res = mknod (output_name.ds_string, in_file_stat.st_mode & ~077, + res = mknod (output_name.ds_string, in_file_stat.st_mode, in_file_stat.st_rdev); } if (res < 0) @@ -373,8 +365,6 @@ if (dot_flag) fputc ('\n', stderr); - apply_delayed_set_stat (); - if (!quiet_flag) { size_t blocks = (output_bytes + io_block_size - 1) / io_block_size; --- cpio-2.9/src/extern.h +++ cpio-2.9/src/extern.h @@ -140,8 +140,8 @@ 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); --- cpio-2.9/src/makepath.c +++ cpio-2.9/src/makepath.c @@ -36,8 +36,10 @@ /* 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 @@ *(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 @@ 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 @@ 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 @@ /* 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 @@ (!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 @@ 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; } --- cpio-2.9/src/util.c +++ cpio-2.9/src/util.c @@ -618,14 +618,7 @@ 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); }