OSDN Git Service

* cygtls.cc (_cygtls::remove): Eliminate compiler warning.
[pf3gnuchains/pf3gnuchains4x.git] / winsup / cygwin / dir.cc
1 /* dir.cc: Posix directory-related routines
2
3    Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
4
5 This file is part of Cygwin.
6
7 This software is a copyrighted work licensed under the terms of the
8 Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
9 details. */
10
11 #include "winsup.h"
12 #include <unistd.h>
13 #include <stdlib.h>
14 #include <sys/stat.h>
15
16 #define _COMPILING_NEWLIB
17 #include <dirent.h>
18
19 #include "pinfo.h"
20 #include "cygerrno.h"
21 #include "security.h"
22 #include "path.h"
23 #include "fhandler.h"
24 #include "dtable.h"
25 #include "cygheap.h"
26
27 extern "C" int
28 dirfd (DIR *dir)
29 {
30   if (check_null_invalid_struct_errno (dir))
31     return -1;
32   if (dir->__d_cookie != __DIRENT_COOKIE)
33     {
34       set_errno (EBADF);
35       syscall_printf ("-1 = dirfd (%p)", dir);
36       return -1;
37     }
38   return dir->__d_dirent->d_fd;
39 }
40
41 /* opendir: POSIX 5.1.2.1 */
42 extern "C" DIR *
43 opendir (const char *name)
44 {
45   fhandler_base *fh;
46   DIR *res;
47
48   fh = build_fh_name (name, NULL, PC_SYM_FOLLOW | PC_FULL);
49   if (!fh)
50     res = NULL;
51   else if (fh->exists ())
52       res = fh->opendir ();
53   else
54     {
55       set_errno (ENOENT);
56       res = NULL;
57     }
58
59   if (res)
60     res->__flags = 0;
61   else if (fh)
62     delete fh;
63   return res;
64 }
65
66 /* readdir: POSIX 5.1.2.1 */
67 extern "C" struct dirent *
68 readdir (DIR *dir)
69 {
70   if (check_null_invalid_struct_errno (dir))
71     return NULL;
72
73   if (dir->__d_cookie != __DIRENT_COOKIE)
74     {
75       set_errno (EBADF);
76       syscall_printf ("%p = readdir (%p)", NULL, dir);
77       return NULL;
78     }
79
80   dirent *res = ((fhandler_base *) dir->__fh)->readdir (dir);
81
82   if (!res)
83     {
84       if (!(dir->__flags & dirent_saw_dot))
85         {
86           res = dir->__d_dirent;
87           strcpy (res->d_name, ".");
88           dir->__flags |= dirent_saw_dot;
89           dir->__d_position++;
90         }
91       else if (!(dir->__flags & dirent_saw_dot_dot))
92         {
93           res = dir->__d_dirent;
94           strcpy (res->d_name, "..");
95           dir->__flags |= dirent_saw_dot_dot;
96           dir->__d_position++;
97         }
98     }
99
100   if (res)
101     {
102       /* Compute d_ino by combining filename hash with the directory hash
103          (which was stored in dir->__d_dirhash when opendir was called). */
104       if (res->d_name[0] == '.')
105         {
106           if (res->d_name[1] == '\0')
107             {
108               dir->__d_dirent->d_ino = dir->__d_dirhash;
109               dir->__flags |= dirent_saw_dot;
110             }
111           else if (res->d_name[1] != '.' || res->d_name[2] != '\0')
112             goto hashit;
113           else
114             {
115               dir->__flags |= dirent_saw_dot_dot;
116               char *p, up[strlen (dir->__d_dirname) + 1];
117               strcpy (up, dir->__d_dirname);
118               if (!(p = strrchr (up, '\\')))
119                 goto hashit;
120               *p = '\0';
121               if (!(p = strrchr (up, '\\')))
122                 dir->__d_dirent->d_ino = hash_path_name (0, ".");
123               else
124                 {
125                   *p = '\0';
126                   dir->__d_dirent->d_ino = hash_path_name (0, up);
127                 }
128             }
129         }
130       else
131         {
132       hashit:
133           __ino64_t dino = hash_path_name (dir->__d_dirhash, "\\");
134           dir->__d_dirent->d_ino = hash_path_name (dino, res->d_name);
135         }
136       res->__ino32 = dir->__d_dirent->d_ino;    // for legacy applications
137     }
138   return res;
139 }
140
141 extern "C" _off64_t
142 telldir64 (DIR *dir)
143 {
144   if (check_null_invalid_struct_errno (dir))
145     return -1;
146
147   if (dir->__d_cookie != __DIRENT_COOKIE)
148     return 0;
149   return ((fhandler_base *) dir->__fh)->telldir (dir);
150 }
151
152 /* telldir */
153 extern "C" _off_t
154 telldir (DIR *dir)
155 {
156   return telldir64 (dir);
157 }
158
159 extern "C" void
160 seekdir64 (DIR *dir, _off64_t loc)
161 {
162   if (check_null_invalid_struct_errno (dir))
163     return;
164
165   if (dir->__d_cookie != __DIRENT_COOKIE)
166     return;
167   dir->__flags = 0;
168   return ((fhandler_base *) dir->__fh)->seekdir (dir, loc);
169 }
170
171 /* seekdir */
172 extern "C" void
173 seekdir (DIR *dir, _off_t loc)
174 {
175   seekdir64 (dir, (_off64_t)loc);
176 }
177
178 /* rewinddir: POSIX 5.1.2.1 */
179 extern "C" void
180 rewinddir (DIR *dir)
181 {
182   if (check_null_invalid_struct_errno (dir))
183     return;
184
185   if (dir->__d_cookie != __DIRENT_COOKIE)
186     return;
187   dir->__flags = 0;
188   return ((fhandler_base *) dir->__fh)->rewinddir (dir);
189 }
190
191 /* closedir: POSIX 5.1.2.1 */
192 extern "C" int
193 closedir (DIR *dir)
194 {
195   if (check_null_invalid_struct_errno (dir))
196     return -1;
197
198   if (dir->__d_cookie != __DIRENT_COOKIE)
199     {
200       set_errno (EBADF);
201       syscall_printf ("-1 = closedir (%p)", dir);
202       return -1;
203     }
204
205   /* Reset the marker in case the caller tries to use `dir' again.  */
206   dir->__d_cookie = 0;
207
208   int res = ((fhandler_base *) dir->__fh)->closedir (dir);
209
210   cygheap->fdtab.release (dir->__d_dirent->d_fd);
211
212   free (dir->__d_dirname);
213   free (dir->__d_dirent);
214   free (dir);
215   syscall_printf ("%d = closedir (%p)", res);
216   return res;
217 }
218
219 /* mkdir: POSIX 5.4.1.1 */
220 extern "C" int
221 mkdir (const char *dir, mode_t mode)
222 {
223   int res = -1;
224   SECURITY_ATTRIBUTES sa = sec_none_nih;
225   security_descriptor sd;
226
227   path_conv real_dir (dir, PC_SYM_NOFOLLOW | PC_WRITABLE);
228
229   if (real_dir.error)
230     {
231       set_errno (real_dir.case_clash ? ECASECLASH : real_dir.error);
232       goto done;
233     }
234
235   nofinalslash (real_dir.get_win32 (), real_dir.get_win32 ());
236
237   if (allow_ntsec && real_dir.has_acls ())
238     set_security_attribute (S_IFDIR | ((mode & 07777) & ~cygheap->umask),
239                             &sa, sd);
240
241   if (CreateDirectoryA (real_dir.get_win32 (), &sa))
242     {
243       if (!allow_ntsec && allow_ntea)
244         set_file_attribute (false, NULL, real_dir.get_win32 (),
245                             S_IFDIR | ((mode & 07777) & ~cygheap->umask));
246 #ifdef HIDDEN_DOT_FILES
247       char *c = strrchr (real_dir.get_win32 (), '\\');
248       if ((c && c[1] == '.') || *real_dir.get_win32 () == '.')
249         SetFileAttributes (real_dir.get_win32 (), FILE_ATTRIBUTE_HIDDEN);
250 #endif
251       res = 0;
252     }
253   else
254     __seterrno ();
255
256 done:
257   syscall_printf ("%d = mkdir (%s, %d)", res, dir, mode);
258   return res;
259 }
260
261 /* rmdir: POSIX 5.5.2.1 */
262 extern "C" int
263 rmdir (const char *dir)
264 {
265   int res = -1;
266
267   path_conv real_dir (dir, PC_SYM_NOFOLLOW | PC_FULL | PC_WRITABLE);
268
269   if (real_dir.error)
270     set_errno (real_dir.error);
271   else if (!real_dir.exists ())
272     set_errno (ENOENT);
273   else if  (!real_dir.isdir ())
274     set_errno (ENOTDIR);
275   else
276     {
277       /* Even own directories can't be removed if R/O attribute is set. */
278       if (real_dir.has_attribute (FILE_ATTRIBUTE_READONLY))
279         SetFileAttributes (real_dir,
280                            (DWORD) real_dir & ~FILE_ATTRIBUTE_READONLY);
281
282       for (bool is_cwd = false; ; is_cwd = true)
283         {
284           DWORD err;
285           int rc = RemoveDirectory (real_dir);
286           DWORD att = GetFileAttributes (real_dir);
287
288           /* Sometimes smb indicates failure when it really succeeds, so check for
289              this case specifically. */
290           if (rc || att == INVALID_FILE_ATTRIBUTES)
291             {
292               /* RemoveDirectory on a samba drive doesn't return an error if the
293                  directory can't be removed because it's not empty. Checking for
294                  existence afterwards keeps us informed about success. */
295               if (att == INVALID_FILE_ATTRIBUTES)
296                 {
297                   res = 0;
298                   break;
299                 }
300               err = ERROR_DIR_NOT_EMPTY;
301             }
302           else
303             err = GetLastError ();
304
305           /* This kludge detects if we are attempting to remove the current working
306              directory.  If so, we will move elsewhere to potentially allow the
307              rmdir to succeed.  This means that cygwin's concept of the current working
308              directory != Windows concept but, hey, whaddaregonnado?
309              Note that this will not cause something like the following to work:
310                      $ cd foo
311                      $ rmdir .
312              since the shell will have foo "open" in the above case and so Windows will
313              not allow the deletion. (Actually it does on 9X.)
314              FIXME: A potential workaround for this is for cygwin apps to *never* call
315              SetCurrentDirectory. */
316
317           if (strcasematch (real_dir, cygheap->cwd.win32)
318               && !strcasematch ("c:\\", cygheap->cwd.win32)
319               && !is_cwd
320               && SetCurrentDirectory ("c:\\"))
321             continue;
322
323           /* On 9X ERROR_ACCESS_DENIED is returned
324              if you try to remove a non-empty directory. */
325           if (err == ERROR_ACCESS_DENIED
326               && wincap.access_denied_on_delete ())
327             err = ERROR_DIR_NOT_EMPTY;
328
329           __seterrno_from_win_error (err);
330
331           /* Directory still exists, restore its characteristics. */
332           if (real_dir.has_attribute (FILE_ATTRIBUTE_READONLY))
333             SetFileAttributes (real_dir, real_dir);
334           if (is_cwd)
335             SetCurrentDirectory (real_dir);
336           break;
337         }
338     }
339
340   syscall_printf ("%d = rmdir (%s)", res, dir);
341   return res;
342 }