aboutsummaryrefslogtreecommitdiff
path: root/libbb
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2009-11-29 07:45:33 +0100
committerDenys Vlasenko <vda.linux@googlemail.com>2009-11-29 07:45:33 +0100
commitf94c9bf288290c9f4e5a7c2745922abd600e88ca (patch)
tree57289568fb310466798ae6b297fe46d6e3d2bfcf /libbb
parent2ce42e98d799de4c3389d9c4ce0a6b0d42dac7cc (diff)
downloadbusybox-f94c9bf288290c9f4e5a7c2745922abd600e88ca.tar.gz
tar: fix bug 673 (misdetection of repeated dir as hardlink). +92 bytes
While at it, remove many superfluous ops on unpack: mkdir("."), lots of umask() calls. Can remove more by caching username->uid. Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to 'libbb')
-rw-r--r--libbb/make_directory.c63
1 files changed, 47 insertions, 16 deletions
diff --git a/libbb/make_directory.c b/libbb/make_directory.c
index a4ad59975..4486eb1ed 100644
--- a/libbb/make_directory.c
+++ b/libbb/make_directory.c
@@ -28,53 +28,76 @@
int FAST_FUNC bb_make_directory(char *path, long mode, int flags)
{
- mode_t mask;
+ mode_t cur_mask;
+ mode_t org_mask;
const char *fail_msg;
- char *s = path;
+ char *s;
char c;
struct stat st;
- mask = umask(0);
- umask(mask & ~0300); /* Ensure intermediate dirs are wx */
+ /* Happens on bb_make_directory(dirname("no_slashes"),...) */
+ if (LONE_CHAR(path, '.'))
+ return 0;
+ org_mask = cur_mask = (mode_t)-1L;
+ s = path;
while (1) {
c = '\0';
- if (flags & FILEUTILS_RECUR) { /* Get the parent. */
- /* Bypass leading non-'/'s and then subsequent '/'s. */
+ if (flags & FILEUTILS_RECUR) { /* Get the parent */
+ /* Bypass leading non-'/'s and then subsequent '/'s */
while (*s) {
if (*s == '/') {
do {
++s;
} while (*s == '/');
c = *s; /* Save the current char */
- *s = '\0'; /* and replace it with nul. */
+ *s = '\0'; /* and replace it with nul */
break;
}
++s;
}
}
- if (!c) /* Last component uses orig umask */
- umask(mask);
+ if (c != '\0') {
+ /* Intermediate dirs: must have wx for user */
+ if (cur_mask == (mode_t)-1L) { /* wasn't done yet? */
+ mode_t new_mask;
+ org_mask = umask(0);
+ cur_mask = 0;
+ /* Clear u=wx in umask - this ensures
+ * they won't be cleared on mkdir */
+ new_mask = (org_mask & ~(mode_t)0300);
+ //bb_error_msg("org_mask:%o cur_mask:%o", org_mask, new_mask);
+ if (new_mask != cur_mask) {
+ cur_mask = new_mask;
+ umask(new_mask);
+ }
+ }
+ } else {
+ /* Last component: uses original umask */
+ //bb_error_msg("1 org_mask:%o", org_mask);
+ if (org_mask != cur_mask) {
+ cur_mask = org_mask;
+ umask(org_mask);
+ }
+ }
if (mkdir(path, 0777) < 0) {
/* If we failed for any other reason than the directory
- * already exists, output a diagnostic and return -1. */
+ * already exists, output a diagnostic and return -1 */
if (errno != EEXIST
|| !(flags & FILEUTILS_RECUR)
|| ((stat(path, &st) < 0) || !S_ISDIR(st.st_mode))
) {
fail_msg = "create";
- umask(mask);
break;
}
/* Since the directory exists, don't attempt to change
* permissions if it was the full target. Note that
* this is not an error condition. */
if (!c) {
- umask(mask);
- return 0;
+ goto ret0;
}
}
@@ -86,13 +109,21 @@ int FAST_FUNC bb_make_directory(char *path, long mode, int flags)
fail_msg = "set permissions of";
break;
}
- return 0;
+ goto ret0;
}
- /* Remove any inserted nul from the path (recursive mode). */
+ /* Remove any inserted nul from the path (recursive mode) */
*s = c;
} /* while (1) */
bb_perror_msg("can't %s directory '%s'", fail_msg, path);
- return -1;
+ flags = -1;
+ goto ret;
+ ret0:
+ flags = 0;
+ ret:
+ //bb_error_msg("2 org_mask:%o", org_mask);
+ if (org_mask != cur_mask)
+ umask(org_mask);
+ return flags;
}