Tizen 2.0 Release
[external/tizen-coreutils.git] / lib / mkancesdirs.c
1 /* Make a file's ancestor directories.
2
3    Copyright (C) 2006 Free Software Foundation, Inc.
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2, or (at your option)
8    any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software Foundation,
17    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
18
19 /* Written by Paul Eggert.  */
20
21 #include <config.h>
22
23 #include "mkancesdirs.h"
24
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <fcntl.h>
28
29 #include <errno.h>
30 #include <unistd.h>
31
32 #include "dirname.h"
33 #include "savewd.h"
34
35 /* Ensure that the ancestor directories of FILE exist, using an
36    algorithm that should work even if two processes execute this
37    function in parallel.  Modify FILE as necessary to access the
38    ancestor directories, but restore FILE to an equivalent value
39    if successful.
40
41    WD points to the working directory, using the conventions of
42    savewd.
43
44    Create any ancestor directories that don't already exist, by
45    invoking MAKE_DIR (FILE, COMPONENT, MAKE_DIR_ARG).  This function
46    should return 0 if successful and the resulting directory is
47    readable, 1 if successful but the resulting directory might not be
48    readable, -1 (setting errno) otherwise.  If COMPONENT is relative,
49    it is relative to the temporary working directory, which may differ
50    from *WD.
51
52    Ordinarily MAKE_DIR is executed with the working directory changed
53    to reflect the already-made prefix, and mkancesdirs returns with
54    the working directory changed a prefix of FILE.  However, if the
55    initial working directory cannot be saved in a file descriptor,
56    MAKE_DIR is invoked in a subprocess and this function returns in
57    both the parent and child process, so the caller should not assume
58    any changed state survives other than the EXITMAX component of WD,
59    and the caller should take care that the parent does not attempt to
60    do the work that the child is doing.
61
62    If successful and if this process can go ahead and create FILE,
63    return the length of the prefix of FILE that has already been made.
64    If successful so far but a child process is doing the actual work,
65    return -2.  If unsuccessful, return -1 and set errno.  */
66
67 ptrdiff_t
68 mkancesdirs (char *file, struct savewd *wd,
69              int (*make_dir) (char const *, char const *, void *),
70              void *make_dir_arg)
71 {
72   /* Address of the previous directory separator that follows an
73      ordinary byte in a file name in the left-to-right scan, or NULL
74      if no such separator precedes the current location P.  */
75   char *sep = NULL;
76
77   /* Address of the leftmost file name component that has not yet
78      been processed.  */
79   char *component = file;
80
81   char *p = file + FILE_SYSTEM_PREFIX_LEN (file);
82   char c;
83   bool made_dir = false;
84
85   /* Scan forward through FILE, creating and chdiring into directories
86      along the way.  Try MAKE_DIR before chdir, so that the procedure
87      works even when two or more processes are executing it in
88      parallel.  Isolate each file name component by having COMPONENT
89      point to its start and SEP point just after its end.  */
90
91   while ((c = *p++))
92     if (ISSLASH (*p))
93       {
94         if (! ISSLASH (c))
95           sep = p;
96       }
97     else if (ISSLASH (c) && *p && sep)
98       {
99         /* Don't bother to make or test for "." since it does not
100            affect the algorithm.  */
101         if (! (sep - component == 1 && component[0] == '.'))
102           {
103             int make_dir_errno = 0;
104             int savewd_chdir_options = 0;
105             int chdir_result;
106
107             /* Temporarily modify FILE to isolate this file name
108                component.  */
109             *sep = '\0';
110
111             /* Invoke MAKE_DIR on this component, except don't bother
112                with ".." since it must exist if its "parent" does.  */
113             if (sep - component == 2
114                 && component[0] == '.' && component[1] == '.')
115               made_dir = false;
116             else
117               switch (make_dir (file, component, make_dir_arg))
118                 {
119                 case -1:
120                   make_dir_errno = errno;
121                   break;
122
123                 case 0:
124                   savewd_chdir_options |= SAVEWD_CHDIR_READABLE;
125                   /* Fall through.  */
126                 case 1:
127                   made_dir = true;
128                   break;
129                 }
130
131             if (made_dir)
132               savewd_chdir_options |= SAVEWD_CHDIR_NOFOLLOW;
133
134             chdir_result =
135               savewd_chdir (wd, component, savewd_chdir_options, NULL);
136
137             /* Undo the temporary modification to FILE, unless there
138                was a failure.  */
139             if (chdir_result != -1)
140               *sep = '/';
141
142             if (chdir_result != 0)
143               {
144                 if (make_dir_errno != 0 && errno == ENOENT)
145                   errno = make_dir_errno;
146                 return chdir_result;
147               }
148           }
149
150         component = p;
151       }
152
153   return component - file;
154 }