]>
Commit | Line | Data |
---|---|---|
7903ee69 CF |
1 | /* fhandler_disk_file.cc |
2 | ||
e136dbc2 | 3 | Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003 Red Hat, Inc. |
7903ee69 CF |
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" | |
7903ee69 CF |
12 | #include <unistd.h> |
13 | #include <stdlib.h> | |
14 | #include <sys/cygwin.h> | |
15 | #include <signal.h> | |
16 | #include "cygerrno.h" | |
17 | #include "perprocess.h" | |
18 | #include "security.h" | |
19 | #include "cygwin/version.h" | |
20 | #include "fhandler.h" | |
21 | #include "path.h" | |
22 | #include "dtable.h" | |
23 | #include "cygheap.h" | |
24 | #include "shared_info.h" | |
7903ee69 CF |
25 | #include "pinfo.h" |
26 | #include <assert.h> | |
75c6a983 | 27 | #include <ctype.h> |
7903ee69 CF |
28 | |
29 | #define _COMPILING_NEWLIB | |
30 | #include <dirent.h> | |
31 | ||
2402700d | 32 | static int __stdcall |
7903ee69 CF |
33 | num_entries (const char *win32_name) |
34 | { | |
35 | WIN32_FIND_DATA buf; | |
36 | HANDLE handle; | |
37 | char buf1[MAX_PATH]; | |
38 | int count = 0; | |
39 | ||
40 | strcpy (buf1, win32_name); | |
41 | int len = strlen (buf1); | |
42 | if (len == 0 || isdirsep (buf1[len - 1])) | |
43 | strcat (buf1, "*"); | |
44 | else | |
45 | strcat (buf1, "/*"); /* */ | |
46 | ||
47 | handle = FindFirstFileA (buf1, &buf); | |
48 | ||
49 | if (handle == INVALID_HANDLE_VALUE) | |
93353aee | 50 | return 2; /* 2 is the minimum number of links to a dir, so... */ |
1aa76ad5 | 51 | int saw_dot = 2; |
7903ee69 CF |
52 | while (FindNextFileA (handle, &buf)) |
53 | { | |
1aa76ad5 CF |
54 | if (buf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) |
55 | count++; | |
56 | if (buf.cFileName[0] == '.' | |
57 | && (buf.cFileName[1] == '\0' | |
58 | || (buf.cFileName[1] == '.' && buf.cFileName[2] == '\0'))) | |
59 | saw_dot--; | |
7903ee69 CF |
60 | } |
61 | FindClose (handle); | |
1aa76ad5 | 62 | return count + saw_dot; |
7903ee69 CF |
63 | } |
64 | ||
2402700d CF |
65 | int __stdcall |
66 | fhandler_disk_file::fstat_by_handle (struct __stat64 *buf, path_conv *pc) | |
67 | { | |
8e74c320 | 68 | int res = 0; |
2402700d CF |
69 | BY_HANDLE_FILE_INFORMATION local; |
70 | ||
71 | /* NT 3.51 seems to have a bug when attempting to get vol serial | |
72 | numbers. This loop gets around this. */ | |
73 | for (int i = 0; i < 2; i++) | |
74 | { | |
75 | if (!(res = GetFileInformationByHandle (get_handle (), &local))) | |
76 | break; | |
77 | if (local.dwVolumeSerialNumber && (long) local.dwVolumeSerialNumber != -1) | |
78 | break; | |
79 | } | |
80 | debug_printf ("%d = GetFileInformationByHandle (%s, %d)", | |
81 | res, get_win32_name (), get_handle ()); | |
82 | if (res == 0) | |
83 | /* GetFileInformationByHandle will fail if it's given stdin/out/err | |
84 | or a pipe*/ | |
85 | { | |
86 | memset (&local, 0, sizeof (local)); | |
87 | local.nFileSizeLow = GetFileSize (get_handle (), &local.nFileSizeHigh); | |
88 | } | |
89 | ||
90 | return fstat_helper (buf, pc, | |
91 | local.ftCreationTime, | |
92 | local.ftLastAccessTime, | |
93 | local.ftLastWriteTime, | |
94 | local.nFileSizeHigh, | |
95 | local.nFileSizeLow, | |
96 | local.nFileIndexHigh, | |
97 | local.nFileIndexLow, | |
98 | local.nNumberOfLinks); | |
99 | } | |
100 | ||
101 | int __stdcall | |
102 | fhandler_disk_file::fstat_by_name (struct __stat64 *buf, path_conv *pc) | |
103 | { | |
104 | int res; | |
105 | HANDLE handle; | |
106 | WIN32_FIND_DATA local; | |
107 | ||
97478fe6 CF |
108 | if (!pc->exists ()) |
109 | { | |
b3e2d035 | 110 | debug_printf ("already determined that pc does not exist"); |
97478fe6 CF |
111 | set_errno (ENOENT); |
112 | res = -1; | |
113 | } | |
c17e8306 | 114 | else if ((handle = FindFirstFile (*pc, &local)) != INVALID_HANDLE_VALUE) |
2402700d | 115 | { |
a80229fd CV |
116 | FindClose (handle); |
117 | res = fstat_helper (buf, pc, | |
118 | local.ftCreationTime, | |
119 | local.ftLastAccessTime, | |
120 | local.ftLastWriteTime, | |
121 | local.nFileSizeHigh, | |
122 | local.nFileSizeLow); | |
2402700d | 123 | } |
c17e8306 PH |
124 | else if (pc->isdir ()) |
125 | { | |
126 | FILETIME ft = {}; | |
127 | res = fstat_helper (buf, pc, ft, ft, ft, 0, 0); | |
128 | } | |
df04ae29 | 129 | else |
c17e8306 PH |
130 | { |
131 | debug_printf ("FindFirstFile failed for '%s', %E", (char *) *pc); | |
132 | __seterrno (); | |
133 | res = -1; | |
134 | } | |
2402700d CF |
135 | return res; |
136 | } | |
137 | ||
138 | int __stdcall | |
acb56175 | 139 | fhandler_disk_file::fstat (struct __stat64 *buf, path_conv *pc) |
7903ee69 CF |
140 | { |
141 | int res = -1; | |
142 | int oret; | |
7903ee69 | 143 | int open_flags = O_RDONLY | O_BINARY | O_DIROPEN; |
2402700d | 144 | bool query_open_already; |
7903ee69 | 145 | |
2402700d | 146 | if (get_io_handle ()) |
5bf785a0 CF |
147 | { |
148 | if (get_nohandle ()) | |
149 | return fstat_by_name (buf, pc); | |
150 | else | |
151 | return fstat_by_handle (buf, pc); | |
152 | } | |
2402700d CF |
153 | /* If we don't care if the file is executable or we already know if it is, |
154 | then just do a "query open" as it is apparently much faster. */ | |
155 | if (pc->exec_state () != dont_know_if_executable) | |
156 | set_query_open (query_open_already = true); | |
157 | else | |
158 | query_open_already = false; | |
159 | ||
97478fe6 | 160 | if (query_open_already && strncasematch (pc->volname (), "FAT", 3) |
5bf785a0 | 161 | && !strpbrk (get_win32_name (), "?*|<>")) |
2402700d | 162 | oret = 0; |
9f033c22 PH |
163 | else if (!(oret = open (pc, open_flags, 0)) |
164 | && !query_open_already | |
165 | && get_errno () == EACCES) | |
7903ee69 | 166 | { |
7903ee69 CF |
167 | /* If we couldn't open the file, try a "query open" with no permissions. |
168 | This will allow us to determine *some* things about the file, at least. */ | |
9f033c22 | 169 | pc->set_exec (0); |
2402700d | 170 | set_query_open (true); |
9f033c22 | 171 | oret = open (pc, open_flags, 0); |
7903ee69 | 172 | } |
2402700d | 173 | |
5bf785a0 | 174 | if (!oret || get_nohandle ()) |
5d8c7a56 CF |
175 | res = fstat_by_name (buf, pc); |
176 | else | |
7903ee69 | 177 | { |
2402700d | 178 | res = fstat_by_handle (buf, pc); |
7903ee69 CF |
179 | close (); |
180 | } | |
7903ee69 CF |
181 | |
182 | return res; | |
183 | } | |
184 | ||
2402700d CF |
185 | int __stdcall |
186 | fhandler_disk_file::fstat_helper (struct __stat64 *buf, path_conv *pc, | |
187 | FILETIME ftCreationTime, | |
188 | FILETIME ftLastAccessTime, | |
189 | FILETIME ftLastWriteTime, | |
190 | DWORD nFileSizeHigh, | |
191 | DWORD nFileSizeLow, | |
192 | DWORD nFileIndexHigh, | |
193 | DWORD nFileIndexLow, | |
194 | DWORD nNumberOfLinks) | |
7903ee69 | 195 | { |
7903ee69 | 196 | /* This is for FAT filesystems, which don't support atime/ctime */ |
2402700d CF |
197 | if (ftLastAccessTime.dwLowDateTime == 0 |
198 | && ftLastAccessTime.dwHighDateTime == 0) | |
199 | ftLastAccessTime = ftLastWriteTime; | |
200 | if (ftCreationTime.dwLowDateTime == 0 | |
201 | && ftCreationTime.dwHighDateTime == 0) | |
202 | ftCreationTime = ftLastWriteTime; | |
203 | ||
c4e6ff48 CV |
204 | to_timestruc_t (&ftLastAccessTime, &buf->st_atim); |
205 | to_timestruc_t (&ftLastWriteTime, &buf->st_mtim); | |
206 | to_timestruc_t (&ftCreationTime, &buf->st_ctim); | |
c2102631 | 207 | buf->st_dev = pc->volser (); |
1727fba0 | 208 | buf->st_size = ((_off64_t)nFileSizeHigh << 32) + nFileSizeLow; |
9f033c22 PH |
209 | /* The number of links to a directory includes the |
210 | number of subdirectories in the directory, since all | |
211 | those subdirectories point to it. | |
212 | This is too slow on remote drives, so we do without it. | |
213 | Setting the count to 2 confuses `find (1)' command. So | |
c2102631 CF |
214 | let's try it with `1' as link count. */ |
215 | if (pc->isdir () && !pc->isremote () && nNumberOfLinks == 1) | |
216 | buf->st_nlink = num_entries (pc->get_win32 ()); | |
217 | else | |
218 | buf->st_nlink = nNumberOfLinks; | |
7903ee69 CF |
219 | |
220 | /* Assume that if a drive has ACL support it MAY have valid "inodes". | |
221 | It definitely does not have valid inodes if it does not have ACL | |
222 | support. */ | |
2402700d CF |
223 | switch (pc->has_acls () && (nFileIndexHigh || nFileIndexLow) |
224 | ? pc->drive_type () : DRIVE_UNKNOWN) | |
7903ee69 CF |
225 | { |
226 | case DRIVE_FIXED: | |
227 | case DRIVE_REMOVABLE: | |
228 | case DRIVE_CDROM: | |
229 | case DRIVE_RAMDISK: | |
230 | /* Although the documentation indicates otherwise, it seems like | |
231 | "inodes" on these devices are persistent, at least across reboots. */ | |
cc95baad | 232 | buf->st_ino = (((__ino64_t) nFileIndexHigh) << 32) |
df04ae29 | 233 | | (__ino64_t) nFileIndexLow; |
7903ee69 CF |
234 | break; |
235 | default: | |
236 | /* Either the nFileIndex* fields are unreliable or unavailable. Use the | |
237 | next best alternative. */ | |
238 | buf->st_ino = get_namehash (); | |
239 | break; | |
240 | } | |
241 | ||
242 | buf->st_blksize = S_BLKSIZE; | |
3a88cf1b CV |
243 | |
244 | /* GetCompressedFileSize() gets autoloaded. It returns INVALID_FILE_SIZE | |
245 | if it doesn't exist. Since that's also a valid return value on 64bit | |
246 | capable file systems, we must additionally check for the win32 error. */ | |
247 | nFileSizeLow = GetCompressedFileSizeA (pc->get_win32 (), &nFileSizeHigh); | |
248 | if (nFileSizeLow != INVALID_FILE_SIZE || GetLastError () == NO_ERROR) | |
249 | /* On systems supporting compressed (and sparsed) files, | |
250 | GetCompressedFileSize() returns the actual amount of | |
251 | bytes allocated on disk. */ | |
1727fba0 | 252 | buf->st_blocks = (((_off64_t)nFileSizeHigh << 32) |
3a88cf1b CV |
253 | + nFileSizeLow + S_BLKSIZE - 1) / S_BLKSIZE; |
254 | else | |
255 | /* Just compute no. of blocks from file size. */ | |
256 | buf->st_blocks = (buf->st_size + S_BLKSIZE - 1) / S_BLKSIZE; | |
7903ee69 CF |
257 | |
258 | buf->st_mode = 0; | |
259 | /* Using a side effect: get_file_attibutes checks for | |
260 | directory. This is used, to set S_ISVTX, if needed. */ | |
99138976 | 261 | if (pc->isdir ()) |
7903ee69 | 262 | buf->st_mode = S_IFDIR; |
2402700d | 263 | else if (pc->issymlink ()) |
2e23862a CV |
264 | { |
265 | /* symlinks are everything for everyone! */ | |
266 | buf->st_mode = S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO; | |
267 | get_file_attribute (pc->has_acls (), get_win32_name (), NULL, | |
268 | &buf->st_uid, &buf->st_gid); | |
269 | goto done; | |
270 | } | |
2402700d | 271 | else if (pc->issocket ()) |
7903ee69 | 272 | buf->st_mode = S_IFSOCK; |
2402700d | 273 | |
2402700d | 274 | if (get_file_attribute (pc->has_acls (), get_win32_name (), &buf->st_mode, |
2e23862a | 275 | &buf->st_uid, &buf->st_gid) == 0) |
7903ee69 CF |
276 | { |
277 | /* If read-only attribute is set, modify ntsec return value */ | |
99138976 | 278 | if (pc->has_attribute (FILE_ATTRIBUTE_READONLY) && !get_symlink_p ()) |
7903ee69 CF |
279 | buf->st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); |
280 | ||
281 | if (!(buf->st_mode & S_IFMT)) | |
282 | buf->st_mode |= S_IFREG; | |
283 | } | |
284 | else | |
285 | { | |
286 | buf->st_mode |= STD_RBITS; | |
287 | ||
99138976 | 288 | if (!pc->has_attribute (FILE_ATTRIBUTE_READONLY)) |
7903ee69 CF |
289 | buf->st_mode |= STD_WBITS; |
290 | /* | S_IWGRP | S_IWOTH; we don't give write to group etc */ | |
291 | ||
b1897d27 | 292 | if (S_ISDIR (buf->st_mode)) |
7903ee69 CF |
293 | buf->st_mode |= S_IFDIR | STD_XBITS; |
294 | else if (buf->st_mode & S_IFMT) | |
295 | /* nothing */; | |
7903ee69 | 296 | else |
2402700d CF |
297 | { |
298 | buf->st_mode |= S_IFREG; | |
281e3b24 | 299 | if (pc->exec_state () == dont_know_if_executable) |
046a4e71 | 300 | { |
281e3b24 CF |
301 | DWORD cur, done; |
302 | char magic[3]; | |
303 | ||
304 | /* First retrieve current position, set to beginning | |
305 | of file if not already there. */ | |
306 | cur = SetFilePointer (get_handle (), 0, NULL, FILE_CURRENT); | |
307 | if (cur != INVALID_SET_FILE_POINTER | |
308 | && (!cur || SetFilePointer (get_handle (), 0, NULL, FILE_BEGIN) | |
309 | != INVALID_SET_FILE_POINTER)) | |
310 | { | |
311 | /* FIXME should we use /etc/magic ? */ | |
312 | magic[0] = magic[1] = magic[2] = '\0'; | |
313 | if (ReadFile (get_handle (), magic, 3, &done, NULL) | |
314 | && has_exec_chars (magic, done)) | |
315 | { | |
316 | set_execable_p (); | |
317 | pc->set_exec (); | |
318 | buf->st_mode |= STD_XBITS; | |
319 | } | |
320 | (void) SetFilePointer (get_handle (), cur, NULL, FILE_BEGIN); | |
321 | } | |
046a4e71 | 322 | } |
046a4e71 | 323 | } |
281e3b24 CF |
324 | |
325 | if (pc->exec_state () == is_executable) | |
326 | buf->st_mode |= STD_XBITS; | |
2311f6b0 CV |
327 | |
328 | /* This fakes the permissions of all files to match the current umask. */ | |
329 | buf->st_mode &= ~(cygheap->umask); | |
046a4e71 | 330 | } |
7dcbb978 | 331 | |
2e23862a | 332 | done: |
2402700d CF |
333 | syscall_printf ("0 = fstat (, %p) st_atime=%x st_size=%D, st_mode=%p, st_ino=%d, sizeof=%d", |
334 | buf, buf->st_atime, buf->st_size, buf->st_mode, | |
335 | (int) buf->st_ino, sizeof (*buf)); | |
7903ee69 CF |
336 | return 0; |
337 | } | |
338 | ||
97a2e075 CF |
339 | fhandler_disk_file::fhandler_disk_file (DWORD devtype) : |
340 | fhandler_base (devtype) | |
341 | { | |
342 | } | |
343 | ||
7903ee69 | 344 | fhandler_disk_file::fhandler_disk_file () : |
97a2e075 | 345 | fhandler_base (FH_DISK) |
7903ee69 CF |
346 | { |
347 | } | |
348 | ||
349 | int | |
350 | fhandler_disk_file::open (path_conv *real_path, int flags, mode_t mode) | |
351 | { | |
352 | if (real_path->case_clash && flags & O_CREAT) | |
353 | { | |
97a2e075 | 354 | debug_printf ("case clash detected"); |
7903ee69 CF |
355 | set_errno (ECASECLASH); |
356 | return 0; | |
357 | } | |
358 | ||
7903ee69 CF |
359 | set_has_acls (real_path->has_acls ()); |
360 | set_isremote (real_path->isremote ()); | |
361 | ||
335556d5 | 362 | int res = fhandler_base::open (real_path, flags | O_DIROPEN, mode); |
7903ee69 CF |
363 | if (!res) |
364 | goto out; | |
365 | ||
366 | /* This is for file systems known for having a buggy CreateFile call | |
367 | which might return a valid HANDLE without having actually opened | |
368 | the file. | |
369 | The only known file system to date is the SUN NFS Solstice Client 3.1 | |
370 | which returns a valid handle when trying to open a file in a nonexistent | |
371 | directory. */ | |
2402700d | 372 | if (real_path->has_buggy_open () && !real_path->exists ()) |
7903ee69 CF |
373 | { |
374 | debug_printf ("Buggy open detected."); | |
375 | close (); | |
376 | set_errno (ENOENT); | |
377 | return 0; | |
378 | } | |
379 | ||
5cd82412 CF |
380 | /* Attributes may be set only if a file is _really_ created. |
381 | This code is now only used for ntea here since the files | |
382 | security attributes are set in CreateFile () now. */ | |
383 | if (flags & O_CREAT | |
384 | && GetLastError () != ERROR_ALREADY_EXISTS | |
385 | && !allow_ntsec && allow_ntea) | |
386 | set_file_attribute (has_acls (), get_win32_name (), mode); | |
387 | ||
fac297d5 | 388 | set_fs_flags (real_path->fs_flags ()); |
7903ee69 CF |
389 | set_symlink_p (real_path->issymlink ()); |
390 | set_execable_p (real_path->exec_state ()); | |
391 | set_socket_p (real_path->issocket ()); | |
392 | ||
393 | out: | |
394 | syscall_printf ("%d = fhandler_disk_file::open (%s, %p)", res, | |
395 | get_win32_name (), flags); | |
396 | return res; | |
397 | } | |
398 | ||
399 | int | |
400 | fhandler_disk_file::close () | |
401 | { | |
335556d5 | 402 | int res = fhandler_base::close (); |
7903ee69 CF |
403 | if (!res) |
404 | cygwin_shared->delqueue.process_queue (); | |
405 | return res; | |
406 | } | |
407 | ||
408 | /* | |
409 | * FIXME !!! | |
410 | * The correct way to do this to get POSIX locking | |
411 | * semantics is to keep a linked list of posix lock | |
412 | * requests and map them into Win32 locks. The problem | |
413 | * is that Win32 does not deal correctly with overlapping | |
414 | * lock requests. Also another pain is that Win95 doesn't do | |
415 | * non-blocking or non exclusive locks at all. For '95 just | |
416 | * convert all lock requests into blocking,exclusive locks. | |
417 | * This shouldn't break many apps but denying all locking | |
418 | * would. | |
419 | * For now just convert to Win32 locks and hope for the best. | |
420 | */ | |
421 | ||
422 | int | |
423 | fhandler_disk_file::lock (int cmd, struct flock *fl) | |
424 | { | |
1727fba0 | 425 | _off64_t win32_start; |
7903ee69 CF |
426 | int win32_len; |
427 | DWORD win32_upper; | |
1727fba0 | 428 | _off64_t startpos; |
7903ee69 CF |
429 | |
430 | /* | |
431 | * We don't do getlck calls yet. | |
432 | */ | |
433 | ||
434 | if (cmd == F_GETLK) | |
435 | { | |
436 | set_errno (ENOSYS); | |
437 | return -1; | |
438 | } | |
439 | ||
440 | /* | |
441 | * Calculate where in the file to start from, | |
442 | * then adjust this by fl->l_start. | |
443 | */ | |
444 | ||
445 | switch (fl->l_whence) | |
446 | { | |
447 | case SEEK_SET: | |
448 | startpos = 0; | |
449 | break; | |
450 | case SEEK_CUR: | |
acb56175 | 451 | if ((startpos = lseek (0, SEEK_CUR)) == ILLEGAL_SEEK) |
7903ee69 CF |
452 | return -1; |
453 | break; | |
454 | case SEEK_END: | |
455 | { | |
456 | BY_HANDLE_FILE_INFORMATION finfo; | |
c90e1cf1 | 457 | if (GetFileInformationByHandle (get_handle (), &finfo) == 0) |
7903ee69 CF |
458 | { |
459 | __seterrno (); | |
460 | return -1; | |
461 | } | |
1727fba0 | 462 | startpos = ((_off64_t)finfo.nFileSizeHigh << 32) |
acb56175 | 463 | + finfo.nFileSizeLow; |
7903ee69 CF |
464 | break; |
465 | } | |
466 | default: | |
467 | set_errno (EINVAL); | |
468 | return -1; | |
469 | } | |
470 | ||
471 | /* | |
472 | * Now the fun starts. Adjust the start and length | |
473 | * fields until they make sense. | |
474 | */ | |
475 | ||
476 | win32_start = startpos + fl->l_start; | |
477 | if (fl->l_len < 0) | |
478 | { | |
479 | win32_start -= fl->l_len; | |
480 | win32_len = -fl->l_len; | |
481 | } | |
482 | else | |
483 | win32_len = fl->l_len; | |
484 | ||
485 | if (win32_start < 0) | |
486 | { | |
487 | /* watch the signs! */ | |
488 | win32_len -= -win32_start; | |
489 | if (win32_len <= 0) | |
490 | { | |
491 | /* Failure ! */ | |
492 | set_errno (EINVAL); | |
493 | return -1; | |
494 | } | |
495 | win32_start = 0; | |
496 | } | |
497 | ||
498 | /* | |
499 | * Special case if len == 0 for POSIX means lock | |
500 | * to the end of the entire file (and all future extensions). | |
501 | */ | |
502 | if (win32_len == 0) | |
503 | { | |
504 | win32_len = 0xffffffff; | |
505 | win32_upper = wincap.lock_file_highword (); | |
506 | } | |
507 | else | |
508 | win32_upper = 0; | |
509 | ||
510 | BOOL res; | |
511 | ||
512 | if (wincap.has_lock_file_ex ()) | |
513 | { | |
514 | DWORD lock_flags = (cmd == F_SETLK) ? LOCKFILE_FAIL_IMMEDIATELY : 0; | |
515 | lock_flags |= (fl->l_type == F_WRLCK) ? LOCKFILE_EXCLUSIVE_LOCK : 0; | |
516 | ||
517 | OVERLAPPED ov; | |
518 | ||
519 | ov.Internal = 0; | |
520 | ov.InternalHigh = 0; | |
521 | ov.Offset = (DWORD)win32_start; | |
522 | ov.OffsetHigh = 0; | |
523 | ov.hEvent = (HANDLE) 0; | |
524 | ||
525 | if (fl->l_type == F_UNLCK) | |
526 | { | |
527 | res = UnlockFileEx (get_handle (), 0, (DWORD)win32_len, win32_upper, &ov); | |
528 | } | |
529 | else | |
530 | { | |
531 | res = LockFileEx (get_handle (), lock_flags, 0, (DWORD)win32_len, | |
532 | win32_upper, &ov); | |
533 | /* Deal with the fail immediately case. */ | |
534 | /* | |
535 | * FIXME !! I think this is the right error to check for | |
536 | * but I must admit I haven't checked.... | |
537 | */ | |
538 | if ((res == 0) && (lock_flags & LOCKFILE_FAIL_IMMEDIATELY) && | |
539 | (GetLastError () == ERROR_LOCK_FAILED)) | |
540 | { | |
541 | set_errno (EAGAIN); | |
542 | return -1; | |
543 | } | |
544 | } | |
545 | } | |
546 | else | |
547 | { | |
548 | /* Windows 95 -- use primitive lock call */ | |
549 | if (fl->l_type == F_UNLCK) | |
550 | res = UnlockFile (get_handle (), (DWORD)win32_start, 0, (DWORD)win32_len, | |
551 | win32_upper); | |
552 | else | |
553 | res = LockFile (get_handle (), (DWORD)win32_start, 0, (DWORD)win32_len, win32_upper); | |
554 | } | |
555 | ||
556 | if (res == 0) | |
557 | { | |
558 | __seterrno (); | |
559 | return -1; | |
560 | } | |
561 | ||
562 | return 0; | |
563 | } | |
564 | ||
565 | DIR * | |
97a2e075 | 566 | fhandler_disk_file::opendir (path_conv& real_name) |
7903ee69 | 567 | { |
7903ee69 CF |
568 | DIR *dir; |
569 | DIR *res = NULL; | |
570 | size_t len; | |
571 | ||
97a2e075 | 572 | if (!real_name.isdir ()) |
7903ee69 CF |
573 | set_errno (ENOTDIR); |
574 | else if ((len = strlen (real_name))> MAX_PATH - 3) | |
575 | set_errno (ENAMETOOLONG); | |
576 | else if ((dir = (DIR *) malloc (sizeof (DIR))) == NULL) | |
577 | set_errno (ENOMEM); | |
578 | else if ((dir->__d_dirname = (char *) malloc (len + 3)) == NULL) | |
579 | { | |
580 | free (dir); | |
581 | set_errno (ENOMEM); | |
582 | } | |
583 | else if ((dir->__d_dirent = | |
584 | (struct dirent *) malloc (sizeof (struct dirent))) == NULL) | |
585 | { | |
586 | free (dir->__d_dirname); | |
587 | free (dir); | |
588 | set_errno (ENOMEM); | |
589 | } | |
590 | else | |
591 | { | |
592 | strcpy (dir->__d_dirname, real_name.get_win32 ()); | |
593 | dir->__d_dirent->d_version = __DIRENT_VERSION; | |
594 | cygheap_fdnew fd; | |
518f5d49 | 595 | if (fd >= 0) |
a113a3c5 | 596 | { |
518f5d49 CV |
597 | fd = this; |
598 | fd->set_nohandle (true); | |
599 | dir->__d_dirent->d_fd = fd; | |
0c7b5572 | 600 | dir->__fh = this; |
518f5d49 CV |
601 | /* FindFirstFile doesn't seem to like duplicate /'s. */ |
602 | len = strlen (dir->__d_dirname); | |
603 | if (len == 0 || isdirsep (dir->__d_dirname[len - 1])) | |
604 | strcat (dir->__d_dirname, "*"); | |
605 | else | |
606 | strcat (dir->__d_dirname, "\\*"); /**/ | |
607 | dir->__d_cookie = __DIRENT_COOKIE; | |
0c7b5572 | 608 | dir->__handle = INVALID_HANDLE_VALUE; |
518f5d49 CV |
609 | dir->__d_position = 0; |
610 | dir->__d_dirhash = get_namehash (); | |
611 | ||
612 | res = dir; | |
613 | } | |
3f214783 CF |
614 | if (real_name.isencoded ()) |
615 | set_encoded (); | |
7903ee69 CF |
616 | } |
617 | ||
97a2e075 | 618 | syscall_printf ("%p = opendir (%s)", res, get_name ()); |
7903ee69 CF |
619 | return res; |
620 | } | |
621 | ||
622 | struct dirent * | |
623 | fhandler_disk_file::readdir (DIR *dir) | |
624 | { | |
625 | WIN32_FIND_DATA buf; | |
626 | HANDLE handle; | |
627 | struct dirent *res = NULL; | |
628 | ||
0c7b5572 | 629 | if (dir->__handle == INVALID_HANDLE_VALUE |
7903ee69 CF |
630 | && dir->__d_position == 0) |
631 | { | |
632 | handle = FindFirstFileA (dir->__d_dirname, &buf); | |
633 | DWORD lasterr = GetLastError (); | |
0c7b5572 | 634 | dir->__handle = handle; |
7903ee69 CF |
635 | if (handle == INVALID_HANDLE_VALUE && (lasterr != ERROR_NO_MORE_FILES)) |
636 | { | |
637 | seterrno_from_win_error (__FILE__, __LINE__, lasterr); | |
638 | return res; | |
639 | } | |
640 | } | |
0c7b5572 | 641 | else if (dir->__handle == INVALID_HANDLE_VALUE) |
3f214783 | 642 | return res; |
0c7b5572 | 643 | else if (!FindNextFileA (dir->__handle, &buf)) |
7903ee69 CF |
644 | { |
645 | DWORD lasterr = GetLastError (); | |
0c7b5572 CF |
646 | (void) FindClose (dir->__handle); |
647 | dir->__handle = INVALID_HANDLE_VALUE; | |
7903ee69 CF |
648 | /* POSIX says you shouldn't set errno when readdir can't |
649 | find any more files; so, if another error we leave it set. */ | |
650 | if (lasterr != ERROR_NO_MORE_FILES) | |
651 | seterrno_from_win_error (__FILE__, __LINE__, lasterr); | |
652 | syscall_printf ("%p = readdir (%p)", res, dir); | |
653 | return res; | |
654 | } | |
655 | ||
656 | /* We get here if `buf' contains valid data. */ | |
3f214783 CF |
657 | if (get_encoded ()) |
658 | (void) fnunmunge (dir->__d_dirent->d_name, buf.cFileName); | |
659 | else | |
660 | strcpy (dir->__d_dirent->d_name, buf.cFileName); | |
7903ee69 CF |
661 | |
662 | /* Check for Windows shortcut. If it's a Cygwin or U/WIN | |
663 | symlink, drop the .lnk suffix. */ | |
664 | if (buf.dwFileAttributes & FILE_ATTRIBUTE_READONLY) | |
665 | { | |
666 | char *c = dir->__d_dirent->d_name; | |
667 | int len = strlen (c); | |
668 | if (strcasematch (c + len - 4, ".lnk")) | |
669 | { | |
670 | char fbuf[MAX_PATH + 1]; | |
671 | strcpy (fbuf, dir->__d_dirname); | |
672 | strcpy (fbuf + strlen (fbuf) - 1, dir->__d_dirent->d_name); | |
673 | path_conv fpath (fbuf, PC_SYM_NOFOLLOW); | |
674 | if (fpath.issymlink ()) | |
675 | c[len - 4] = '\0'; | |
676 | } | |
677 | } | |
678 | ||
97a2e075 | 679 | dir->__d_position++; |
7903ee69 CF |
680 | res = dir->__d_dirent; |
681 | syscall_printf ("%p = readdir (%p) (%s)", | |
682 | &dir->__d_dirent, dir, buf.cFileName); | |
683 | return res; | |
684 | } | |
685 | ||
1727fba0 | 686 | _off64_t |
7903ee69 CF |
687 | fhandler_disk_file::telldir (DIR *dir) |
688 | { | |
689 | return dir->__d_position; | |
690 | } | |
691 | ||
692 | void | |
1727fba0 | 693 | fhandler_disk_file::seekdir (DIR *dir, _off64_t loc) |
7903ee69 CF |
694 | { |
695 | rewinddir (dir); | |
696 | while (loc > dir->__d_position) | |
97a2e075 | 697 | if (!readdir (dir)) |
7903ee69 CF |
698 | break; |
699 | } | |
700 | ||
701 | void | |
702 | fhandler_disk_file::rewinddir (DIR *dir) | |
703 | { | |
0c7b5572 | 704 | if (dir->__handle != INVALID_HANDLE_VALUE) |
7903ee69 | 705 | { |
0c7b5572 CF |
706 | (void) FindClose (dir->__handle); |
707 | dir->__handle = INVALID_HANDLE_VALUE; | |
7903ee69 CF |
708 | } |
709 | dir->__d_position = 0; | |
710 | } | |
711 | ||
712 | int | |
713 | fhandler_disk_file::closedir (DIR *dir) | |
714 | { | |
715 | int res = 0; | |
0c7b5572 CF |
716 | if (dir->__handle != INVALID_HANDLE_VALUE && |
717 | FindClose (dir->__handle) == 0) | |
7903ee69 CF |
718 | { |
719 | __seterrno (); | |
720 | res = -1; | |
721 | } | |
722 | syscall_printf ("%d = closedir (%p)", res, dir); | |
723 | return 0; | |
724 | } | |
97a2e075 CF |
725 | |
726 | fhandler_cygdrive::fhandler_cygdrive (int unit) : | |
727 | fhandler_disk_file (FH_CYGDRIVE), unit (unit), ndrives (0), pdrive (NULL) | |
728 | { | |
729 | } | |
730 | ||
731 | #define DRVSZ sizeof ("x:\\") | |
732 | void | |
733 | fhandler_cygdrive::set_drives () | |
734 | { | |
0c7b5572 CF |
735 | const int len = 2 + 26 * DRVSZ; |
736 | char *p = (char *) crealloc ((void *) win32_path_name, len); | |
97a2e075 | 737 | |
ff938546 CF |
738 | win32_path_name = pdrive = p; |
739 | ndrives = GetLogicalDriveStrings (len, p) / DRVSZ; | |
97a2e075 CF |
740 | } |
741 | ||
742 | int | |
acb56175 | 743 | fhandler_cygdrive::fstat (struct __stat64 *buf, path_conv *pc) |
97a2e075 CF |
744 | { |
745 | if (!iscygdrive_root ()) | |
746 | return fhandler_disk_file::fstat (buf, pc); | |
747 | buf->st_mode = S_IFDIR | 0555; | |
748 | if (!ndrives) | |
749 | set_drives (); | |
1aa76ad5 | 750 | buf->st_nlink = ndrives + 2; |
97a2e075 CF |
751 | return 0; |
752 | } | |
753 | ||
754 | DIR * | |
755 | fhandler_cygdrive::opendir (path_conv& real_name) | |
756 | { | |
757 | DIR *dir; | |
758 | ||
759 | dir = fhandler_disk_file::opendir (real_name); | |
760 | if (dir && iscygdrive_root () && !ndrives) | |
761 | set_drives (); | |
762 | ||
763 | return dir; | |
764 | } | |
765 | ||
766 | struct dirent * | |
767 | fhandler_cygdrive::readdir (DIR *dir) | |
768 | { | |
769 | if (!iscygdrive_root ()) | |
770 | return fhandler_disk_file::readdir (dir); | |
771 | if (!pdrive || !*pdrive) | |
3fea2e40 | 772 | return NULL; |
1aa76ad5 | 773 | if (GetFileAttributes (pdrive) == INVALID_FILE_ATTRIBUTES) |
28af03fa | 774 | { |
5ba08a92 | 775 | pdrive = strchr (pdrive, '\0') + 1; |
ded25056 CF |
776 | return readdir (dir); |
777 | } | |
1aa76ad5 CF |
778 | |
779 | *dir->__d_dirent->d_name = cyg_tolower (*pdrive); | |
780 | dir->__d_dirent->d_name[1] = '\0'; | |
97a2e075 | 781 | dir->__d_position++; |
5ba08a92 | 782 | pdrive = strchr (pdrive, '\0') + 1; |
97a2e075 | 783 | syscall_printf ("%p = readdir (%p) (%s)", &dir->__d_dirent, dir, |
5bf785a0 | 784 | dir->__d_dirent->d_name); |
97a2e075 CF |
785 | return dir->__d_dirent; |
786 | } | |
787 | ||
1727fba0 | 788 | _off64_t |
97a2e075 CF |
789 | fhandler_cygdrive::telldir (DIR *dir) |
790 | { | |
791 | return fhandler_disk_file::telldir (dir); | |
792 | } | |
793 | ||
794 | void | |
1727fba0 | 795 | fhandler_cygdrive::seekdir (DIR *dir, _off64_t loc) |
97a2e075 CF |
796 | { |
797 | if (!iscygdrive_root ()) | |
798 | return fhandler_disk_file::seekdir (dir, loc); | |
799 | ||
5ba08a92 CF |
800 | for (pdrive = win32_path_name, dir->__d_position = -1; *pdrive; |
801 | pdrive = strchr (pdrive, '\0') + 1) | |
97a2e075 CF |
802 | if (++dir->__d_position >= loc) |
803 | break; | |
804 | ||
805 | return; | |
806 | } | |
807 | ||
808 | void | |
809 | fhandler_cygdrive::rewinddir (DIR *dir) | |
810 | { | |
811 | if (!iscygdrive_root ()) | |
812 | return fhandler_disk_file::rewinddir (dir); | |
813 | pdrive = win32_path_name; | |
814 | dir->__d_position = 0; | |
815 | return; | |
816 | } | |
817 | ||
818 | int | |
819 | fhandler_cygdrive::closedir (DIR *dir) | |
820 | { | |
821 | if (!iscygdrive_root ()) | |
822 | return fhandler_disk_file::closedir (dir); | |
823 | pdrive = win32_path_name; | |
824 | return -1; | |
825 | } |