Mercurial > hg > octave-shane > gnulib-hg
annotate lib/mkdir-p.c @ 6527:6b31c8787689
Sync from coreutils.
* doc/getdate.texi (General date syntax): Invalid dates are rejected.
(Time of day items): Mention the possibility of leap seconds.
Problem reported by Dr. David Alan Gilbert.
* lib/chdir-long.c (cdb_free): Don't bother trying to open directory
for write access: POSIX says that must fail.
* lib/fts.c (diropen): Likewise.
* lib/save-cwd.c (save_cwd): Likewise.
* lib/chdir-long.c (cdb_free): Open with O_NOCTTY | O_NONBLOCK as
well, for minor improvements on hosts that lack O_DIRECTORY.
* lib/gettime.c (gettime) [!defined OK_TO_USE_1S_CLOCK]:
Report an error at compile-time if only a 1-second nominal clock
resolution is found.
* lib/lchmod.h: New file.
* lib/mkdir-p.c: Include lchmod.h, lchown.h.
(make_dir_parents): Use lchown rather than chown, and
lchmod rather than chmod.
* lib/mountlist.c (ME_DUMMY): "none" and "proc" file systems are dummies
too. Problem with "none" reported by Bob Proulx. Problem with
"proc" reported by n0dalus.
* lib/mountlist.c: Include <limits.h>.
(dev_from_mount_options)
[defined MOUNTED_GETMNTENT1 || defined MOUNTED_GETMNTENT2]:
New function. It no longer assumes "dev=" has the System V meaning
on Linux (since it doesn't). It also parses "dev=" more carefully.
(read_file_system_list)
[defined MOUNTED_GETMNTENT1 || defined MOUNTED_GETMNTENT2]: Use it.
MOUNTED_GETMNTENT2 is new here; the code didn't used to look for
dev= in that case.
* lib/posixtm.h (PDS_PRE_2000): New macro.
* lib/posixtm.c (year): Arg is now syntax_bits rather than allow_century.
All usages changed. Reject dates outside the range 1969-1999 if
PDS_PRE_2000 is used.
* modules/mkdir-p (Files): Add chdir-safer.c, chdir-safer.h, lchmod.h,
chdir-safer.m4, lchmod.m4.
* modules/openat: Add mkdirat.c, openat-priv.h.
* modules/lib-ignore: New file.
* lib/version-etc.c (COPYRIGHT_YEAR): Update to 2006.
Rewrite fts.c not to change the current working directory,
by using openat, fstatat, fdopendir, etc..
* lib/fts.c [! _LIBC]: Include "openat.h", "unistd--.h", and "fcntl--.h".
[_LIBC] (fchdir): Don't undef or define; no longer used.
(FCHDIR): Define in terms of cwd_advance_fd rather than fchdir.
Now, this `function' always succeeds, and consumes its file descriptor
parameter -- so callers must not close such FDs. Update callers.
(diropen_fd, opendirat, cwd_advance_fd): New functions.
(diropen): Add parameter, SP. Adjust all callers.
Implement using diropen_fd, rather than open.
(fts_open): Initialize new member, fts_cwd_fd.
Remove fts_rft-setting code.
(fts_close): Close fts_cwd_fd, if necessary.
(__opendir2): Define in terms of opendir or opendirat,
depending on whether the FST_NOCHDIR flag is set.
(fts_build): Since fts_safe_changedir consumes its FD, and since
this code must do `closedir(dirp)', dup the dirfd(dirp) argument,
and close the dup'd file descriptor upon failure.
(fts_stat): Use fstatat(...AT_SYMLINK_NOFOLLOW) in place of lstat.
(fts_safe_changedir): Tweak semantics to reflect that this function
now calls cwd_advance_fd and hence consumes its FD argument.
* lib/fts_.h [struct FTS] (fts_cwd_fd): New member.
(fts_rft): Remove now-unused member.
* lib/openat.c (fchownat): New function.
* lib/openat.h (fchmodat, fchownat): Declare.
(chmodat, lchmodat): Define convenience functions.
(chownat, lchownat): Likewise.
* lib/chdir-safer.h, chdir-safer.c: New files.
* lib/modechange.c (mode_compile): Reject an invalid mode string
that starts with an octal digit. From Andreas Gruenbacher.
* lib/openat.c: Include "fcntl--.h" and "unistd--.h", to map open
and dup to open_safer and dup_safer, respectively.
(openat_permissive): Fix typo in comment.
* lib/openat.c: Don't include <stdlib.h>, <unistd.h>, <fcntl.h>,
"gettext.h"; either no longer needed or are guaranteed by openat.h.
(_): Remove; no longer needed.
(openat): Renamed from rpl_openat; no need for rpl_openat
since openat.h renames openat for us.
Replace most of the body with a call to openat_permissive,
to avoid duplicate code.
Port to (probably hypothetical) environments were mode_t is
wider than int.
(openat_permissive): Require mode arg, so that we can check
types better. Put it just after flags. Change cwd failure
indicator from pointer-to-bool to pointer-to-errno-value.
All callers changed.
Invoke openat_save_fail and/or openat_restore_fail if
cwd_errno is null, so that openat can call us.
(openat_permissive, fdopendir, fstatat, unlinkat):
Simplify errno handling to avoid some duplicate code,
as it's OK to set errno on success.
* lib/openat.h: Revamp code so that function macros depend on
__OPENAT_PREFIX only, not also on AT_FDCWD.
(openat_ro): Remove. Caller changed to use openat_permissive.
(openat_permissive): Now a macro, if not a function.
(openat_restore_fail, openat_save_fail): Now always functions,
since mkdirat needs them even if __OPENAT_PREFIX is defined.
* lib/openat-priv.h: New file, defining macros used by mkdirat.c
and openat.c.
* lib/mkdirat.c: Include openat-priv.h.
Remove definitions of macros defined therein.
* lib/openat.c: Likewise.
* lib/mkdirat.c (mkdirat): New file and function.
* lib/openat.h (mkdirat): Declare.
* lib/openat.c (fdopendir): Don't change errno when returning non-NULL.
* lib/openat.h (openat_permissive): Declare.
(openat_ro): Define.
* lib/openat.c (EXPECTED_ERRNO): New macro.
(openat_permissive): New function -- used in remove.c rewrite.
(all functions): Set errno just before returning, only if there
was an actual failure.
Use EXPECTED_ERRNO rather than comparing against only ENOTDIR.
Emulate openat-family functions using Linux's procfs, if possible.
Idea and some code based on Ulrich Drepper's glibc changes.
* lib/openat.c: (BUILD_PROC_NAME): New macro.
Include <stdio.h>, <string.h>, "alloca.h" and "intprops.h".
(rpl_openat): Emulate by trying to open /proc/self/fd/%d/%s,
before falling back on save_cwd and restore_cwd.
(fdopendir, fstatat, unlinkat): Likewise.
* lib/openat.c (fstatat, unlinkat): Perform the syscall directly,
skipping the save_cwd...restore_cwd overhead, if FILE is absolute.
* lib/openat.c (rpl_openat): Use the promoted type (int), not mode_t,
as second argument to va_arg. Otherwise, some versions of gcc
warn that `if this code is reached, the program will abort'.
Add POSIX ACL support
* lib/acl.h (copy_acl, set_acl): Add declarations.
* lib/acl.c (acl_entries): Add fallback implementation for POSIX ACL
systems other than Linux.
(chmod_or_fchmod): New function: use fchmod when possible,
and chmod otherwise.
(file_has_acl): Add a POSIX ACL implementation, with a
Linux-specific subcase.
(copy_acl): Add: copy an acl and S_ISUID, S_ISGID, and
S_ISVTX from one file to another. Fall back to fchmod/chmod when
acls are unsupported.
(set_acl): Add: set a file's acl and S_ISUID, S_ISGID, and
S_ISVTX to a defined value. Fall back to fchmod/chmod when acls
are unsupported.
* m4/lib-ignore.m4: New file.
* m4/lchmod.m4: New file.
* m4/chdir-safer.m4: New file.
* m4/openat.m4 (gl_FUNC_OPENAT): Require and compile mkdirat.c.
Require openat-priv.h.
* m4/acl.m4 (AC_FUNC_ACL): Add POSIX ACL and Linux-specific acl tests.
author | Paul Eggert <eggert@cs.ucla.edu> |
---|---|
date | Mon, 09 Jan 2006 23:13:56 +0000 |
parents | fdd622fe6d47 |
children | bbdf9204a185 |
rev | line source |
---|---|
5907 | 1 /* mkdir-p.c -- Ensure that a directory and its parents exist. |
2 | |
6527 | 3 Copyright (C) 1990, 1997, 1998, 1999, 2000, 2002, 2003, 2004, 2005, 2006 |
5907 | 4 Free Software Foundation, Inc. |
5 | |
6 This program is free software; you can redistribute it and/or modify | |
7 it under the terms of the GNU General Public License as published by | |
8 the Free Software Foundation; either version 2, or (at your option) | |
9 any later version. | |
10 | |
11 This program is distributed in the hope that it will be useful, | |
12 but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 GNU General Public License for more details. | |
15 | |
16 You should have received a copy of the GNU General Public License | |
17 along with this program; if not, write to the Free Software Foundation, | |
18 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ | |
19 | |
20 /* Written by David MacKenzie <djm@gnu.ai.mit.edu> and Jim Meyering. */ | |
21 | |
6259
96c32553b4c6
Use a consistent style for including <config.h>.
Paul Eggert <eggert@cs.ucla.edu>
parents:
5948
diff
changeset
|
22 #ifdef HAVE_CONFIG_H |
5907 | 23 # include <config.h> |
24 #endif | |
25 | |
26 #include "mkdir-p.h" | |
27 | |
28 #include <alloca.h> | |
29 | |
30 #include <stdio.h> | |
31 #include <sys/types.h> | |
32 #include <sys/stat.h> | |
6271
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
33 #include <unistd.h> |
5907 | 34 |
35 #include <stdlib.h> | |
36 #include <errno.h> | |
37 #include <string.h> | |
38 | |
39 #include "gettext.h" | |
40 #define _(msgid) gettext (msgid) | |
41 | |
6527 | 42 #include "chdir-safer.h" |
5907 | 43 #include "dirname.h" |
44 #include "error.h" | |
6527 | 45 #include "lchmod.h" |
46 #include "lchown.h" | |
5907 | 47 #include "quote.h" |
6527 | 48 #include "save-cwd.h" |
5907 | 49 #include "stat-macros.h" |
50 | |
51 /* Ensure that the directory ARG exists. | |
52 | |
53 Create any leading directories that don't already exist, with | |
54 permissions PARENT_MODE. | |
55 If the last element of ARG does not exist, create it as | |
56 a new directory with permissions MODE. | |
57 If OWNER and GROUP are non-negative, use them to set the UID and GID of | |
58 any created directories. | |
59 If VERBOSE_FMT_STRING is nonzero, use it as a printf format | |
60 string for printing a message after successfully making a directory, | |
61 with the name of the directory that was just made as an argument. | |
62 If PRESERVE_EXISTING is true and ARG is an existing directory, | |
63 then do not attempt to set its permissions and ownership. | |
64 | |
6271
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
65 Set *CWD_ERRNO to a (nonzero) error number if this |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
66 function has changed the current working directory and is unable to |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
67 restore it to its initial state. Do not change |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
68 *CWD_ERRNO otherwise. |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
69 |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
70 Return true iff ARG exists as a directory with the proper ownership |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
71 and permissions when done. Note that this function returns true |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
72 even when it fails to return to the initial working directory. */ |
5907 | 73 |
74 bool | |
75 make_dir_parents (char const *arg, | |
76 mode_t mode, | |
77 mode_t parent_mode, | |
78 uid_t owner, | |
79 gid_t group, | |
80 bool preserve_existing, | |
6271
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
81 char const *verbose_fmt_string, |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
82 int *cwd_errno) |
5907 | 83 { |
84 struct stat stats; | |
85 bool retval = true; | |
6271
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
86 bool do_chdir = false; /* Whether to chdir before each mkdir. */ |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
87 struct saved_cwd cwd; |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
88 bool cwd_problem = false; |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
89 char const *fixup_permissions_dir = NULL; |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
90 char const *full_dir = arg; |
5907 | 91 |
6271
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
92 struct ptr_list |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
93 { |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
94 char *dirname_end; |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
95 struct ptr_list *next; |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
96 }; |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
97 struct ptr_list *leading_dirs = NULL; |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
98 |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
99 if (stat (arg, &stats) == 0) |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
100 { |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
101 if (! S_ISDIR (stats.st_mode)) |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
102 { |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
103 error (0, 0, _("%s exists but is not a directory"), quote (arg)); |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
104 return false; |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
105 } |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
106 |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
107 if (!preserve_existing) |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
108 fixup_permissions_dir = arg; |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
109 } |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
110 else if (errno != ENOENT || !*arg) |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
111 { |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
112 error (0, errno, "%s", quote (arg)); |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
113 return false; |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
114 } |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
115 else |
5907 | 116 { |
117 char *slash; | |
118 mode_t tmp_mode; /* Initial perms for leading dirs. */ | |
119 bool re_protect; /* Should leading dirs be unwritable? */ | |
120 char *basename_dir; | |
121 char *dir; | |
122 | |
123 /* Temporarily relax umask in case it's overly restrictive. */ | |
124 mode_t oldmask = umask (0); | |
125 | |
126 /* Make a copy of ARG that we can scribble NULs on. */ | |
6527 | 127 dir = alloca (strlen (arg) + 1); |
5907 | 128 strcpy (dir, arg); |
129 strip_trailing_slashes (dir); | |
6271
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
130 full_dir = dir; |
5907 | 131 |
6527 | 132 /* If leading directories shouldn't be readable, writable or executable, |
5907 | 133 or should have set[ug]id or sticky bits set and we are setting |
134 their owners, we need to fix their permissions after making them. */ | |
6527 | 135 if (((parent_mode & S_IRWXU) != S_IRWXU) |
5907 | 136 || ((owner != (uid_t) -1 || group != (gid_t) -1) |
137 && (parent_mode & (S_ISUID | S_ISGID | S_ISVTX)) != 0)) | |
138 { | |
139 tmp_mode = S_IRWXU; | |
140 re_protect = true; | |
141 } | |
142 else | |
143 { | |
144 tmp_mode = parent_mode; | |
145 re_protect = false; | |
146 } | |
147 | |
148 /* If we can record the current working directory, we may be able | |
149 to do the chdir optimization. */ | |
150 do_chdir = (save_cwd (&cwd) == 0); | |
151 | |
152 /* If we've saved the cwd and DIR is an absolute file name, | |
153 we must chdir to `/' in order to enable the chdir optimization. | |
154 So if chdir ("/") fails, turn off the optimization. */ | |
155 if (do_chdir && dir[0] == '/') | |
156 { | |
157 /* POSIX says "//" might be special, so chdir to "//" if the | |
158 file name starts with exactly two slashes. */ | |
159 char const *root = "//" + (dir[1] != '/' || dir[2] == '/'); | |
160 if (chdir (root) != 0) | |
6271
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
161 { |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
162 free_cwd (&cwd); |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
163 do_chdir = false; |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
164 } |
5907 | 165 } |
166 | |
167 slash = dir; | |
168 | |
169 /* Skip over leading slashes. */ | |
170 while (*slash == '/') | |
171 slash++; | |
172 | |
6271
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
173 while (true) |
5907 | 174 { |
6527 | 175 bool dir_known_to_exist; |
176 int mkdir_errno; | |
177 | |
5907 | 178 /* slash points to the leftmost unprocessed component of dir. */ |
179 basename_dir = slash; | |
180 | |
181 slash = strchr (slash, '/'); | |
182 if (slash == NULL) | |
183 break; | |
184 | |
185 /* If we're *not* doing chdir before each mkdir, then we have to refer | |
186 to the target using the full (multi-component) directory name. */ | |
187 if (!do_chdir) | |
188 basename_dir = dir; | |
189 | |
190 *slash = '\0'; | |
6527 | 191 dir_known_to_exist = (mkdir (basename_dir, tmp_mode) == 0); |
192 mkdir_errno = errno; | |
193 | |
194 if (dir_known_to_exist) | |
5907 | 195 { |
196 if (verbose_fmt_string) | |
197 error (0, 0, verbose_fmt_string, quote (dir)); | |
198 | |
199 if ((owner != (uid_t) -1 || group != (gid_t) -1) | |
6527 | 200 && lchown (basename_dir, owner, group) |
5907 | 201 #if defined AFS && defined EPERM |
202 && errno != EPERM | |
203 #endif | |
204 ) | |
205 { | |
206 error (0, errno, _("cannot change owner and/or group of %s"), | |
207 quote (dir)); | |
6271
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
208 retval = false; |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
209 break; |
5907 | 210 } |
211 | |
212 if (re_protect) | |
213 { | |
6527 | 214 struct ptr_list *new = alloca (sizeof *new); |
5907 | 215 new->dirname_end = slash; |
216 new->next = leading_dirs; | |
217 leading_dirs = new; | |
218 } | |
219 } | |
220 | |
221 /* If we were able to save the initial working directory, | |
222 then we can use chdir to change into each directory before | |
223 creating an entry in that directory. This avoids making | |
6271
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
224 mkdir process O(n^2) file name components. */ |
6527 | 225 if (do_chdir) |
5907 | 226 { |
6527 | 227 /* If we know that basename_dir is a directory (because we've |
228 just created it), then ensure that when we change to it, | |
229 that final component is not a symlink. Otherwise, we must | |
230 accept the possibility that basename_dir is a preexisting | |
231 symlink-to-directory and chdir through the symlink. */ | |
232 if ((dir_known_to_exist | |
233 ? chdir_no_follow (basename_dir) | |
234 : chdir (basename_dir)) == 0) | |
235 dir_known_to_exist = true; | |
236 else if (dir_known_to_exist) | |
237 { | |
238 error (0, errno, _("cannot chdir to directory %s"), | |
239 quote (dir)); | |
240 retval = false; | |
241 break; | |
242 } | |
243 } | |
244 else if (!dir_known_to_exist) | |
245 dir_known_to_exist = (stat (basename_dir, &stats) == 0 | |
246 && S_ISDIR (stats.st_mode)); | |
247 | |
248 if (!dir_known_to_exist) | |
249 { | |
250 error (0, mkdir_errno, _("cannot create directory %s"), | |
5907 | 251 quote (dir)); |
6271
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
252 retval = false; |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
253 break; |
5907 | 254 } |
255 | |
256 *slash++ = '/'; | |
257 | |
6271
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
258 /* Avoid unnecessary calls to mkdir when given |
5907 | 259 file names containing multiple adjacent slashes. */ |
260 while (*slash == '/') | |
261 slash++; | |
262 } | |
263 | |
264 if (!do_chdir) | |
265 basename_dir = dir; | |
266 | |
267 /* Done creating leading directories. Restore original umask. */ | |
268 umask (oldmask); | |
269 | |
270 /* We're done making leading directories. | |
271 Create the final component of the file name. */ | |
6271
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
272 if (retval) |
5907 | 273 { |
6527 | 274 bool dir_known_to_exist = (mkdir (basename_dir, mode) == 0); |
275 int mkdir_errno = errno; | |
276 struct stat sbuf; | |
277 | |
278 if ( ! dir_known_to_exist) | |
279 dir_known_to_exist = (stat (basename_dir, &sbuf) == 0 | |
280 && S_ISDIR (sbuf.st_mode)); | |
281 | |
282 if ( ! dir_known_to_exist) | |
6271
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
283 { |
6527 | 284 error (0, mkdir_errno, |
285 _("cannot create directory %s"), quote (dir)); | |
6271
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
286 retval = false; |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
287 } |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
288 else |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
289 { |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
290 if (verbose_fmt_string) |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
291 error (0, 0, verbose_fmt_string, quote (dir)); |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
292 fixup_permissions_dir = basename_dir; |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
293 } |
5907 | 294 } |
6271
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
295 } |
5907 | 296 |
6271
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
297 if (fixup_permissions_dir) |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
298 { |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
299 /* chown must precede chmod because on some systems, |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
300 chown clears the set[ug]id bits for non-superusers, |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
301 resulting in incorrect permissions. |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
302 On System V, users can give away files with chown and then not |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
303 be able to chmod them. So don't give files away. */ |
5907 | 304 |
305 if (owner != (uid_t) -1 || group != (gid_t) -1) | |
306 { | |
6527 | 307 if (lchown (fixup_permissions_dir, owner, group) != 0 |
5907 | 308 #ifdef AFS |
309 && errno != EPERM | |
310 #endif | |
311 ) | |
312 { | |
313 error (0, errno, _("cannot change owner and/or group of %s"), | |
6271
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
314 quote (full_dir)); |
5907 | 315 retval = false; |
316 } | |
317 } | |
318 | |
319 /* The above chown may have turned off some permission bits in MODE. | |
320 Another reason we may have to use chmod here is that mkdir(2) is | |
321 required to honor only the file permission bits. In particular, | |
322 it need not honor the `special' bits, so if MODE includes any | |
323 special bits, set them here. */ | |
6527 | 324 if ((mode & ~S_IRWXUGO) && lchmod (fixup_permissions_dir, mode) != 0) |
5907 | 325 { |
326 error (0, errno, _("cannot change permissions of %s"), | |
6271
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
327 quote (full_dir)); |
5907 | 328 retval = false; |
329 } | |
330 } | |
6271
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
331 |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
332 if (do_chdir) |
5907 | 333 { |
6271
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
334 if (restore_cwd (&cwd) != 0) |
5907 | 335 { |
6271
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
336 *cwd_errno = errno; |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
337 cwd_problem = true; |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
338 } |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
339 free_cwd (&cwd); |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
340 } |
5907 | 341 |
6271
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
342 /* If the mode for leading directories didn't include owner "wx" |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
343 privileges, reset their protections to the correct value. */ |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
344 for (; leading_dirs != NULL; leading_dirs = leading_dirs->next) |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
345 { |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
346 leading_dirs->dirname_end[0] = '\0'; |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
347 if ((cwd_problem && *full_dir != '/') |
6527 | 348 || lchmod (full_dir, parent_mode) != 0) |
6271
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
349 { |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
350 error (0, (cwd_problem ? 0 : errno), |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
351 _("cannot change permissions of %s"), quote (full_dir)); |
fdd622fe6d47
* mkdir-p.c (ENOSYS): Define to EEXIST if not defined.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
352 retval = false; |
5907 | 353 } |
354 } | |
355 | |
356 return retval; | |
357 } |