OSDN Git Service

* winsup.h: Eliminate inclusion of most of the cygwin .h files. Use .h files
[pf3gnuchains/sourceware.git] / winsup / cygwin / dir.cc
1 /* dir.cc: Posix directory-related routines
2
3    Copyright 1996, 1997, 1998, 1999, 2000 Cygnus Solutions.
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 "sync.h"
21 #include "sigproc.h"
22 #include "pinfo.h"
23 #include "cygerrno.h"
24 #include "fhandler.h"
25 #include "path.h"
26
27 /* Cygwin internal */
28 /* Return whether the directory of a file is writable.  Return 1 if it
29    is.  Otherwise, return 0, and set errno appropriately.  */
30 int __stdcall
31 writable_directory (const char *file)
32 {
33 #if 0
34   char dir[strlen (file) + 1];
35
36   strcpy (dir, file);
37
38   const char *usedir;
39   char *slash = strrchr (dir, '\\');
40   if (slash == NULL)
41     usedir = ".";
42   else if (slash == dir)
43     {
44       usedir = "\\";
45     }
46   else
47     {
48       *slash = '\0';
49       usedir = dir;
50     }
51
52   int acc = access (usedir, W_OK);
53
54   return acc == 0;
55 #else
56   return 1;
57 #endif
58 }
59
60 /* opendir: POSIX 5.1.2.1 */
61 extern "C" DIR *
62 opendir (const char *dirname)
63 {
64   int len;
65   DIR *dir;
66   DIR *res = 0;
67   struct stat statbuf;
68
69   path_conv real_dirname (dirname, PC_SYM_FOLLOW | PC_FULL);
70
71   if (real_dirname.error)
72     {
73       set_errno (real_dirname.error);
74       goto failed;
75     }
76
77   if (stat (myself->rootlen ? dirname : real_dirname.get_win32 (),
78             &statbuf) == -1)
79     goto failed;
80
81   if (!(statbuf.st_mode & S_IFDIR))
82     {
83       set_errno (ENOTDIR);
84       goto failed;
85     }
86
87   len = strlen (real_dirname.get_win32 ());
88   if (len > MAX_PATH - 3)
89     {
90       set_errno (ENAMETOOLONG);
91       goto failed;
92     }
93
94   if ((dir = (DIR *) malloc (sizeof (DIR))) == NULL)
95     {
96       set_errno (ENOMEM);
97       goto failed;
98     }
99   if ((dir->__d_dirname = (char *) malloc (len + 3)) == NULL)
100     {
101       free (dir);
102       set_errno (ENOMEM);
103       goto failed;
104     }
105   if ((dir->__d_dirent =
106             (struct dirent *) malloc (sizeof (struct dirent))) == NULL)
107     {
108       free (dir->__d_dirname);
109       free (dir);
110       set_errno (ENOMEM);
111       goto failed;
112     }
113   strcpy (dir->__d_dirname, real_dirname.get_win32 ());
114   /* FindFirstFile doesn't seem to like duplicate /'s. */
115   len = strlen (dir->__d_dirname);
116   if (len == 0 || SLASH_P (dir->__d_dirname[len - 1]))
117     strcat (dir->__d_dirname, "*");
118   else
119     strcat (dir->__d_dirname, "\\*");  /**/
120   dir->__d_cookie = __DIRENT_COOKIE;
121   dir->__d_u.__d_data.__handle = INVALID_HANDLE_VALUE;
122   dir->__d_position = 0;
123   dir->__d_dirhash = statbuf.st_ino;
124
125   res = dir;
126
127 failed:
128   syscall_printf ("%p = opendir (%s)", res, dirname);
129   return res;
130 }
131
132 /* readdir: POSIX 5.1.2.1 */
133 extern "C" struct dirent *
134 readdir (DIR * dir)
135 {
136   WIN32_FIND_DATA buf;
137   HANDLE handle;
138   struct dirent *res = NULL;
139
140   if (dir->__d_cookie != __DIRENT_COOKIE)
141     {
142       set_errno (EBADF);
143       syscall_printf ("%p = readdir (%p)", res, dir);
144       return res;
145     }
146
147   if (dir->__d_u.__d_data.__handle == INVALID_HANDLE_VALUE)
148     {
149       handle = FindFirstFileA (dir->__d_dirname, &buf);
150       DWORD lasterr = GetLastError ();
151       dir->__d_u.__d_data.__handle = handle;
152       if (handle == INVALID_HANDLE_VALUE && (lasterr != ERROR_NO_MORE_FILES))
153         {
154           seterrno_from_win_error (__FILE__, __LINE__, lasterr);
155           return res;
156         }
157     }
158   else if (!FindNextFileA (dir->__d_u.__d_data.__handle, &buf))
159     {
160       DWORD lasterr = GetLastError ();
161       (void) FindClose (dir->__d_u.__d_data.__handle);
162       dir->__d_u.__d_data.__handle = INVALID_HANDLE_VALUE;
163       /* POSIX says you shouldn't set errno when readdir can't
164          find any more files; so, if another error we leave it set. */
165       if (lasterr != ERROR_NO_MORE_FILES)
166           seterrno_from_win_error (__FILE__, __LINE__, lasterr);
167       syscall_printf ("%p = readdir (%p)", res, dir);
168       return res;
169     }
170
171   /* We get here if `buf' contains valid data.  */
172   strcpy (dir->__d_dirent->d_name, buf.cFileName);
173
174   /* Compute d_ino by combining filename hash with the directory hash
175      (which was stored in dir->__d_dirhash when opendir was called). */
176   if (buf.cFileName[0] == '.')
177     {
178       if (buf.cFileName[1] == '\0')
179         dir->__d_dirent->d_ino = dir->__d_dirhash;
180       else if (buf.cFileName[1] != '.' || buf.cFileName[2] != '\0')
181         goto hashit;
182       else
183         {
184           char *p, up[strlen (dir->__d_dirname) + 1];
185           strcpy (up, dir->__d_dirname);
186           if (!(p = strrchr (up, '\\')))
187             goto hashit;
188           *p = '\0';
189           if (!(p = strrchr (up, '\\')))
190             dir->__d_dirent->d_ino = hash_path_name (0, ".");
191           else
192             {
193               *p = '\0';
194               dir->__d_dirent->d_ino = hash_path_name (0, up);
195             }
196         }
197     }
198   else
199     {
200   hashit:
201       ino_t dino = hash_path_name (dir->__d_dirhash, "\\");
202       dir->__d_dirent->d_ino = hash_path_name (dino, buf.cFileName);
203     }
204
205   ++dir->__d_position;
206   res = dir->__d_dirent;
207   syscall_printf ("%p = readdir (%p) (%s)",
208                   &dir->__d_dirent, dir, buf.cFileName);
209   return res;
210 }
211
212 /* telldir */
213 extern "C" off_t
214 telldir (DIR * dir)
215 {
216   if (dir->__d_cookie != __DIRENT_COOKIE)
217     return 0;
218   return dir->__d_position;
219 }
220
221 /* seekdir */
222 extern "C" void
223 seekdir (DIR * dir, off_t loc)
224 {
225   if (dir->__d_cookie != __DIRENT_COOKIE)
226     return;
227   rewinddir (dir);
228   while (loc > dir->__d_position)
229     if (! readdir (dir))
230       break;
231 }
232
233 /* rewinddir: POSIX 5.1.2.1 */
234 extern "C" void
235 rewinddir (DIR * dir)
236 {
237   syscall_printf ("rewinddir (%p)", dir);
238
239   if (dir->__d_cookie != __DIRENT_COOKIE)
240     return;
241   if (dir->__d_u.__d_data.__handle != INVALID_HANDLE_VALUE)
242     {
243       (void) FindClose (dir->__d_u.__d_data.__handle);
244       dir->__d_u.__d_data.__handle = INVALID_HANDLE_VALUE;
245       dir->__d_position = 0;
246     }
247 }
248
249 /* closedir: POSIX 5.1.2.1 */
250 extern "C" int
251 closedir (DIR * dir)
252 {
253   if (dir->__d_cookie != __DIRENT_COOKIE)
254     {
255       set_errno (EBADF);
256       syscall_printf ("-1 = closedir (%p)", dir);
257       return -1;
258     }
259
260   if (dir->__d_u.__d_data.__handle != INVALID_HANDLE_VALUE &&
261       FindClose (dir->__d_u.__d_data.__handle) == 0)
262     {
263       __seterrno ();
264       syscall_printf ("-1 = closedir (%p)", dir);
265       return -1;
266     }
267
268   /* Reset the marker in case the caller tries to use `dir' again.  */
269   dir->__d_cookie = 0;
270
271   free (dir->__d_dirname);
272   free (dir->__d_dirent);
273   free (dir);
274   syscall_printf ("0 = closedir (%p)", dir);
275   return 0;
276 }
277
278 /* mkdir: POSIX 5.4.1.1 */
279 extern "C" int
280 mkdir (const char *dir, mode_t mode)
281 {
282   int res = -1;
283
284   path_conv real_dir (dir, PC_SYM_NOFOLLOW);
285
286   if (real_dir.error)
287     {
288       set_errno (real_dir.error);
289       goto done;
290     }
291
292   nofinalslash(real_dir.get_win32 (), real_dir.get_win32 ());
293   if (! writable_directory (real_dir.get_win32 ()))
294     goto done;
295
296   if (CreateDirectoryA (real_dir.get_win32 (), 0))
297     {
298       set_file_attribute (real_dir.has_acls (), real_dir.get_win32 (),
299                           S_IFDIR | ((mode & 0777) & ~myself->umask));
300       res = 0;
301     }
302   else
303     __seterrno ();
304
305 done:
306   syscall_printf ("%d = mkdir (%s, %d)", res, dir, mode);
307   return res;
308 }
309
310 /* rmdir: POSIX 5.5.2.1 */
311 extern "C" int
312 rmdir (const char *dir)
313 {
314   int res = -1;
315
316   path_conv real_dir (dir, PC_SYM_NOFOLLOW);
317
318   if (real_dir.error)
319     {
320       set_errno (real_dir.error);
321       goto done;
322     }
323
324   if (RemoveDirectoryA (real_dir.get_win32 ()))
325     {
326       /* RemoveDirectory on a samba drive doesn't return an error if the
327          directory can't be removed because it's not empty. Checking for
328          existence afterwards keeps us informed about success. */
329       if (GetFileAttributesA (real_dir.get_win32 ()) != (DWORD) -1)
330         set_errno (ENOTEMPTY);
331       else
332         res = 0;
333     }
334   else if (GetLastError() == ERROR_ACCESS_DENIED)
335     {
336       /* Under Windows 9X or on a samba share, ERROR_ACCESS_DENIED is
337          returned if you try to remove a file. On 9X the same error is
338          returned if you try to remove a non-empty directory. */
339      if (real_dir.file_attributes () != (DWORD) -1 &&
340          !(real_dir.file_attributes () & FILE_ATTRIBUTE_DIRECTORY))
341        set_errno (ENOTDIR);
342      else if (os_being_run != winNT)
343        set_errno (ENOTEMPTY);
344      else
345        __seterrno ();
346     }
347   else
348     __seterrno ();
349
350 done:
351   syscall_printf ("%d = rmdir (%s)", res, dir);
352   return res;
353 }