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