X-Git-Url: http://erislabs.net/gitweb/?a=blobdiff_plain;f=lib%2Fmakepath.c;h=99119a213107aa821a92a3095f80def13994ddf3;hb=0eeff3be000687cfd5aa94daba426c383e2c6950;hp=17844318a51e653d19e8626c7ebc0be8ab8fa6fc;hpb=5253e5dce47355d4f4490616bafbbabe1097e393;p=gnulib.git diff --git a/lib/makepath.c b/lib/makepath.c index 17844318a..99119a213 100644 --- a/lib/makepath.c +++ b/lib/makepath.c @@ -1,5 +1,5 @@ /* makepath.c -- Ensure that a directory path exists. - Copyright (C) 1990, 1997 Free Software Foundation, Inc. + Copyright (C) 1990, 1997, 1998 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 @@ -71,6 +71,16 @@ extern int errno; # endif #endif +#ifndef S_IWUSR +# define S_IWUSR 0200 +#endif + +#ifndef S_IXUSR +# define S_IXUSR 0100 +#endif + +#define WX_USR (S_IWUSR | S_IXUSR) + #ifdef __MSDOS__ typedef int uid_t; typedef int gid_t; @@ -87,7 +97,7 @@ void strip_trailing_slashes (); { \ /* We're done operating on basename_dir. \ Restore working directory. */ \ - if (saved_cwd) \ + if (do_chdir) \ { \ int fail = restore_cwd (&cwd, NULL, NULL); \ free_cwd (&cwd); \ @@ -123,7 +133,6 @@ void strip_trailing_slashes (); Return 0 if ARGPATH exists as a directory with the proper ownership and permissions when done, otherwise 1. */ -#if __STDC__ int make_path (const char *argpath, int mode, @@ -132,18 +141,6 @@ make_path (const char *argpath, gid_t group, int preserve_existing, const char *verbose_fmt_string) -#else -int -make_path (argpath, mode, parent_mode, owner, group, preserve_existing, - verbose_fmt_string) - const char *argpath; - int mode; - int parent_mode; - uid_t owner; - gid_t group; - int preserve_existing; - const char *verbose_fmt_string; -#endif { struct stat stats; int retval = 0; @@ -159,10 +156,9 @@ make_path (argpath, mode, parent_mode, owner, group, preserve_existing, struct ptr_list *next; }; struct ptr_list *p, *leading_dirs = NULL; - int saved_cwd = 0; + int do_chdir; /* Whether to chdir before each mkdir. */ struct saved_cwd cwd; char *basename_dir; - int first_subdir = 1; char *dirpath; /* Temporarily relax umask in case it's overly restrictive. */ @@ -176,8 +172,8 @@ make_path (argpath, mode, parent_mode, owner, group, preserve_existing, /* 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 + if (((parent_mode & WX_USR) != WX_USR) + || ((owner != (uid_t) -1 || group != (gid_t) -1) && (parent_mode & 07000) != 0)) { tmp_mode = 0700; @@ -189,6 +185,16 @@ make_path (argpath, mode, parent_mode, owner, group, preserve_existing, re_protect = 0; } + /* If we can record the current working directory, we may be able + to do the chdir optimization. */ + do_chdir = !save_cwd (&cwd); + + /* If we've saved the cwd and DIRPATH is an absolute pathname, + we must chdir to `/' in order to enable the chdir optimization. + So if chdir ("/") fails, turn off the optimization. */ + if (do_chdir && *dirpath == '/' && chdir ("/") < 0) + do_chdir = 0; + slash = dirpath; /* Skip over leading slashes. */ @@ -197,6 +203,8 @@ make_path (argpath, mode, parent_mode, owner, group, preserve_existing, while (1) { + int newly_created_dir = 1; + /* slash points to the leftmost unprocessed component of dirpath. */ basename_dir = slash; @@ -204,18 +212,19 @@ make_path (argpath, mode, parent_mode, owner, group, preserve_existing, if (slash == NULL) break; - if (first_subdir) - { - first_subdir = 0; - saved_cwd = !save_cwd (&cwd); - } - - /* If save_cwd could not record the current directory, then don't - do the chdir optimization and resort to using full pathnames. */ - - if (!saved_cwd) + /* If we're *not* doing chdir before each mkdir, then we have to refer + to the target using the full (multi-component) directory name. */ + if (!do_chdir) basename_dir = dirpath; + /* The mkdir and stat calls below appear to be reversed. + They are not. It is important to call mkdir first and then to + call stat (to distinguish the three cases) only if mkdir fails. + The alternative to this approach is to `stat' each directory, + then to call mkdir if it doesn't exist. But if some other process + were to create the directory between the stat & mkdir, the mkdir + would fail with EEXIST. */ + *slash = '\0'; if (mkdir (basename_dir, tmp_mode)) { @@ -234,38 +243,42 @@ make_path (argpath, mode, parent_mode, owner, group, preserve_existing, else { /* DIRPATH already exists and is a directory. */ + newly_created_dir = 0; } } - if (verbose_fmt_string != NULL) - error (0, 0, verbose_fmt_string, dirpath); + if (newly_created_dir) + { + if (verbose_fmt_string) + fprintf (stderr, verbose_fmt_string, dirpath); - if (owner != (uid_t) -1 && group != (gid_t) -1 - && chown (basename_dir, owner, group) + if ((owner != (uid_t) -1 || group != (gid_t) -1) + && chown (basename_dir, owner, group) #if defined(AFS) && defined (EPERM) - && errno != EPERM + && errno != EPERM #endif - ) - { - error (0, errno, "%s", dirpath); - CLEANUP; - return 1; - } + ) + { + error (0, errno, "%s", dirpath); + CLEANUP; + return 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; + 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; + } } /* If we were able to save the initial working directory, then we can use chdir to change into each directory before creating an entry in that directory. This avoids making stat and mkdir process O(n^2) file name components. */ - if (saved_cwd && chdir (basename_dir) < 0) + if (do_chdir && chdir (basename_dir) < 0) { error (0, errno, "cannot chdir to directory, %s", dirpath); CLEANUP; @@ -280,7 +293,7 @@ make_path (argpath, mode, parent_mode, owner, group, preserve_existing, slash++; } - if (!saved_cwd) + if (!do_chdir) basename_dir = dirpath; /* We're done making leading directories. @@ -356,7 +369,7 @@ make_path (argpath, mode, parent_mode, owner, group, preserve_existing, 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 + if ((owner != (uid_t) -1 || group != (gid_t) -1) && chown (dirpath, owner, group) #ifdef AFS && errno != EPERM