OSDN Git Service

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