]> sourceware.org Git - newlib-cygwin.git/blob - winsup/cygwin/fhandler_disk_file.cc
5dfcae4d9eb7119bc5e224debf9b8b38f400f7cf
[newlib-cygwin.git] / winsup / cygwin / fhandler_disk_file.cc
1 /* fhandler_disk_file.cc
2
3 This file is part of Cygwin.
4
5 This software is a copyrighted work licensed under the terms of the
6 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
7 details. */
8
9 #include "winsup.h"
10 #include <winioctl.h>
11 #include <lm.h>
12 #include <stdlib.h>
13 #include <cygwin/acl.h>
14 #include <sys/statvfs.h>
15 #include "cygerrno.h"
16 #include "security.h"
17 #include "path.h"
18 #include "fhandler.h"
19 #include "dtable.h"
20 #include "cygheap.h"
21 #include "shared_info.h"
22 #include "pinfo.h"
23 #include "ntdll.h"
24 #include "tls_pbuf.h"
25 #include "devices.h"
26 #include "ldap.h"
27
28 #define _COMPILING_NEWLIB
29 #include <dirent.h>
30
31 enum __DIR_mount_type {
32 __DIR_mount_none = 0,
33 __DIR_mount_target,
34 __DIR_mount_virt_target
35 };
36
37 class __DIR_mounts
38 {
39 int count;
40 const char *parent_dir;
41 int parent_dir_len;
42 UNICODE_STRING mounts[MAX_MOUNTS];
43 bool found[MAX_MOUNTS + 3];
44 UNICODE_STRING cygdrive;
45
46 #define __DIR_PROC (MAX_MOUNTS)
47 #define __DIR_CYGDRIVE (MAX_MOUNTS+1)
48 #define __DIR_DEV (MAX_MOUNTS+2)
49
50 public:
51 __DIR_mounts (const char *posix_path)
52 : parent_dir (posix_path)
53 {
54 parent_dir_len = strlen (parent_dir);
55 count = mount_table->get_mounts_here (parent_dir, parent_dir_len, mounts,
56 &cygdrive);
57 rewind ();
58 }
59 ~__DIR_mounts ()
60 {
61 for (int i = 0; i < count; ++i)
62 RtlFreeUnicodeString (&mounts[i]);
63 RtlFreeUnicodeString (&cygdrive);
64 }
65 /* For an entry within this dir, check if a mount point exists. */
66 bool check_mount (PUNICODE_STRING fname)
67 {
68 if (parent_dir_len == 1) /* root dir */
69 {
70 if (RtlEqualUnicodeString (fname, &ro_u_proc, FALSE))
71 {
72 found[__DIR_PROC] = true;
73 return true;
74 }
75 if (RtlEqualUnicodeString (fname, &ro_u_dev, FALSE))
76 {
77 found[__DIR_DEV] = true;
78 return true;
79 }
80 if (fname->Length / sizeof (WCHAR) == mount_table->cygdrive_len - 2
81 && RtlEqualUnicodeString (fname, &cygdrive, FALSE))
82 {
83 found[__DIR_CYGDRIVE] = true;
84 return true;
85 }
86 }
87 for (int i = 0; i < count; ++i)
88 if (RtlEqualUnicodeString (fname, &mounts[i], FALSE))
89 {
90 found[i] = true;
91 return true;
92 }
93 return false;
94 }
95 /* On each call, add another mount point within this directory, which is
96 not backed by a real subdir. */
97 __DIR_mount_type check_missing_mount (PUNICODE_STRING retname = NULL)
98 {
99 for (int i = 0; i < count; ++i)
100 if (!found[i])
101 {
102 found[i] = true;
103 if (retname)
104 *retname = mounts[i];
105 return __DIR_mount_target;
106 }
107 if (parent_dir_len == 1) /* root dir */
108 {
109 if (!found[__DIR_PROC])
110 {
111 found[__DIR_PROC] = true;
112 if (retname)
113 *retname = ro_u_proc;
114 return __DIR_mount_virt_target;
115 }
116 if (!found[__DIR_DEV])
117 {
118 found[__DIR_DEV] = true;
119 if (retname)
120 *retname = ro_u_dev;
121 return __DIR_mount_virt_target;
122 }
123 if (!found[__DIR_CYGDRIVE])
124 {
125 found[__DIR_CYGDRIVE] = true;
126 if (cygdrive.Length > 0)
127 {
128 if (retname)
129 *retname = cygdrive;
130 return __DIR_mount_virt_target;
131 }
132 }
133 }
134 return __DIR_mount_none;
135 }
136 void rewind () { memset (found, 0, sizeof found); }
137 };
138
139 inline bool
140 path_conv::isgood_inode (ino_t ino) const
141 {
142 /* If the FS doesn't support nonambiguous inode numbers anyway, bail out
143 immediately. */
144 if (!hasgood_inode ())
145 return false;
146 /* If the inode numbers are 64 bit numbers or if it's a local FS, they
147 are to be trusted. */
148 if (ino > UINT32_MAX || !isremote ())
149 return true;
150 /* The inode numbers returned from a remote NT4 NTFS are ephemeral
151 32 bit numbers. */
152 if (fs_is_ntfs ())
153 return false;
154 /* Starting with version 3.5.4, Samba returns the real inode numbers, if
155 the file is on the same device as the root of the share (Samba function
156 get_FileIndex). 32 bit inode numbers returned by older versions (likely
157 < 3.0) are ephemeral. */
158 if (fs_is_samba () && fs.samba_version () < 0x03050400)
159 return false;
160 /* Otherwise, trust the inode numbers unless proved otherwise. */
161 return true;
162 }
163
164 /* Check reparse point to determine if it should be treated as a
165 posix symlink or as a normal file/directory. Logic is explained
166 in detail in check_reparse_point_target in path.cc. */
167 static inline bool
168 readdir_check_reparse_point (POBJECT_ATTRIBUTES attr, bool remote)
169 {
170 NTSTATUS status;
171 HANDLE reph;
172 IO_STATUS_BLOCK io;
173 tmp_pathbuf tp;
174 UNICODE_STRING symbuf;
175 bool ret = false;
176
177 status = NtOpenFile (&reph, READ_CONTROL, attr, &io, FILE_SHARE_VALID_FLAGS,
178 FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT);
179 if (NT_SUCCESS (status))
180 {
181 PREPARSE_DATA_BUFFER rp = (PREPARSE_DATA_BUFFER) tp.c_get ();
182 ret = (check_reparse_point_target (reph, remote, rp, &symbuf) > 0);
183 NtClose (reph);
184 }
185 return ret;
186 }
187
188 inline ino_t
189 path_conv::get_ino_by_handle (HANDLE hdl)
190 {
191 IO_STATUS_BLOCK io;
192 FILE_INTERNAL_INFORMATION fii;
193
194 if (NT_SUCCESS (NtQueryInformationFile (hdl, &io, &fii, sizeof fii,
195 FileInternalInformation))
196 && isgood_inode (fii.IndexNumber.QuadPart))
197 return fii.IndexNumber.QuadPart;
198 return 0;
199 }
200
201 /* For files on NFS shares, we request an EA of type NfsV3Attributes.
202 This returns the content of a struct fattr3 as defined in RFC 1813.
203 The content is the NFS equivalent of struct stat. so there's not much
204 to do here except for copying. */
205 int __reg2
206 fhandler_base::fstat_by_nfs_ea (struct stat *buf)
207 {
208 fattr3 *nfs_attr = pc.nfsattr ();
209 PWCHAR domain;
210 cyg_ldap cldap;
211 bool ldap_open = false;
212
213 if (get_io_handle ())
214 {
215 /* NFS stumbles over its own caching. If you write to the file,
216 a subsequent fstat does not return the actual size of the file,
217 but the size at the time the handle has been opened. Unless
218 access through another handle invalidates the caching within the
219 NFS client. */
220 if (get_access () & GENERIC_WRITE)
221 FlushFileBuffers (get_io_handle ());
222 pc.get_finfo (get_io_handle ());
223 }
224 buf->st_dev = nfs_attr->fsid;
225 buf->st_ino = nfs_attr->fileid;
226 buf->st_mode = (nfs_attr->mode & 0xfff)
227 | nfs_type_mapping[nfs_attr->type & 7];
228 buf->st_nlink = nfs_attr->nlink;
229 if (cygheap->pg.nss_pwd_db ())
230 {
231 /* Try to map UNIX uid/gid to Cygwin uid/gid. If there's no mapping in
232 the cache, try to fetch it from the configured RFC 2307 domain (see
233 last comment in cygheap_domain_info::init() for more information) and
234 add it to the mapping cache. */
235 buf->st_uid = cygheap->ugid_cache.get_uid (nfs_attr->uid);
236 if (buf->st_uid == ILLEGAL_UID)
237 {
238 uid_t map_uid = ILLEGAL_UID;
239
240 domain = cygheap->dom.get_rfc2307_domain ();
241 if ((ldap_open = (cldap.open (domain) == NO_ERROR)))
242 map_uid = cldap.remap_uid (nfs_attr->uid);
243 if (map_uid == ILLEGAL_UID)
244 map_uid = MAP_UNIX_TO_CYGWIN_ID (nfs_attr->uid);
245 cygheap->ugid_cache.add_uid (nfs_attr->uid, map_uid);
246 buf->st_uid = map_uid;
247 }
248 }
249 else /* fake files being owned by current user. */
250 buf->st_uid = myself->uid;
251 if (cygheap->pg.nss_grp_db ())
252 {
253 /* See above */
254 buf->st_gid = cygheap->ugid_cache.get_gid (nfs_attr->gid);
255 if (buf->st_gid == ILLEGAL_GID)
256 {
257 gid_t map_gid = ILLEGAL_GID;
258
259 domain = cygheap->dom.get_rfc2307_domain ();
260 if ((ldap_open || cldap.open (domain) == NO_ERROR))
261 map_gid = cldap.remap_gid (nfs_attr->gid);
262 if (map_gid == ILLEGAL_GID)
263 map_gid = MAP_UNIX_TO_CYGWIN_ID (nfs_attr->gid);
264 cygheap->ugid_cache.add_gid (nfs_attr->gid, map_gid);
265 buf->st_gid = map_gid;
266 }
267 }
268 else /* fake files being owned by current group. */
269 buf->st_gid = myself->gid;
270 buf->st_rdev = makedev (nfs_attr->rdev.specdata1,
271 nfs_attr->rdev.specdata2);
272 buf->st_size = nfs_attr->size;
273 buf->st_blksize = PREFERRED_IO_BLKSIZE;
274 buf->st_blocks = (nfs_attr->used + S_BLKSIZE - 1) / S_BLKSIZE;
275 buf->st_atim.tv_sec = nfs_attr->atime.tv_sec;
276 buf->st_atim.tv_nsec = nfs_attr->atime.tv_nsec;
277 buf->st_mtim.tv_sec = nfs_attr->mtime.tv_sec;
278 buf->st_mtim.tv_nsec = nfs_attr->mtime.tv_nsec;
279 buf->st_ctim.tv_sec = nfs_attr->ctime.tv_sec;
280 buf->st_ctim.tv_nsec = nfs_attr->ctime.tv_nsec;
281 return 0;
282 }
283
284 int __reg2
285 fhandler_base::fstat_by_handle (struct stat *buf)
286 {
287 HANDLE h = get_stat_handle ();
288 NTSTATUS status = 0;
289
290 /* If the file has been opened for other purposes than stat, we can't rely
291 on the information stored in pc.fai. So we overwrite them here. */
292 if (get_io_handle ())
293 {
294 status = pc.get_finfo (h);
295 if (!NT_SUCCESS (status))
296 {
297 debug_printf ("%y = NtQueryInformationFile(%S, FileAllInformation)",
298 status, pc.get_nt_native_path ());
299 return -1;
300 }
301 }
302 if (pc.isgood_inode (pc.fai ()->InternalInformation.IndexNumber.QuadPart))
303 ino = pc.fai ()->InternalInformation.IndexNumber.QuadPart;
304 return fstat_helper (buf);
305 }
306
307 int __reg2
308 fhandler_base::fstat_by_name (struct stat *buf)
309 {
310 NTSTATUS status;
311 OBJECT_ATTRIBUTES attr;
312 IO_STATUS_BLOCK io;
313 UNICODE_STRING dirname;
314 UNICODE_STRING basename;
315 HANDLE dir;
316 struct {
317 FILE_ID_BOTH_DIR_INFORMATION fdi;
318 WCHAR buf[NAME_MAX + 1];
319 } fdi_buf;
320
321 if (!ino && pc.hasgood_inode () && !pc.has_buggy_fileid_dirinfo ())
322 {
323 RtlSplitUnicodePath (pc.get_nt_native_path (), &dirname, &basename);
324 InitializeObjectAttributes (&attr, &dirname, pc.objcaseinsensitive (),
325 NULL, NULL);
326 status = NtOpenFile (&dir, SYNCHRONIZE | FILE_LIST_DIRECTORY,
327 &attr, &io, FILE_SHARE_VALID_FLAGS,
328 FILE_SYNCHRONOUS_IO_NONALERT
329 | FILE_OPEN_FOR_BACKUP_INTENT
330 | FILE_DIRECTORY_FILE);
331 if (!NT_SUCCESS (status))
332 debug_printf ("%y = NtOpenFile(%S)", status,
333 pc.get_nt_native_path ());
334 else
335 {
336 status = NtQueryDirectoryFile (dir, NULL, NULL, NULL, &io,
337 &fdi_buf.fdi, sizeof fdi_buf,
338 FileIdBothDirectoryInformation,
339 TRUE, &basename, TRUE);
340 NtClose (dir);
341 if (!NT_SUCCESS (status))
342 debug_printf ("%y = NtQueryDirectoryFile(%S)", status,
343 pc.get_nt_native_path ());
344 else
345 ino = fdi_buf.fdi.FileId.QuadPart;
346 }
347 }
348 return fstat_helper (buf);
349 }
350
351 int __reg2
352 fhandler_base::fstat_fs (struct stat *buf)
353 {
354 int res = -1;
355 int oret;
356 int open_flags = O_RDONLY | O_BINARY;
357
358 if (get_stat_handle ())
359 {
360 if (!nohandle () && !is_fs_special ())
361 res = pc.fs_is_nfs () ? fstat_by_nfs_ea (buf) : fstat_by_handle (buf);
362 if (res)
363 res = fstat_by_name (buf);
364 return res;
365 }
366 /* First try to open with generic read access. This allows to read the file
367 in fstat_helper (when checking for executability) without having to
368 re-open it. Opening a file can take a lot of time on network drives
369 so we try to avoid that. */
370 oret = open_fs (open_flags, 0);
371 if (!oret)
372 {
373 query_open (query_read_attributes);
374 oret = open_fs (open_flags, 0);
375 }
376 if (oret)
377 {
378 /* We now have a valid handle, regardless of the "nohandle" state.
379 Since fhandler_base::close only calls CloseHandle if !nohandle,
380 we have to set it to false before calling close and restore
381 the state afterwards. */
382 res = pc.fs_is_nfs () ? fstat_by_nfs_ea (buf) : fstat_by_handle (buf);
383 bool no_handle = nohandle ();
384 nohandle (false);
385 close_fs ();
386 nohandle (no_handle);
387 set_io_handle (NULL);
388 }
389 if (res)
390 res = fstat_by_name (buf);
391
392 return res;
393 }
394
395 int __reg2
396 fhandler_base::fstat_helper (struct stat *buf)
397 {
398 IO_STATUS_BLOCK st;
399 FILE_COMPRESSION_INFORMATION fci;
400 HANDLE h = get_stat_handle ();
401 PFILE_ALL_INFORMATION pfai = pc.fai ();
402 ULONG attributes = pc.file_attributes ();
403
404 to_timestruc_t (&pfai->BasicInformation.LastAccessTime, &buf->st_atim);
405 to_timestruc_t (&pfai->BasicInformation.LastWriteTime, &buf->st_mtim);
406 /* If the ChangeTime is 0, the underlying FS doesn't support this timestamp
407 (FAT for instance). If so, it's faked using LastWriteTime. */
408 to_timestruc_t (pfai->BasicInformation.ChangeTime.QuadPart
409 ? &pfai->BasicInformation.ChangeTime
410 : &pfai->BasicInformation.LastWriteTime,
411 &buf->st_ctim);
412 to_timestruc_t (&pfai->BasicInformation.CreationTime, &buf->st_birthtim);
413 buf->st_dev = get_dev ();
414 /* CV 2011-01-13: Observations on the Cygwin mailing list point to an
415 interesting behaviour in some Windows versions. Apparently the size of
416 a directory is computed at the time the directory is first scanned. This
417 can result in two subsequent NtQueryInformationFile calls to return size
418 0 in the first call and size > 0 in the second call. This in turn can
419 affect applications like newer tar.
420 FIXME: Is the allocation size affected as well? */
421 buf->st_size = pc.isdir ()
422 ? 0
423 : (off_t) pfai->StandardInformation.EndOfFile.QuadPart;
424 /* The number of links to a directory includes the number of subdirectories
425 in the directory, since all those subdirectories point to it. However,
426 this is painfully slow, so we do without it. */
427 buf->st_nlink = pc.fai()->StandardInformation.NumberOfLinks;
428
429 /* Enforce namehash as inode number on untrusted file systems. */
430 buf->st_ino = ino ?: get_ino ();
431
432 buf->st_blksize = PREFERRED_IO_BLKSIZE;
433
434 if (buf->st_size == 0
435 && pfai->StandardInformation.AllocationSize.QuadPart == 0LL)
436 /* File is empty and no blocks are preallocated. */
437 buf->st_blocks = 0;
438 else if (pfai->StandardInformation.AllocationSize.QuadPart > 0LL)
439 /* A successful NtQueryInformationFile returns the allocation size
440 correctly for compressed and sparse files as well.
441 Allocation size 0 is ignored here because (at least) Windows 10
442 1607 always returns 0 for CompactOS compressed files. */
443 buf->st_blocks = (pfai->StandardInformation.AllocationSize.QuadPart
444 + S_BLKSIZE - 1) / S_BLKSIZE;
445 else if ((pfai->StandardInformation.AllocationSize.QuadPart == 0LL
446 || ::has_attribute (attributes, FILE_ATTRIBUTE_COMPRESSED
447 | FILE_ATTRIBUTE_SPARSE_FILE))
448 && h && !is_fs_special ()
449 && !NtQueryInformationFile (h, &st, (PVOID) &fci, sizeof fci,
450 FileCompressionInformation))
451 /* Otherwise we request the actual amount of bytes allocated for
452 compressed, sparsed and CompactOS files. */
453 buf->st_blocks = (fci.CompressedFileSize.QuadPart + S_BLKSIZE - 1)
454 / S_BLKSIZE;
455 else
456 /* Otherwise compute no. of blocks from file size. */
457 buf->st_blocks = (buf->st_size + S_BLKSIZE - 1) / S_BLKSIZE;
458
459 buf->st_mode = 0;
460 /* Using a side effect: get_file_attributes checks for directory.
461 This is used, to set S_ISVTX, if needed. */
462 if (pc.isdir ())
463 buf->st_mode = S_IFDIR;
464 else if (pc.issymlink ())
465 {
466 buf->st_size = pc.get_symlink_length ();
467 /* symlinks are everything for everyone! */
468 buf->st_mode = S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO;
469 get_file_attribute (h, pc, NULL,
470 &buf->st_uid, &buf->st_gid);
471 goto done;
472 }
473 else if (pc.issocket ())
474 buf->st_mode = S_IFSOCK;
475
476 if (!get_file_attribute (is_fs_special () && !pc.issocket () ? NULL : h, pc,
477 &buf->st_mode, &buf->st_uid, &buf->st_gid))
478 {
479 /* If read-only attribute is set, modify ntsec return value */
480 if (::has_attribute (attributes, FILE_ATTRIBUTE_READONLY)
481 && !pc.isdir () && !pc.issymlink ())
482 buf->st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
483
484 if (buf->st_mode & S_IFMT)
485 /* nothing */;
486 else if (!is_fs_special ())
487 buf->st_mode |= S_IFREG;
488 else
489 {
490 buf->st_dev = buf->st_rdev = dev ();
491 buf->st_mode = dev ().mode ();
492 buf->st_size = 0;
493 }
494 }
495 else
496 {
497 buf->st_mode |= STD_RBITS;
498
499 if (!::has_attribute (attributes, FILE_ATTRIBUTE_READONLY))
500 buf->st_mode |= STD_WBITS;
501 /* | S_IWGRP | S_IWOTH; we don't give write to group etc */
502
503 if (pc.isdir ())
504 buf->st_mode |= S_IFDIR | STD_WBITS | STD_XBITS;
505 else if (buf->st_mode & S_IFMT)
506 /* nothing */;
507 else if (is_fs_special ())
508 {
509 buf->st_dev = buf->st_rdev = dev ();
510 buf->st_mode = dev ().mode ();
511 buf->st_size = 0;
512 }
513 else
514 {
515 buf->st_mode |= S_IFREG;
516 /* Check suffix for executable file. */
517 if (pc.exec_state () != is_executable)
518 {
519 PUNICODE_STRING path = pc.get_nt_native_path ();
520
521 if (RtlEqualUnicodePathSuffix (path, &ro_u_exe, TRUE)
522 || RtlEqualUnicodePathSuffix (path, &ro_u_lnk, TRUE)
523 || RtlEqualUnicodePathSuffix (path, &ro_u_com, TRUE))
524 pc.set_exec ();
525 }
526 /* No known suffix, check file header. This catches binaries and
527 shebang scripts. */
528 if (pc.exec_state () == dont_know_if_executable)
529 {
530 OBJECT_ATTRIBUTES attr;
531 NTSTATUS status = 0;
532 IO_STATUS_BLOCK io;
533
534 /* We have to re-open the file. Either the file is not opened
535 for reading, or the read will change the file position of the
536 original handle. */
537 status = NtOpenFile (&h, SYNCHRONIZE | FILE_READ_DATA,
538 pc.init_reopen_attr (attr, h), &io,
539 FILE_SHARE_VALID_FLAGS,
540 FILE_OPEN_FOR_BACKUP_INTENT
541 | FILE_SYNCHRONOUS_IO_NONALERT);
542 if (!NT_SUCCESS (status))
543 debug_printf ("%y = NtOpenFile(%S)", status,
544 pc.get_nt_native_path ());
545 else
546 {
547 LARGE_INTEGER off = { QuadPart:0LL };
548 char magic[3];
549
550 status = NtReadFile (h, NULL, NULL, NULL,
551 &io, magic, 3, &off, NULL);
552 if (!NT_SUCCESS (status))
553 debug_printf ("%y = NtReadFile(%S)", status,
554 pc.get_nt_native_path ());
555 else if (has_exec_chars (magic, io.Information))
556 {
557 /* Heureka, it's an executable */
558 pc.set_exec ();
559 buf->st_mode |= STD_XBITS;
560 }
561 NtClose (h);
562 }
563 }
564 }
565 if (pc.exec_state () == is_executable)
566 buf->st_mode |= STD_XBITS;
567
568 /* This fakes the permissions of all files to match the current umask. */
569 buf->st_mode &= ~(cygheap->umask);
570 /* If the FS supports ACLs, we're here because we couldn't even open
571 the file for READ_CONTROL access. Chances are high that the file's
572 security descriptor has no ACE for "Everyone", so we should not fake
573 any access for "others". */
574 if (has_acls ())
575 buf->st_mode &= ~(S_IROTH | S_IWOTH | S_IXOTH);
576 }
577
578 done:
579 syscall_printf ("0 = fstat (%S, %p) st_size=%D, st_mode=0%o, st_ino=%D"
580 "st_atim=%lx.%lx st_ctim=%lx.%lx "
581 "st_mtim=%lx.%lx st_birthtim=%lx.%lx",
582 pc.get_nt_native_path (), buf,
583 buf->st_size, buf->st_mode, buf->st_ino,
584 buf->st_atim.tv_sec, buf->st_atim.tv_nsec,
585 buf->st_ctim.tv_sec, buf->st_ctim.tv_nsec,
586 buf->st_mtim.tv_sec, buf->st_mtim.tv_nsec,
587 buf->st_birthtim.tv_sec, buf->st_birthtim.tv_nsec);
588 return 0;
589 }
590
591 int __reg2
592 fhandler_disk_file::fstat (struct stat *buf)
593 {
594 return fstat_fs (buf);
595 }
596
597 int __reg2
598 fhandler_disk_file::fstatvfs (struct statvfs *sfs)
599 {
600 int ret = -1, opened = 0;
601 NTSTATUS status;
602 IO_STATUS_BLOCK io;
603 FILE_FS_FULL_SIZE_INFORMATION full_fsi;
604 /* We must not use the stat handle here, even if it exists. The handle
605 has been opened with FILE_OPEN_REPARSE_POINT, thus, in case of a volume
606 mount point, it points to the FS of the mount point, rather than to the
607 mounted FS. */
608 HANDLE fh = get_handle ();
609
610 if (!fh)
611 {
612 OBJECT_ATTRIBUTES attr;
613 opened = NT_SUCCESS (NtOpenFile (&fh, READ_CONTROL,
614 pc.get_object_attr (attr, sec_none_nih),
615 &io, FILE_SHARE_VALID_FLAGS,
616 FILE_OPEN_FOR_BACKUP_INTENT));
617 if (!opened)
618 {
619 /* Can't open file. Try again with parent dir. */
620 UNICODE_STRING dirname;
621 RtlSplitUnicodePath (pc.get_nt_native_path (), &dirname, NULL);
622 attr.ObjectName = &dirname;
623 opened = NT_SUCCESS (NtOpenFile (&fh, READ_CONTROL, &attr, &io,
624 FILE_SHARE_VALID_FLAGS,
625 FILE_OPEN_FOR_BACKUP_INTENT));
626 if (!opened)
627 goto out;
628 }
629 }
630
631 sfs->f_files = ULONG_MAX;
632 sfs->f_ffree = ULONG_MAX;
633 sfs->f_favail = ULONG_MAX;
634 sfs->f_fsid = pc.fs_serial_number ();
635 sfs->f_flag = pc.fs_flags ();
636 sfs->f_namemax = pc.fs_name_len ();
637 /* Get allocation related information. */
638 status = NtQueryVolumeInformationFile (fh, &io, &full_fsi, sizeof full_fsi,
639 FileFsFullSizeInformation);
640 if (NT_SUCCESS (status))
641 {
642 sfs->f_bsize = full_fsi.BytesPerSector * full_fsi.SectorsPerAllocationUnit;
643 sfs->f_frsize = sfs->f_bsize;
644 sfs->f_blocks = (fsblkcnt_t) full_fsi.TotalAllocationUnits.QuadPart;
645 sfs->f_bfree = (fsblkcnt_t)
646 full_fsi.ActualAvailableAllocationUnits.QuadPart;
647 sfs->f_bavail = (fsblkcnt_t)
648 full_fsi.CallerAvailableAllocationUnits.QuadPart;
649 if (sfs->f_bfree > sfs->f_bavail)
650 {
651 /* Quotas active. We can't trust TotalAllocationUnits. */
652 NTFS_VOLUME_DATA_BUFFER nvdb;
653
654 status = NtFsControlFile (fh, NULL, NULL, NULL, &io,
655 FSCTL_GET_NTFS_VOLUME_DATA,
656 NULL, 0, &nvdb, sizeof nvdb);
657 if (!NT_SUCCESS (status))
658 debug_printf ("%y = NtFsControlFile(%S, FSCTL_GET_NTFS_VOLUME_DATA)",
659 status, pc.get_nt_native_path ());
660 else
661 sfs->f_blocks = (fsblkcnt_t) nvdb.TotalClusters.QuadPart;
662 }
663 ret = 0;
664 }
665 else if (status == STATUS_INVALID_PARAMETER /* Netapp */
666 || status == STATUS_INVALID_INFO_CLASS)
667 {
668 FILE_FS_SIZE_INFORMATION fsi;
669 status = NtQueryVolumeInformationFile (fh, &io, &fsi, sizeof fsi,
670 FileFsSizeInformation);
671 if (NT_SUCCESS (status))
672 {
673 sfs->f_bsize = fsi.BytesPerSector * fsi.SectorsPerAllocationUnit;
674 sfs->f_frsize = sfs->f_bsize;
675 sfs->f_blocks = (fsblkcnt_t) fsi.TotalAllocationUnits.QuadPart;
676 sfs->f_bfree = sfs->f_bavail =
677 (fsblkcnt_t) fsi.AvailableAllocationUnits.QuadPart;
678 ret = 0;
679 }
680 else
681 debug_printf ("%y = NtQueryVolumeInformationFile"
682 "(%S, FileFsSizeInformation)",
683 status, pc.get_nt_native_path ());
684 }
685 else
686 debug_printf ("%y = NtQueryVolumeInformationFile"
687 "(%S, FileFsFullSizeInformation)",
688 status, pc.get_nt_native_path ());
689 out:
690 if (opened)
691 NtClose (fh);
692 syscall_printf ("%d = fstatvfs(%s, %p)", ret, get_name (), sfs);
693 return ret;
694 }
695
696 int __reg1
697 fhandler_disk_file::fchmod (mode_t mode)
698 {
699 extern int chmod_device (path_conv& pc, mode_t mode);
700 int ret = -1;
701 int oret = 0;
702 NTSTATUS status;
703 IO_STATUS_BLOCK io;
704
705 if (pc.is_fs_special ())
706 return chmod_device (pc, mode);
707
708 if (!get_handle ())
709 {
710 query_open (query_write_dac);
711 if (!(oret = open (O_BINARY, 0)))
712 {
713 /* Need WRITE_DAC to write ACLs. */
714 if (pc.has_acls ())
715 return -1;
716 /* Otherwise FILE_WRITE_ATTRIBUTES is sufficient. */
717 query_open (query_write_attributes);
718 if (!(oret = open (O_BINARY, 0)))
719 return -1;
720 }
721 }
722
723 if (pc.fs_is_nfs ())
724 {
725 /* chmod on NFS shares works by writing an EA of type NfsV3Attributes.
726 Only type and mode have to be set. Apparently type isn't checked
727 for consistency, so it's sufficent to set it to NF3REG all the time. */
728 struct {
729 FILE_FULL_EA_INFORMATION ffei;
730 char buf[sizeof (NFS_V3_ATTR) + sizeof (fattr3)];
731 } ffei_buf;
732 ffei_buf.ffei.NextEntryOffset = 0;
733 ffei_buf.ffei.Flags = 0;
734 ffei_buf.ffei.EaNameLength = sizeof (NFS_V3_ATTR) - 1;
735 ffei_buf.ffei.EaValueLength = sizeof (fattr3);
736 strcpy (ffei_buf.ffei.EaName, NFS_V3_ATTR);
737 fattr3 *nfs_attr = (fattr3 *) (ffei_buf.ffei.EaName
738 + ffei_buf.ffei.EaNameLength + 1);
739 memset (nfs_attr, 0, sizeof (fattr3));
740 nfs_attr->type = NF3REG;
741 nfs_attr->mode = mode;
742 status = NtSetEaFile (get_handle (), &io,
743 &ffei_buf.ffei, sizeof ffei_buf);
744 if (!NT_SUCCESS (status))
745 __seterrno_from_nt_status (status);
746 else
747 ret = 0;
748 goto out;
749 }
750
751 if (pc.has_acls ())
752 {
753 security_descriptor sd, sd_ret;
754 uid_t uid;
755 gid_t gid;
756 tmp_pathbuf tp;
757 aclent_t *aclp;
758 bool standard_acl = false;
759 int nentries, idx;
760
761 if (!get_file_sd (get_handle (), pc, sd, false))
762 {
763 aclp = (aclent_t *) tp.c_get ();
764 if ((nentries = get_posix_access (sd, NULL, &uid, &gid,
765 aclp, MAX_ACL_ENTRIES,
766 &standard_acl)) >= 0)
767 {
768 /* Overwrite ACL permissions as required by POSIX 1003.1e
769 draft 17. */
770 aclp[0].a_perm = (mode >> 6) & S_IRWXO;
771
772 /* POSIXly correct: If CLASS_OBJ is present, chmod only modifies
773 CLASS_OBJ, not GROUP_OBJ.
774
775 Deliberate deviation from POSIX 1003.1e: If the ACL is a
776 "standard" ACL, that is, it only contains POSIX permissions
777 as well as entries for the Administrators group and SYSTEM,
778 then it's kind of a POSIX-only ACL in a twisted, Windowsy
779 way. If so, we change GROUP_OBJ and CLASS_OBJ perms. */
780 if (standard_acl
781 && (idx = searchace (aclp, nentries, GROUP_OBJ)) >= 0)
782 aclp[idx].a_perm = (mode >> 3) & S_IRWXO;
783 if (nentries > MIN_ACL_ENTRIES
784 && (idx = searchace (aclp, nentries, CLASS_OBJ)) >= 0)
785 aclp[idx].a_perm = (mode >> 3) & S_IRWXO;
786
787 if ((idx = searchace (aclp, nentries, OTHER_OBJ)) >= 0)
788 aclp[idx].a_perm = mode & S_IRWXO;
789 if (pc.isdir ())
790 mode |= S_IFDIR;
791 if (set_posix_access (mode, uid, gid, aclp, nentries, sd_ret,
792 pc.fs_is_samba ()))
793 ret = set_file_sd (get_handle (), pc, sd_ret, false);
794 }
795 }
796 }
797
798 /* If the mode has any write bits set, the DOS R/O flag is in the way. */
799 if (mode & (S_IWUSR | S_IWGRP | S_IWOTH))
800 pc &= (DWORD) ~FILE_ATTRIBUTE_READONLY;
801 else if (!pc.has_acls ()) /* Never set DOS R/O if security is used. */
802 pc |= (DWORD) FILE_ATTRIBUTE_READONLY;
803 if (S_ISSOCK (mode))
804 pc |= (DWORD) FILE_ATTRIBUTE_SYSTEM;
805
806 status = NtSetAttributesFile (get_handle (), pc.file_attributes ());
807 /* MVFS needs a good amount of kicking to be convinced that it has to write
808 back metadata changes and to invalidate the cached metadata information
809 stored for the given handle. This method to open a second handle to
810 the file and write the same metadata information twice has been found
811 experimentally: http://cygwin.com/ml/cygwin/2009-07/msg00533.html */
812 if (pc.fs_is_mvfs () && NT_SUCCESS (status) && !oret)
813 {
814 OBJECT_ATTRIBUTES attr;
815 HANDLE fh;
816
817 if (NT_SUCCESS (NtOpenFile (&fh, FILE_WRITE_ATTRIBUTES,
818 pc.init_reopen_attr (attr, get_handle ()),
819 &io, FILE_SHARE_VALID_FLAGS,
820 FILE_OPEN_FOR_BACKUP_INTENT)))
821 {
822 NtSetAttributesFile (fh, pc.file_attributes ());
823 NtClose (fh);
824 }
825 }
826 /* Correct NTFS security attributes have higher priority */
827 if (!pc.has_acls ())
828 {
829 if (!NT_SUCCESS (status))
830 __seterrno_from_nt_status (status);
831 else
832 ret = 0;
833 }
834
835 out:
836 if (oret)
837 close_fs ();
838
839 return ret;
840 }
841
842 int __reg2
843 fhandler_disk_file::fchown (uid_t uid, gid_t gid)
844 {
845 int oret = 0;
846 int ret = -1;
847 security_descriptor sd, sd_ret;
848 mode_t attr = pc.isdir () ? S_IFDIR : 0;
849 uid_t old_uid;
850 gid_t old_gid;
851 tmp_pathbuf tp;
852 aclent_t *aclp;
853 int nentries;
854
855 if (!pc.has_acls ())
856 {
857 /* fake - if not supported, pretend we're like win95
858 where it just works */
859 /* FIXME: Could be supported on NFS when user->uid mapping is in place. */
860 return 0;
861 }
862
863 if (!get_handle ())
864 {
865 query_open (query_write_control);
866 if (!(oret = fhandler_disk_file::open (O_BINARY, 0)))
867 return -1;
868 }
869
870 if (get_file_sd (get_handle (), pc, sd, false))
871 goto out;
872
873 aclp = (aclent_t *) tp.c_get ();
874 if ((nentries = get_posix_access (sd, &attr, &old_uid, &old_gid,
875 aclp, MAX_ACL_ENTRIES)) < 0)
876 goto out;
877
878 if (uid == ILLEGAL_UID)
879 uid = old_uid;
880 if (gid == ILLEGAL_GID)
881 gid = old_gid;
882 if (uid == old_uid && gid == old_gid)
883 {
884 ret = 0;
885 goto out;
886 }
887
888 /* Windows ACLs can contain permissions for one group, while being owned by
889 another user/group. The permission bits returned above are pretty much
890 useless then. Creating a new ACL with these useless permissions results
891 in a potentially broken symlink. So what we do here is to set the
892 underlying permissions of symlinks to a sensible value which allows the
893 world to read the symlink and only the new owner to change it. */
894 if (pc.issymlink ())
895 for (int idx = 0; idx < nentries; ++idx)
896 {
897 aclp[idx].a_perm |= S_IROTH;
898 if (aclp[idx].a_type & USER_OBJ)
899 aclp[idx].a_perm |= S_IWOTH;
900 }
901
902 if (set_posix_access (attr, uid, gid, aclp, nentries, sd_ret,
903 pc.fs_is_samba ()))
904 ret = set_file_sd (get_handle (), pc, sd_ret, true);
905
906 /* If you're running a Samba server with no winbind, the uid<->SID mapping
907 is disfunctional. Even trying to chown to your own account fails since
908 the account used on the server is the UNIX account which gets used for
909 the standard user mapping. This is a default mechanism which doesn't
910 know your real Windows SID. There are two possible error codes in
911 different Samba releases for this situation, one of them unfortunately
912 the not very significant STATUS_ACCESS_DENIED. Instead of relying on
913 the error codes, we're using the below very simple heuristic.
914 If set_file_sd failed, and the original user account was either already
915 unknown, or one of the standard UNIX accounts, we're faking success. */
916 if (ret == -1 && pc.fs_is_samba ())
917 {
918 PSID sid;
919
920 if (uid == old_uid
921 || ((sid = sidfromuid (old_uid, NULL)) != NO_SID
922 && RtlEqualPrefixSid (sid,
923 well_known_samba_unix_user_fake_sid)))
924 {
925 debug_printf ("Faking chown worked on standalone Samba");
926 ret = 0;
927 }
928 }
929
930 out:
931 if (oret)
932 close_fs ();
933
934 return ret;
935 }
936
937 int __reg3
938 fhandler_disk_file::facl (int cmd, int nentries, aclent_t *aclbufp)
939 {
940 int res = -1;
941 int oret = 0;
942
943 if (!pc.has_acls ())
944 {
945 cant_access_acl:
946 switch (cmd)
947 {
948
949 case SETACL:
950 /* Open for writing required to be able to set ctime
951 (even though setting the ACL is just pretended). */
952 if (!get_handle ())
953 oret = open (O_WRONLY | O_BINARY, 0);
954 res = 0;
955 break;
956 case GETACL:
957 if (!aclbufp)
958 set_errno (EFAULT);
959 else if (nentries < MIN_ACL_ENTRIES)
960 set_errno (ENOSPC);
961 else
962 {
963 struct stat st;
964 if (!fstat (&st))
965 {
966 aclbufp[0].a_type = USER_OBJ;
967 aclbufp[0].a_id = st.st_uid;
968 aclbufp[0].a_perm = (st.st_mode & S_IRWXU) >> 6;
969 aclbufp[1].a_type = GROUP_OBJ;
970 aclbufp[1].a_id = st.st_gid;
971 aclbufp[1].a_perm = (st.st_mode & S_IRWXG) >> 3;
972 aclbufp[2].a_type = OTHER_OBJ;
973 aclbufp[2].a_id = ILLEGAL_GID;
974 aclbufp[2].a_perm = st.st_mode & S_IRWXO;
975 res = MIN_ACL_ENTRIES;
976 }
977 }
978 break;
979 case GETACLCNT:
980 res = MIN_ACL_ENTRIES;
981 break;
982 default:
983 set_errno (EINVAL);
984 break;
985 }
986 }
987 else
988 {
989 if ((cmd == SETACL && !get_handle ())
990 || (cmd != SETACL && !get_stat_handle ()))
991 {
992 query_open (cmd == SETACL ? query_write_dac : query_read_control);
993 if (!(oret = open (O_BINARY, 0)))
994 {
995 if (cmd == GETACL || cmd == GETACLCNT)
996 goto cant_access_acl;
997 return -1;
998 }
999 }
1000 switch (cmd)
1001 {
1002 case SETACL:
1003 if (!aclsort32 (nentries, 0, aclbufp))
1004 {
1005 bool rw = false;
1006 res = setacl (get_handle (), pc, nentries, aclbufp, rw);
1007 if (rw)
1008 {
1009 IO_STATUS_BLOCK io;
1010 FILE_BASIC_INFORMATION fbi;
1011 fbi.CreationTime.QuadPart
1012 = fbi.LastAccessTime.QuadPart
1013 = fbi.LastWriteTime.QuadPart
1014 = fbi.ChangeTime.QuadPart = 0LL;
1015 fbi.FileAttributes = (pc.file_attributes ()
1016 & ~FILE_ATTRIBUTE_READONLY)
1017 ?: FILE_ATTRIBUTE_NORMAL;
1018 NtSetInformationFile (get_handle (), &io, &fbi, sizeof fbi,
1019 FileBasicInformation);
1020 }
1021 }
1022 break;
1023 case GETACL:
1024 if (!aclbufp)
1025 set_errno(EFAULT);
1026 else {
1027 res = getacl (get_stat_handle (), pc, nentries, aclbufp);
1028 /* For this ENOSYS case, see security.cc:get_file_attribute(). */
1029 if (res == -1 && get_errno () == ENOSYS)
1030 goto cant_access_acl;
1031 }
1032 break;
1033 case GETACLCNT:
1034 res = getacl (get_stat_handle (), pc, 0, NULL);
1035 /* Ditto. */
1036 if (res == -1 && get_errno () == ENOSYS)
1037 goto cant_access_acl;
1038 break;
1039 default:
1040 set_errno (EINVAL);
1041 break;
1042 }
1043 }
1044
1045 if (oret)
1046 close_fs ();
1047
1048 return res;
1049 }
1050
1051 ssize_t
1052 fhandler_disk_file::fgetxattr (const char *name, void *value, size_t size)
1053 {
1054 if (pc.is_fs_special ())
1055 {
1056 set_errno (ENOTSUP);
1057 return -1;
1058 }
1059 return read_ea (get_handle (), pc, name, (char *) value, size);
1060 }
1061
1062 int
1063 fhandler_disk_file::fsetxattr (const char *name, const void *value, size_t size,
1064 int flags)
1065 {
1066 if (pc.is_fs_special ())
1067 {
1068 set_errno (ENOTSUP);
1069 return -1;
1070 }
1071 return write_ea (get_handle (), pc, name, (const char *) value, size, flags);
1072 }
1073
1074 int
1075 fhandler_disk_file::fadvise (off_t offset, off_t length, int advice)
1076 {
1077 if (advice < POSIX_FADV_NORMAL || advice > POSIX_FADV_NOREUSE)
1078 return EINVAL;
1079
1080 /* Windows only supports advice flags for the whole file. We're using
1081 a simplified test here so that we don't have to ask for the actual
1082 file size. Length == 0 means all bytes starting at offset anyway.
1083 So we only actually follow the advice, if it's given for offset == 0. */
1084 if (offset != 0)
1085 return 0;
1086
1087 /* We only support normal and sequential mode for now. Everything which
1088 is not POSIX_FADV_SEQUENTIAL is treated like POSIX_FADV_NORMAL. */
1089 if (advice != POSIX_FADV_SEQUENTIAL)
1090 advice = POSIX_FADV_NORMAL;
1091
1092 IO_STATUS_BLOCK io;
1093 FILE_MODE_INFORMATION fmi;
1094 NTSTATUS status = NtQueryInformationFile (get_handle (), &io,
1095 &fmi, sizeof fmi,
1096 FileModeInformation);
1097 if (NT_SUCCESS (status))
1098 {
1099 fmi.Mode &= ~FILE_SEQUENTIAL_ONLY;
1100 if (advice == POSIX_FADV_SEQUENTIAL)
1101 fmi.Mode |= FILE_SEQUENTIAL_ONLY;
1102 status = NtSetInformationFile (get_handle (), &io, &fmi, sizeof fmi,
1103 FileModeInformation);
1104 if (NT_SUCCESS (status))
1105 return 0;
1106 __seterrno_from_nt_status (status);
1107 }
1108
1109 return geterrno_from_nt_status (status);
1110 }
1111
1112 int
1113 fhandler_disk_file::ftruncate (off_t length, bool allow_truncate)
1114 {
1115 int res = 0;
1116
1117 if (length < 0 || !get_handle ())
1118 res = EINVAL;
1119 else if (pc.isdir ())
1120 res = EISDIR;
1121 else if (!(get_access () & GENERIC_WRITE))
1122 res = EBADF;
1123 else
1124 {
1125 NTSTATUS status;
1126 IO_STATUS_BLOCK io;
1127 FILE_STANDARD_INFORMATION fsi;
1128 FILE_END_OF_FILE_INFORMATION feofi;
1129
1130 status = NtQueryInformationFile (get_handle (), &io, &fsi, sizeof fsi,
1131 FileStandardInformation);
1132 if (!NT_SUCCESS (status))
1133 return geterrno_from_nt_status (status);
1134
1135 /* If called through posix_fallocate, silently succeed if length
1136 is less than the file's actual length. */
1137 if (!allow_truncate && length < fsi.EndOfFile.QuadPart)
1138 return 0;
1139
1140 feofi.EndOfFile.QuadPart = length;
1141 /* Create sparse files only when called through ftruncate, not when
1142 called through posix_fallocate. */
1143 if (allow_truncate && pc.support_sparse ()
1144 && !has_attribute (FILE_ATTRIBUTE_SPARSE_FILE)
1145 && length >= fsi.EndOfFile.QuadPart + (128 * 1024))
1146 {
1147 status = NtFsControlFile (get_handle (), NULL, NULL, NULL, &io,
1148 FSCTL_SET_SPARSE, NULL, 0, NULL, 0);
1149 if (NT_SUCCESS (status))
1150 pc.file_attributes (pc.file_attributes ()
1151 | FILE_ATTRIBUTE_SPARSE_FILE);
1152 syscall_printf ("%y = NtFsControlFile(%S, FSCTL_SET_SPARSE)",
1153 status, pc.get_nt_native_path ());
1154 }
1155 status = NtSetInformationFile (get_handle (), &io,
1156 &feofi, sizeof feofi,
1157 FileEndOfFileInformation);
1158 if (!NT_SUCCESS (status))
1159 res = geterrno_from_nt_status (status);
1160 }
1161 return res;
1162 }
1163
1164 int
1165 fhandler_disk_file::link (const char *newpath)
1166 {
1167 size_t nlen = strlen (newpath);
1168 path_conv newpc (newpath, PC_SYM_NOFOLLOW | PC_POSIX | PC_NULLEMPTY, stat_suffixes);
1169 if (newpc.error)
1170 {
1171 set_errno (newpc.error);
1172 return -1;
1173 }
1174
1175 if (newpc.exists ())
1176 {
1177 syscall_printf ("file '%S' exists?", newpc.get_nt_native_path ());
1178 set_errno (EEXIST);
1179 return -1;
1180 }
1181
1182 if (isdirsep (newpath[nlen - 1]) || has_dot_last_component (newpath, false))
1183 {
1184 set_errno (ENOENT);
1185 return -1;
1186 }
1187
1188 char new_buf[nlen + 5];
1189 if (!newpc.error)
1190 {
1191 /* If the original file is a lnk special file (except for sockets),
1192 and if the original file has a .lnk suffix, add one to the hardlink
1193 as well. */
1194 if (pc.is_lnk_special () && !pc.issocket ()
1195 && RtlEqualUnicodePathSuffix (pc.get_nt_native_path (),
1196 &ro_u_lnk, TRUE))
1197 {
1198 /* Shortcut hack. */
1199 stpcpy (stpcpy (new_buf, newpath), ".lnk");
1200 newpath = new_buf;
1201 newpc.check (newpath, PC_SYM_NOFOLLOW);
1202 }
1203 else if (!pc.isdir ()
1204 && pc.is_binary ()
1205 && RtlEqualUnicodePathSuffix (pc.get_nt_native_path (),
1206 &ro_u_exe, TRUE)
1207 && !RtlEqualUnicodePathSuffix (newpc.get_nt_native_path (),
1208 &ro_u_exe, TRUE))
1209 {
1210 /* Executable hack. */
1211 stpcpy (stpcpy (new_buf, newpath), ".exe");
1212 newpath = new_buf;
1213 newpc.check (newpath, PC_SYM_NOFOLLOW);
1214 }
1215 }
1216
1217 /* We only need READ_CONTROL access so the handle returned in pc is
1218 sufficient. And if the file couldn't be opened with READ_CONTROL
1219 access in path_conv, we won't be able to do it here anyway. */
1220 HANDLE fh = get_stat_handle ();
1221 if (!fh)
1222 {
1223 set_errno (EACCES);
1224 return -1;
1225 }
1226 PUNICODE_STRING tgt = newpc.get_nt_native_path ();
1227 ULONG size = sizeof (FILE_LINK_INFORMATION) + tgt->Length;
1228 PFILE_LINK_INFORMATION pfli = (PFILE_LINK_INFORMATION) alloca (size);
1229 pfli->ReplaceIfExists = FALSE;
1230 pfli->RootDirectory = NULL;
1231 memcpy (pfli->FileName, tgt->Buffer, pfli->FileNameLength = tgt->Length);
1232
1233 NTSTATUS status;
1234 IO_STATUS_BLOCK io;
1235 status = NtSetInformationFile (fh, &io, pfli, size, FileLinkInformation);
1236 if (!NT_SUCCESS (status))
1237 {
1238 if (status == STATUS_INVALID_DEVICE_REQUEST
1239 || status == STATUS_NOT_SUPPORTED)
1240 /* FS doesn't support hard links. Linux returns EPERM. */
1241 set_errno (EPERM);
1242 else
1243 __seterrno_from_nt_status (status);
1244 return -1;
1245 }
1246 else if (pc.file_attributes () & FILE_ATTRIBUTE_TEMPORARY)
1247 {
1248 /* If the original file has been opened with O_TMPFILE the file has
1249 FILE_ATTRIBUTE_TEMPORARY set. After a successful hardlink the
1250 file is not temporary anymore in the usual sense. So we remove
1251 FILE_ATTRIBUTE_TEMPORARY here, even if this makes the original file
1252 visible in directory enumeration. */
1253 OBJECT_ATTRIBUTES attr;
1254 status = NtOpenFile (&fh, FILE_WRITE_ATTRIBUTES,
1255 pc.init_reopen_attr (attr, fh), &io,
1256 FILE_SHARE_VALID_FLAGS, FILE_OPEN_FOR_BACKUP_INTENT);
1257 if (!NT_SUCCESS (status))
1258 debug_printf ("Opening for removing TEMPORARY attrib failed, "
1259 "status = %y", status);
1260 else
1261 {
1262 FILE_BASIC_INFORMATION fbi;
1263
1264 fbi.CreationTime.QuadPart = fbi.LastAccessTime.QuadPart
1265 = fbi.LastWriteTime.QuadPart = fbi.ChangeTime.QuadPart = 0LL;
1266 fbi.FileAttributes = (pc.file_attributes ()
1267 & ~FILE_ATTRIBUTE_TEMPORARY)
1268 ?: FILE_ATTRIBUTE_NORMAL;
1269 status = NtSetInformationFile (fh, &io, &fbi, sizeof fbi,
1270 FileBasicInformation);
1271 if (!NT_SUCCESS (status))
1272 debug_printf ("Removing the TEMPORARY attrib failed, status = %y",
1273 status);
1274 NtClose (fh);
1275 }
1276 }
1277 return 0;
1278 }
1279
1280 int
1281 fhandler_disk_file::utimens (const struct timespec *tvp)
1282 {
1283 return utimens_fs (tvp);
1284 }
1285
1286 int
1287 fhandler_base::utimens_fs (const struct timespec *tvp)
1288 {
1289 struct timespec timeofday;
1290 struct timespec tmp[2];
1291 bool closeit = false;
1292
1293 if (!get_handle ())
1294 {
1295 query_open (query_write_attributes);
1296 if (!open_fs (O_BINARY, 0))
1297 {
1298 /* It's documented in MSDN that FILE_WRITE_ATTRIBUTES is sufficient
1299 to change the timestamps. Unfortunately it's not sufficient for a
1300 remote HPFS which requires GENERIC_WRITE, so we just retry to open
1301 for writing, though this fails for R/O files of course. */
1302 query_open (no_query);
1303 if (!open_fs (O_WRONLY | O_BINARY, 0))
1304 {
1305 syscall_printf ("Opening file failed");
1306 return -1;
1307 }
1308 }
1309 closeit = true;
1310 }
1311
1312 clock_gettime (CLOCK_REALTIME, &timeofday);
1313 if (!tvp)
1314 tmp[1] = tmp[0] = timeofday;
1315 else
1316 {
1317 if ((tvp[0].tv_nsec < UTIME_NOW || tvp[0].tv_nsec > 999999999L)
1318 || (tvp[1].tv_nsec < UTIME_NOW || tvp[1].tv_nsec > 999999999L))
1319 {
1320 if (closeit)
1321 close_fs ();
1322 set_errno (EINVAL);
1323 return -1;
1324 }
1325 tmp[0] = (tvp[0].tv_nsec == UTIME_NOW) ? timeofday : tvp[0];
1326 tmp[1] = (tvp[1].tv_nsec == UTIME_NOW) ? timeofday : tvp[1];
1327 }
1328 debug_printf ("incoming lastaccess %ly %ly", tmp[0].tv_sec, tmp[0].tv_nsec);
1329
1330 IO_STATUS_BLOCK io;
1331 FILE_BASIC_INFORMATION fbi;
1332
1333 fbi.CreationTime.QuadPart = 0LL;
1334 /* UTIME_OMIT is handled in timespec_to_filetime by setting FILETIME to 0. */
1335 timespec_to_filetime (&tmp[0], &fbi.LastAccessTime);
1336 timespec_to_filetime (&tmp[1], &fbi.LastWriteTime);
1337 fbi.ChangeTime.QuadPart = 0LL;
1338 fbi.FileAttributes = 0;
1339 NTSTATUS status = NtSetInformationFile (get_handle (), &io, &fbi, sizeof fbi,
1340 FileBasicInformation);
1341 /* For this special case for MVFS see the comment in
1342 fhandler_disk_file::fchmod. */
1343 if (pc.fs_is_mvfs () && NT_SUCCESS (status) && !closeit)
1344 {
1345 OBJECT_ATTRIBUTES attr;
1346 HANDLE fh;
1347
1348 if (NT_SUCCESS (NtOpenFile (&fh, FILE_WRITE_ATTRIBUTES,
1349 pc.init_reopen_attr (attr, get_handle ()),
1350 &io, FILE_SHARE_VALID_FLAGS,
1351 FILE_OPEN_FOR_BACKUP_INTENT)))
1352 {
1353 NtSetInformationFile (fh, &io, &fbi, sizeof fbi,
1354 FileBasicInformation);
1355 NtClose (fh);
1356 }
1357 }
1358 if (closeit)
1359 close_fs ();
1360 /* Opening a directory on a 9x share from a NT machine works(!), but
1361 then NtSetInformationFile fails with STATUS_NOT_SUPPORTED. Oh well... */
1362 if (!NT_SUCCESS (status) && status != STATUS_NOT_SUPPORTED)
1363 {
1364 __seterrno_from_nt_status (status);
1365 return -1;
1366 }
1367 return 0;
1368 }
1369
1370 fhandler_disk_file::fhandler_disk_file () :
1371 fhandler_base (), prw_handle (NULL)
1372 {
1373 }
1374
1375 fhandler_disk_file::fhandler_disk_file (path_conv &pc) :
1376 fhandler_base (), prw_handle (NULL)
1377 {
1378 set_name (pc);
1379 }
1380
1381 int
1382 fhandler_disk_file::open (int flags, mode_t mode)
1383 {
1384 return open_fs (flags, mode);
1385 }
1386
1387 int
1388 fhandler_disk_file::close ()
1389 {
1390 /* Close extra pread/pwrite handle, if it exists. */
1391 if (prw_handle)
1392 NtClose (prw_handle);
1393 return fhandler_base::close ();
1394 }
1395
1396 int
1397 fhandler_disk_file::fcntl (int cmd, intptr_t arg)
1398 {
1399 int res;
1400
1401 switch (cmd)
1402 {
1403 case F_LCK_MANDATORY: /* Mandatory locking only works on files. */
1404 mandatory_locking (!!arg);
1405 need_fork_fixup (true);
1406 res = 0;
1407 break;
1408 default:
1409 res = fhandler_base::fcntl (cmd, arg);
1410 break;
1411 }
1412 return res;
1413 }
1414
1415 int
1416 fhandler_disk_file::dup (fhandler_base *child, int flags)
1417 {
1418 fhandler_disk_file *fhc = (fhandler_disk_file *) child;
1419
1420 int ret = fhandler_base::dup (child, flags);
1421 if (!ret && prw_handle
1422 && !DuplicateHandle (GetCurrentProcess (), prw_handle,
1423 GetCurrentProcess (), &fhc->prw_handle,
1424 0, TRUE, DUPLICATE_SAME_ACCESS))
1425 fhc->prw_handle = NULL;
1426 return ret;
1427 }
1428
1429 void
1430 fhandler_disk_file::fixup_after_fork (HANDLE parent)
1431 {
1432 prw_handle = NULL;
1433 mandatory_locking (false);
1434 fhandler_base::fixup_after_fork (parent);
1435 }
1436
1437 int
1438 fhandler_base::open_fs (int flags, mode_t mode)
1439 {
1440 /* Unfortunately NT allows to open directories for writing, but that's
1441 disallowed according to SUSv3. */
1442 if (pc.isdir () && (flags & O_ACCMODE) != O_RDONLY)
1443 {
1444 set_errno (EISDIR);
1445 return 0;
1446 }
1447
1448 bool new_file = !exists ();
1449
1450 int res = fhandler_base::open (flags | O_DIROPEN, mode);
1451 if (res)
1452 {
1453 /* The file info in pc is wrong at this point for newly created files.
1454 Refresh it before fetching any file info. */
1455 if (new_file)
1456 pc.get_finfo (get_io_handle ());
1457
1458 if (pc.isgood_inode (pc.get_ino ()))
1459 ino = pc.get_ino ();
1460 }
1461
1462 syscall_printf ("%d = fhandler_disk_file::open(%S, %y)", res,
1463 pc.get_nt_native_path (), flags);
1464 return res;
1465 }
1466
1467 /* POSIX demands that pread/pwrite don't change the current file position.
1468 While NtReadFile/NtWriteFile support atomic seek-and-io, both change the
1469 file pointer if the file handle has been opened for synchonous I/O.
1470 Using this handle for pread/pwrite would break atomicity, because the
1471 read/write operation would have to be followed by a seek back to the old
1472 file position. What we do is to open another handle to the file on the
1473 first call to either pread or pwrite. This is used for any subsequent
1474 pread/pwrite. Thus the current file position of the "normal" file
1475 handle is not touched.
1476
1477 FIXME:
1478
1479 Note that this is just a hack. The problem with this approach is that
1480 a change to the file permissions might disallow to open the file with
1481 the required permissions to read or write. This appears to be a border case,
1482 but that's exactly what git does. It creates the file for reading and
1483 writing and after writing it, it chmods the file to read-only. Then it
1484 calls pread on the file to examine the content. This works, but if git
1485 would use the original handle to pwrite to the file, it would be broken
1486 with our approach.
1487
1488 One way to implement this is to open the pread/pwrite handle right at
1489 file open time. We would simply maintain two handles, which wouldn't
1490 be much of a problem given how we do that for other fhandler types as
1491 well.
1492
1493 However, ultimately fhandler_disk_file should become a derived class of
1494 fhandler_base_overlapped. Each raw_read or raw_write would fetch the
1495 actual file position, read/write from there, and then set the file
1496 position again. Fortunately, while the file position is not maintained
1497 by the I/O manager, it can be fetched and set to a new value by all
1498 processes holding a handle to that file object. Pread and pwrite differ
1499 from raw_read and raw_write just by not touching the current file pos.
1500 Actually they could be merged with raw_read/raw_write if we add a position
1501 parameter to the latter. */
1502
1503 int
1504 fhandler_disk_file::prw_open (bool write)
1505 {
1506 NTSTATUS status;
1507 IO_STATUS_BLOCK io;
1508 OBJECT_ATTRIBUTES attr;
1509
1510 /* First try to open with the original access mask */
1511 ACCESS_MASK access = get_access ();
1512 status = NtOpenFile (&prw_handle, access,
1513 pc.init_reopen_attr (attr, get_handle ()), &io,
1514 FILE_SHARE_VALID_FLAGS, get_options ());
1515 if (status == STATUS_ACCESS_DENIED)
1516 {
1517 /* If we get an access denied, chmod has been called. Try again
1518 with just the required rights to perform the called function. */
1519 access &= write ? ~GENERIC_READ : ~GENERIC_WRITE;
1520 status = NtOpenFile (&prw_handle, access, &attr, &io,
1521 FILE_SHARE_VALID_FLAGS, get_options ());
1522 }
1523 debug_printf ("%y = NtOpenFile (%p, %y, %S, io, %y, %y)",
1524 status, prw_handle, access, pc.get_nt_native_path (),
1525 FILE_SHARE_VALID_FLAGS, get_options ());
1526 if (!NT_SUCCESS (status))
1527 {
1528 __seterrno_from_nt_status (status);
1529 return -1;
1530 }
1531 return 0;
1532 }
1533
1534 ssize_t __reg3
1535 fhandler_disk_file::pread (void *buf, size_t count, off_t offset)
1536 {
1537 ssize_t res;
1538
1539 if ((get_flags () & O_ACCMODE) == O_WRONLY)
1540 {
1541 set_errno (EBADF);
1542 return -1;
1543 }
1544
1545 /* In binary mode, we can use an atomic NtReadFile call.
1546 Windows mandatory locking semantics disallow to use another HANDLE. */
1547 if (rbinary () && !mandatory_locking ())
1548 {
1549 extern int __stdcall is_at_eof (HANDLE h);
1550 NTSTATUS status;
1551 IO_STATUS_BLOCK io;
1552 LARGE_INTEGER off = { QuadPart:offset };
1553
1554 if (!prw_handle && prw_open (false))
1555 goto non_atomic;
1556 status = NtReadFile (prw_handle, NULL, NULL, NULL, &io, buf, count,
1557 &off, NULL);
1558 if (status == STATUS_END_OF_FILE)
1559 res = 0;
1560 else if (!NT_SUCCESS (status))
1561 {
1562 if (pc.isdir ())
1563 {
1564 set_errno (EISDIR);
1565 return -1;
1566 }
1567 if (status == (NTSTATUS) STATUS_ACCESS_VIOLATION)
1568 {
1569 if (is_at_eof (prw_handle))
1570 {
1571 res = 0;
1572 goto out;
1573 }
1574 switch (mmap_is_attached_or_noreserve (buf, count))
1575 {
1576 case MMAP_NORESERVE_COMMITED:
1577 status = NtReadFile (prw_handle, NULL, NULL, NULL, &io,
1578 buf, count, &off, NULL);
1579 if (NT_SUCCESS (status))
1580 {
1581 res = io.Information;
1582 goto out;
1583 }
1584 break;
1585 case MMAP_RAISE_SIGBUS:
1586 raise (SIGBUS);
1587 default:
1588 break;
1589 }
1590 }
1591 __seterrno_from_nt_status (status);
1592 return -1;
1593 }
1594 else
1595 res = io.Information;
1596 }
1597 else
1598 {
1599 non_atomic:
1600 /* Text mode stays slow and non-atomic. */
1601 off_t curpos = lseek (0, SEEK_CUR);
1602 if (curpos < 0 || lseek (offset, SEEK_SET) < 0)
1603 res = -1;
1604 else
1605 {
1606 size_t tmp_count = count;
1607 read (buf, tmp_count);
1608 if (lseek (curpos, SEEK_SET) >= 0)
1609 res = (ssize_t) tmp_count;
1610 else
1611 res = -1;
1612 }
1613 }
1614 out:
1615 debug_printf ("%d = pread(%p, %ld, %D)\n", res, buf, count, offset);
1616 return res;
1617 }
1618
1619 ssize_t __reg3
1620 fhandler_disk_file::pwrite (void *buf, size_t count, off_t offset)
1621 {
1622 if ((get_flags () & O_ACCMODE) == O_RDONLY)
1623 {
1624 set_errno (EBADF);
1625 return -1;
1626 }
1627
1628 /* In binary mode, we can use an atomic NtWriteFile call.
1629 Windows mandatory locking semantics disallow to use another HANDLE. */
1630 if (wbinary () && !mandatory_locking ())
1631 {
1632 NTSTATUS status;
1633 IO_STATUS_BLOCK io;
1634 LARGE_INTEGER off = { QuadPart:offset };
1635
1636 if (!prw_handle && prw_open (true))
1637 goto non_atomic;
1638 status = NtWriteFile (prw_handle, NULL, NULL, NULL, &io, buf, count,
1639 &off, NULL);
1640 if (!NT_SUCCESS (status))
1641 {
1642 __seterrno_from_nt_status (status);
1643 return -1;
1644 }
1645 return io.Information;
1646 }
1647
1648 non_atomic:
1649 /* Text mode stays slow and non-atomic. */
1650 int res;
1651 off_t curpos = lseek (0, SEEK_CUR);
1652 if (curpos < 0 || lseek (offset, SEEK_SET) < 0)
1653 res = curpos;
1654 else
1655 {
1656 res = (ssize_t) write (buf, count);
1657 if (lseek (curpos, SEEK_SET) < 0)
1658 res = -1;
1659 }
1660 debug_printf ("%d = pwrite(%p, %ld, %D)\n", res, buf, count, offset);
1661 return res;
1662 }
1663
1664 int
1665 fhandler_disk_file::mkdir (mode_t mode)
1666 {
1667 int res = -1;
1668 SECURITY_ATTRIBUTES sa = sec_none_nih;
1669 NTSTATUS status;
1670 HANDLE dir;
1671 OBJECT_ATTRIBUTES attr;
1672 IO_STATUS_BLOCK io;
1673 PFILE_FULL_EA_INFORMATION p = NULL;
1674 ULONG plen = 0;
1675 ULONG access = FILE_LIST_DIRECTORY | SYNCHRONIZE;
1676
1677 if (pc.fs_is_nfs ())
1678 {
1679 /* When creating a dir on an NFS share, we have to set the
1680 file mode by writing a NFS fattr3 structure with the
1681 correct mode bits set. */
1682 plen = sizeof (FILE_FULL_EA_INFORMATION) + sizeof (NFS_V3_ATTR)
1683 + sizeof (fattr3);
1684 p = (PFILE_FULL_EA_INFORMATION) alloca (plen);
1685 p->NextEntryOffset = 0;
1686 p->Flags = 0;
1687 p->EaNameLength = sizeof (NFS_V3_ATTR) - 1;
1688 p->EaValueLength = sizeof (fattr3);
1689 strcpy (p->EaName, NFS_V3_ATTR);
1690 fattr3 *nfs_attr = (fattr3 *) (p->EaName + p->EaNameLength + 1);
1691 memset (nfs_attr, 0, sizeof (fattr3));
1692 nfs_attr->type = NF3DIR;
1693 nfs_attr->mode = (mode & 07777) & ~cygheap->umask;
1694 }
1695 else if (has_acls () && !isremote ())
1696 /* If the filesystem supports ACLs, we will overwrite the DACL after the
1697 call to NtCreateFile. This requires a handle with READ_CONTROL and
1698 WRITE_DAC access, otherwise get_file_sd and set_file_sd both have to
1699 open the file again.
1700 FIXME: On remote NTFS shares open sometimes fails because even the
1701 creator of the file doesn't have the right to change the DACL.
1702 I don't know what setting that is or how to recognize such a share,
1703 so for now we don't request WRITE_DAC on remote drives. */
1704 access |= READ_CONTROL | WRITE_DAC;
1705 status = NtCreateFile (&dir, access, pc.get_object_attr (attr, sa), &io, NULL,
1706 FILE_ATTRIBUTE_DIRECTORY, FILE_SHARE_VALID_FLAGS,
1707 FILE_CREATE,
1708 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT
1709 | FILE_OPEN_FOR_BACKUP_INTENT,
1710 p, plen);
1711 if (NT_SUCCESS (status))
1712 {
1713 /* Set the "directory attribute" so that pc.isdir() returns correct
1714 value in subsequent function calls. */
1715 pc.file_attributes (FILE_ATTRIBUTE_DIRECTORY);
1716 if (has_acls ())
1717 set_created_file_access (dir, pc, mode & 07777);
1718 NtClose (dir);
1719 res = 0;
1720 }
1721 else
1722 __seterrno_from_nt_status (status);
1723
1724 return res;
1725 }
1726
1727 int
1728 fhandler_disk_file::rmdir ()
1729 {
1730 extern NTSTATUS unlink_nt (path_conv &pc);
1731
1732 if (!pc.isdir ())
1733 {
1734 set_errno (ENOTDIR);
1735 return -1;
1736 }
1737 if (!pc.exists ())
1738 {
1739 set_errno (ENOENT);
1740 return -1;
1741 }
1742
1743 NTSTATUS status = unlink_nt (pc);
1744
1745 /* Check for existence of remote dirs after trying to delete them.
1746 Two reasons:
1747 - Sometimes SMB indicates failure when it really succeeds.
1748 - Removing a directory on a Samba drive using an old Samba version
1749 sometimes doesn't return an error, if the directory can't be removed
1750 because it's not empty. */
1751 if (isremote ())
1752 {
1753 OBJECT_ATTRIBUTES attr;
1754 FILE_BASIC_INFORMATION fbi;
1755 NTSTATUS q_status;
1756
1757 q_status = NtQueryAttributesFile (pc.get_object_attr (attr, sec_none_nih),
1758 &fbi);
1759 if (!NT_SUCCESS (status) && q_status == STATUS_OBJECT_NAME_NOT_FOUND)
1760 status = STATUS_SUCCESS;
1761 else if (pc.fs_is_samba ()
1762 && NT_SUCCESS (status) && NT_SUCCESS (q_status))
1763 status = STATUS_DIRECTORY_NOT_EMPTY;
1764 }
1765 if (!NT_SUCCESS (status))
1766 {
1767 __seterrno_from_nt_status (status);
1768 return -1;
1769 }
1770 return 0;
1771 }
1772
1773 /* This is the minimal number of entries which fit into the readdir cache.
1774 The number of bytes allocated by the cache is determined by this number,
1775 To tune caching, just tweak this number. To get a feeling for the size,
1776 the size of the readdir cache is DIR_NUM_ENTRIES * 624 + 4 bytes. */
1777
1778 #define DIR_NUM_ENTRIES 100 /* Cache size 62404 bytes */
1779
1780 #define DIR_BUF_SIZE (DIR_NUM_ENTRIES \
1781 * (sizeof (FILE_ID_BOTH_DIR_INFORMATION) \
1782 + (NAME_MAX + 1) * sizeof (WCHAR)))
1783
1784 struct __DIR_cache
1785 {
1786 char __cache[DIR_BUF_SIZE];
1787 ULONG __pos;
1788 };
1789
1790 #define d_cachepos(d) (((__DIR_cache *) (d)->__d_dirname)->__pos)
1791 #define d_cache(d) (((__DIR_cache *) (d)->__d_dirname)->__cache)
1792
1793 #define d_mounts(d) ((__DIR_mounts *) (d)->__d_internal)
1794
1795 DIR *
1796 fhandler_disk_file::opendir (int fd)
1797 {
1798 DIR *dir;
1799 DIR *res = NULL;
1800
1801 if (!pc.isdir ())
1802 set_errno (ENOTDIR);
1803 else if ((dir = (DIR *) malloc (sizeof (DIR))) == NULL)
1804 set_errno (ENOMEM);
1805 else if ((dir->__d_dirname = (char *) malloc ( sizeof (struct __DIR_cache)))
1806 == NULL)
1807 {
1808 set_errno (ENOMEM);
1809 goto free_dir;
1810 }
1811 else if ((dir->__d_dirent =
1812 (struct dirent *) malloc (sizeof (struct dirent))) == NULL)
1813 {
1814 set_errno (ENOMEM);
1815 goto free_dirname;
1816 }
1817 else
1818 {
1819 cygheap_fdnew cfd;
1820 if (cfd < 0 && fd < 0)
1821 goto free_dirent;
1822
1823 dir->__d_dirent->__d_version = __DIRENT_VERSION;
1824 dir->__d_cookie = __DIRENT_COOKIE;
1825 dir->__handle = INVALID_HANDLE_VALUE;
1826 dir->__d_position = 0;
1827 dir->__flags = (get_name ()[0] == '/' && get_name ()[1] == '\0')
1828 ? dirent_isroot : 0;
1829 dir->__d_internal = 0;
1830
1831 if (pc.iscygdrive ())
1832 {
1833 if (fd < 0 && !open (O_RDONLY, 0))
1834 goto free_mounts;
1835 }
1836 else
1837 {
1838 dir->__d_internal = (uintptr_t) new __DIR_mounts (get_name ());
1839 d_cachepos (dir) = 0;
1840 if (fd < 0)
1841 {
1842 /* opendir() case. Initialize with given directory name and
1843 NULL directory handle. */
1844 OBJECT_ATTRIBUTES attr;
1845 NTSTATUS status;
1846 IO_STATUS_BLOCK io;
1847 /* Tools like ls(1) call dirfd() to fetch the directory
1848 descriptor for calls to facl or fstat. The tight access mask
1849 used so far is not sufficient to reuse the handle for these
1850 calls, instead the facl/fstat calls find the handle to be
1851 unusable and have to re-open the file for reading attributes
1852 and control data. So, what we do here is to try to open the
1853 directory with more relaxed access mask which enables to use
1854 the handle for the aforementioned purpose. This should work
1855 in almost all cases. Only if it doesn't work due to
1856 permission problems, we drop the additional access bits and
1857 try again. */
1858 ACCESS_MASK fstat_mask = READ_CONTROL | FILE_READ_ATTRIBUTES;
1859
1860 do
1861 {
1862 status = NtOpenFile (&get_handle (),
1863 SYNCHRONIZE | FILE_LIST_DIRECTORY
1864 | fstat_mask,
1865 pc.get_object_attr (attr, sec_none_nih),
1866 &io, FILE_SHARE_VALID_FLAGS,
1867 FILE_SYNCHRONOUS_IO_NONALERT
1868 | FILE_OPEN_FOR_BACKUP_INTENT
1869 | FILE_DIRECTORY_FILE);
1870 if (!NT_SUCCESS (status))
1871 {
1872 if (status == STATUS_ACCESS_DENIED && fstat_mask)
1873 fstat_mask = 0;
1874 else
1875 {
1876 __seterrno_from_nt_status (status);
1877 goto free_mounts;
1878 }
1879 }
1880 }
1881 while (!NT_SUCCESS (status));
1882 }
1883
1884 /* FileIdBothDirectoryInformation was unsupported on XP when
1885 accessing UDF. It's not clear if the call isn't also unsupported
1886 on other OS/FS combinations. Instead of testing for yet another
1887 error code, use FileIdBothDirectoryInformation only on FSes
1888 supporting persistent ACLs.
1889
1890 NFS clients hide dangling symlinks from directory queries,
1891 unless you use the FileNamesInformation info class.
1892 FileIdBothDirectoryInformation works fine, but only if the NFS
1893 share is mounted to a drive letter. TODO: We don't test that
1894 here for now, but it might be worth to test if there's a speed
1895 gain in using FileIdBothDirectoryInformation, because it doesn't
1896 require to open the file to read the inode number. */
1897 if (pc.hasgood_inode ())
1898 {
1899 dir->__flags |= dirent_set_d_ino;
1900 if (pc.fs_is_nfs ())
1901 dir->__flags |= dirent_nfs_d_ino;
1902 else if (!pc.has_buggy_fileid_dirinfo ())
1903 dir->__flags |= dirent_get_d_ino;
1904 }
1905 }
1906 if (fd >= 0)
1907 dir->__d_fd = fd;
1908 else
1909 {
1910 /* Filling cfd with `this' (aka storing this in the file
1911 descriptor table should only happen after it's clear that
1912 opendir doesn't fail, otherwise we end up cfree'ing the
1913 fhandler twice, once in opendir() in dir.cc, the second
1914 time on exit. Nasty, nasty... */
1915 cfd = this;
1916 dir->__d_fd = cfd;
1917 }
1918 set_close_on_exec (true);
1919 dir->__fh = this;
1920 res = dir;
1921 }
1922
1923 syscall_printf ("%p = opendir (%s)", res, get_name ());
1924 return res;
1925
1926 free_mounts:
1927 delete d_mounts (dir);
1928 free_dirent:
1929 free (dir->__d_dirent);
1930 free_dirname:
1931 free (dir->__d_dirname);
1932 free_dir:
1933 free (dir);
1934 return res;
1935 }
1936
1937 ino_t __reg2
1938 readdir_get_ino (const char *path, bool dot_dot)
1939 {
1940 char *fname;
1941 struct stat st;
1942 HANDLE hdl;
1943 OBJECT_ATTRIBUTES attr;
1944 IO_STATUS_BLOCK io;
1945 ino_t ino = 0;
1946
1947 if (dot_dot)
1948 {
1949 fname = (char *) alloca (strlen (path) + 4);
1950 char *c = stpcpy (fname, path);
1951 if (c[-1] != '/')
1952 *c++ = '/';
1953 strcpy (c, "..");
1954 path = fname;
1955 }
1956 path_conv pc (path, PC_SYM_NOFOLLOW | PC_POSIX | PC_NOWARN | PC_KEEP_HANDLE);
1957 if (pc.isspecial ())
1958 {
1959 if (!stat_worker (pc, &st))
1960 ino = st.st_ino;
1961 }
1962 else if (!pc.hasgood_inode ())
1963 ino = hash_path_name (0, pc.get_nt_native_path ());
1964 else if ((hdl = pc.handle ()) != NULL
1965 || NT_SUCCESS (NtOpenFile (&hdl, READ_CONTROL,
1966 pc.get_object_attr (attr, sec_none_nih),
1967 &io, FILE_SHARE_VALID_FLAGS,
1968 FILE_OPEN_FOR_BACKUP_INTENT
1969 | (pc.is_rep_symlink ()
1970 ? FILE_OPEN_REPARSE_POINT : 0)))
1971 )
1972 {
1973 ino = pc.get_ino_by_handle (hdl);
1974 if (!ino)
1975 ino = hash_path_name (0, pc.get_nt_native_path ());
1976 }
1977 return ino;
1978 }
1979
1980 int
1981 fhandler_disk_file::readdir_helper (DIR *dir, dirent *de, DWORD w32_err,
1982 DWORD attr, PUNICODE_STRING fname)
1983 {
1984 if (w32_err)
1985 {
1986 switch (d_mounts (dir)->check_missing_mount (fname))
1987 {
1988 case __DIR_mount_none:
1989 fname->Length = 0;
1990 return geterrno_from_win_error (w32_err);
1991 case __DIR_mount_virt_target:
1992 de->d_type = DT_DIR;
1993 break;
1994 default:
1995 break;
1996 }
1997 attr = 0;
1998 dir->__flags &= ~dirent_set_d_ino;
1999 }
2000
2001 /* Set d_type if type can be determined from file attributes. For .lnk
2002 symlinks, d_type will be reset below. Reparse points can be NTFS
2003 symlinks, even if they have the FILE_ATTRIBUTE_DIRECTORY flag set. */
2004 if (attr && !(attr & ~FILE_ATTRIBUTE_VALID_FLAGS))
2005 {
2006 if (attr & FILE_ATTRIBUTE_DIRECTORY)
2007 de->d_type = DT_DIR;
2008 /* FILE_ATTRIBUTE_SYSTEM might denote system-bit type symlinks. */
2009 else if (!(attr & FILE_ATTRIBUTE_SYSTEM))
2010 de->d_type = DT_REG;
2011 }
2012
2013 /* Check for reparse points that can be treated as posix symlinks.
2014 Mountpoints and unknown or unhandled reparse points will be treated
2015 as normal file/directory/unknown. In all cases, returning the INO of
2016 the reparse point (not of the target) matches behavior of posix systems.
2017 */
2018 if (attr & FILE_ATTRIBUTE_REPARSE_POINT)
2019 {
2020 OBJECT_ATTRIBUTES oattr;
2021
2022 InitializeObjectAttributes (&oattr, fname, pc.objcaseinsensitive (),
2023 get_handle (), NULL);
2024 if (readdir_check_reparse_point (&oattr, isremote ()))
2025 de->d_type = DT_LNK;
2026 }
2027
2028 /* Check for Windows shortcut. If it's a Cygwin or U/WIN symlink, drop the
2029 .lnk suffix and set d_type accordingly. */
2030 if ((attr & (FILE_ATTRIBUTE_DIRECTORY
2031 | FILE_ATTRIBUTE_REPARSE_POINT
2032 | FILE_ATTRIBUTE_READONLY)) == FILE_ATTRIBUTE_READONLY
2033 && fname->Length > 4 * sizeof (WCHAR))
2034 {
2035 UNICODE_STRING uname;
2036
2037 RtlInitCountedUnicodeString (&uname,
2038 fname->Buffer
2039 + fname->Length / sizeof (WCHAR) - 4,
2040 4 * sizeof (WCHAR));
2041 if (RtlEqualUnicodeString (&uname, &ro_u_lnk, TRUE))
2042 {
2043 tmp_pathbuf tp;
2044 char *file = tp.c_get ();
2045 char *p = stpcpy (file, pc.get_posix ());
2046 if (p[-1] != '/')
2047 *p++ = '/';
2048 sys_wcstombs (p, NT_MAX_PATH - (p - file),
2049 fname->Buffer, fname->Length / sizeof (WCHAR));
2050 path_conv fpath (file, PC_SYM_NOFOLLOW);
2051 if (fpath.issymlink ())
2052 {
2053 fname->Length -= 4 * sizeof (WCHAR);
2054 de->d_type = DT_LNK;
2055 }
2056 else if (fpath.isfifo ())
2057 {
2058 fname->Length -= 4 * sizeof (WCHAR);
2059 de->d_type = DT_FIFO;
2060 }
2061 else if (fpath.is_fs_special ())
2062 {
2063 fname->Length -= 4 * sizeof (WCHAR);
2064 de->d_type = S_ISCHR (fpath.dev.mode ()) ? DT_CHR : DT_BLK;
2065 }
2066 }
2067 }
2068
2069 sys_wcstombs (de->d_name, NAME_MAX + 1, fname->Buffer,
2070 fname->Length / sizeof (WCHAR));
2071
2072 /* Don't try to optimize relative to dir->__d_position. On several
2073 filesystems it's no safe bet that "." and ".." entries always
2074 come first. */
2075 if (de->d_name[0] == '.')
2076 {
2077 if (de->d_name[1] == '\0')
2078 dir->__flags |= dirent_saw_dot;
2079 else if (de->d_name[1] == '.' && de->d_name[2] == '\0')
2080 dir->__flags |= dirent_saw_dot_dot;
2081 }
2082 return 0;
2083 }
2084
2085 int
2086 fhandler_disk_file::readdir (DIR *dir, dirent *de)
2087 {
2088 int res = 0;
2089 NTSTATUS status = STATUS_SUCCESS;
2090 PFILE_ID_BOTH_DIR_INFORMATION buf = NULL;
2091 PWCHAR FileName;
2092 ULONG FileNameLength;
2093 ULONG FileAttributes;
2094 IO_STATUS_BLOCK io;
2095 UNICODE_STRING fname;
2096
2097 /* d_cachepos always refers to the next cache entry to use. If it's 0
2098 we must reload the cache. */
2099 restart:
2100 FileAttributes = 0;
2101 if (d_cachepos (dir) == 0)
2102 {
2103 if ((dir->__flags & dirent_get_d_ino))
2104 {
2105 status = NtQueryDirectoryFile (get_handle (), NULL, NULL, NULL, &io,
2106 d_cache (dir), DIR_BUF_SIZE,
2107 FileIdBothDirectoryInformation,
2108 FALSE, NULL, dir->__d_position == 0);
2109 /* FileIdBothDirectoryInformation isn't supported for remote drives
2110 on NT4 and 2K systems. There are also hacked versions of
2111 Samba 3.0.x out there (Debian-based it seems), which return
2112 STATUS_NOT_SUPPORTED rather than handling this info class.
2113 We just fall back to using a standard directory query in
2114 this case and note this case using the dirent_get_d_ino flag. */
2115 if (!NT_SUCCESS (status) && status != STATUS_NO_MORE_FILES
2116 && (status == STATUS_INVALID_LEVEL
2117 || status == STATUS_NOT_SUPPORTED
2118 || status == STATUS_INVALID_PARAMETER
2119 || status == STATUS_INVALID_NETWORK_RESPONSE
2120 || status == STATUS_INVALID_INFO_CLASS))
2121 dir->__flags &= ~dirent_get_d_ino;
2122 /* Something weird happens on Samba up to version 3.0.21c, which is
2123 fixed in 3.0.22. FileIdBothDirectoryInformation seems to work
2124 nicely, but only up to the 128th entry in the directory. After
2125 reaching this entry, the next call to NtQueryDirectoryFile
2126 (FileIdBothDirectoryInformation) returns STATUS_INVALID_LEVEL.
2127 Why should we care, we can just switch to
2128 FileBothDirectoryInformation, isn't it? Nope! The next call to
2129 NtQueryDirectoryFile(FileBothDirectoryInformation) actually
2130 returns STATUS_NO_MORE_FILES, regardless how many files are left
2131 unread in the directory. This does not happen when using
2132 FileBothDirectoryInformation right from the start, but since
2133 we can't decide whether the server we're talking with has this
2134 bug or not, we end up serving Samba shares always in the slow
2135 mode using FileBothDirectoryInformation. So, what we do here is
2136 to implement the solution suggested by Andrew Tridgell, we just
2137 reread all entries up to dir->d_position using
2138 FileBothDirectoryInformation.
2139 However, We do *not* mark this server as broken and fall back to
2140 using FileBothDirectoryInformation further on. This would slow
2141 down every access to such a server, even for directories under
2142 128 entries. Also, bigger dirs only suffer from one additional
2143 call per full directory scan, which shouldn't be too big a hit.
2144 This can easily be changed if necessary. */
2145 if (status == STATUS_INVALID_LEVEL && dir->__d_position)
2146 {
2147 d_cachepos (dir) = 0;
2148 for (int cnt = 0; cnt < dir->__d_position; ++cnt)
2149 {
2150 if (d_cachepos (dir) == 0)
2151 {
2152 status = NtQueryDirectoryFile (get_handle (), NULL, NULL,
2153 NULL, &io, d_cache (dir),
2154 DIR_BUF_SIZE,
2155 FileBothDirectoryInformation,
2156 FALSE, NULL, cnt == 0);
2157 if (!NT_SUCCESS (status))
2158 goto go_ahead;
2159 }
2160 buf = (PFILE_ID_BOTH_DIR_INFORMATION) (d_cache (dir)
2161 + d_cachepos (dir));
2162 if (buf->NextEntryOffset == 0)
2163 d_cachepos (dir) = 0;
2164 else
2165 d_cachepos (dir) += buf->NextEntryOffset;
2166 }
2167 goto go_ahead;
2168 }
2169 }
2170 if (!(dir->__flags & dirent_get_d_ino))
2171 status = NtQueryDirectoryFile (get_handle (), NULL, NULL, NULL, &io,
2172 d_cache (dir), DIR_BUF_SIZE,
2173 (dir->__flags & dirent_nfs_d_ino)
2174 ? FileNamesInformation
2175 : FileBothDirectoryInformation,
2176 FALSE, NULL, dir->__d_position == 0);
2177 }
2178
2179 go_ahead:
2180
2181 if (status == STATUS_NO_MORE_FILES)
2182 /*nothing*/;
2183 else if (!NT_SUCCESS (status))
2184 debug_printf ("NtQueryDirectoryFile failed, status %y, win32 error %u",
2185 status, RtlNtStatusToDosError (status));
2186 else
2187 {
2188 buf = (PFILE_ID_BOTH_DIR_INFORMATION) (d_cache (dir) + d_cachepos (dir));
2189 if (buf->NextEntryOffset == 0)
2190 d_cachepos (dir) = 0;
2191 else
2192 d_cachepos (dir) += buf->NextEntryOffset;
2193 if ((dir->__flags & dirent_get_d_ino))
2194 {
2195 FileName = buf->FileName;
2196 FileNameLength = buf->FileNameLength;
2197 FileAttributes = buf->FileAttributes;
2198 if ((dir->__flags & dirent_set_d_ino))
2199 de->d_ino = buf->FileId.QuadPart;
2200 }
2201 else if ((dir->__flags & dirent_nfs_d_ino))
2202 {
2203 FileName = ((PFILE_NAMES_INFORMATION) buf)->FileName;
2204 FileNameLength = ((PFILE_NAMES_INFORMATION) buf)->FileNameLength;
2205 }
2206 else
2207 {
2208 FileName = ((PFILE_BOTH_DIR_INFORMATION) buf)->FileName;
2209 FileNameLength =
2210 ((PFILE_BOTH_DIR_INFORMATION) buf)->FileNameLength;
2211 FileAttributes =
2212 ((PFILE_BOTH_DIR_INFORMATION) buf)->FileAttributes;
2213 }
2214 /* We don't show O_TMPFILE files in the filesystem. This is a kludge,
2215 so we may end up removing this snippet again. */
2216 if (FileAttributes & FILE_ATTRIBUTE_TEMPORARY)
2217 goto restart;
2218 RtlInitCountedUnicodeString (&fname, FileName, FileNameLength);
2219 d_mounts (dir)->check_mount (&fname);
2220 if (de->d_ino == 0 && (dir->__flags & dirent_set_d_ino))
2221 {
2222 /* Don't try to optimize relative to dir->__d_position. On several
2223 filesystems it's no safe bet that "." and ".." entries always
2224 come first. */
2225 if (FileNameLength == sizeof (WCHAR) && FileName[0] == '.')
2226 de->d_ino = pc.get_ino_by_handle (get_handle ());
2227 else if (FileNameLength == 2 * sizeof (WCHAR)
2228 && FileName[0] == L'.' && FileName[1] == L'.')
2229 {
2230 if (!(dir->__flags & dirent_isroot))
2231 de->d_ino = readdir_get_ino (get_name (), true);
2232 else
2233 de->d_ino = pc.get_ino_by_handle (get_handle ());
2234 }
2235 else
2236 {
2237 OBJECT_ATTRIBUTES attr;
2238 HANDLE hdl;
2239 NTSTATUS f_status;
2240
2241 InitializeObjectAttributes (&attr, &fname,
2242 pc.objcaseinsensitive (),
2243 get_handle (), NULL);
2244 /* FILE_OPEN_REPARSE_POINT on NFS is a no-op, so the normal
2245 NtOpenFile here returns the inode number of the symlink target,
2246 rather than the inode number of the symlink itself.
2247
2248 Worse, trying to open a symlink without setting the special
2249 "ActOnSymlink" EA triggers a bug in Windows 7 which results
2250 in a timeout of up to 20 seconds, followed by two exceptions
2251 in the NT kernel.
2252
2253 Since both results are far from desirable, we open symlinks
2254 on NFS so that we get the right inode and a happy W7.
2255 And, since some filesystems choke on the EAs, we don't
2256 use them unconditionally. */
2257 f_status = (dir->__flags & dirent_nfs_d_ino)
2258 ? NtCreateFile (&hdl, READ_CONTROL, &attr, &io,
2259 NULL, 0, FILE_SHARE_VALID_FLAGS,
2260 FILE_OPEN, FILE_OPEN_FOR_BACKUP_INTENT,
2261 &nfs_aol_ffei, sizeof nfs_aol_ffei)
2262 : NtOpenFile (&hdl, READ_CONTROL, &attr, &io,
2263 FILE_SHARE_VALID_FLAGS,
2264 FILE_OPEN_FOR_BACKUP_INTENT
2265 | FILE_OPEN_REPARSE_POINT);
2266 if (NT_SUCCESS (f_status))
2267 {
2268 /* We call NtQueryInformationFile here, rather than
2269 pc.get_ino_by_handle(), otherwise we can't short-circuit
2270 dirent_set_d_ino correctly. */
2271 FILE_INTERNAL_INFORMATION fii;
2272 f_status = NtQueryInformationFile (hdl, &io, &fii, sizeof fii,
2273 FileInternalInformation);
2274 NtClose (hdl);
2275 if (NT_SUCCESS (f_status))
2276 {
2277 if (pc.isgood_inode (fii.IndexNumber.QuadPart))
2278 de->d_ino = fii.IndexNumber.QuadPart;
2279 else
2280 /* Untrusted file system. Don't try to fetch inode
2281 number again. */
2282 dir->__flags &= ~dirent_set_d_ino;
2283 }
2284 }
2285 }
2286 }
2287 }
2288
2289 if (!(res = readdir_helper (dir, de, RtlNtStatusToDosError (status),
2290 FileAttributes, &fname)))
2291 dir->__d_position++;
2292 else if (!(dir->__flags & dirent_saw_dot))
2293 {
2294 strcpy (de->d_name , ".");
2295 de->d_ino = pc.get_ino_by_handle (get_handle ());
2296 de->d_type = DT_DIR;
2297 dir->__d_position++;
2298 dir->__flags |= dirent_saw_dot;
2299 res = 0;
2300 }
2301 else if (!(dir->__flags & dirent_saw_dot_dot))
2302 {
2303 strcpy (de->d_name , "..");
2304 if (!(dir->__flags & dirent_isroot))
2305 de->d_ino = readdir_get_ino (get_name (), true);
2306 else
2307 de->d_ino = pc.get_ino_by_handle (get_handle ());
2308 de->d_type = DT_DIR;
2309 dir->__d_position++;
2310 dir->__flags |= dirent_saw_dot_dot;
2311 res = 0;
2312 }
2313
2314 syscall_printf ("%d = readdir(%p, %p) (L\"%lS\" > \"%ls\") (attr %y > type %d)",
2315 res, dir, &de, res ? NULL : &fname, res ? "***" : de->d_name,
2316 FileAttributes, de->d_type);
2317 return res;
2318 }
2319
2320 long
2321 fhandler_disk_file::telldir (DIR *dir)
2322 {
2323 return dir->__d_position;
2324 }
2325
2326 void
2327 fhandler_disk_file::seekdir (DIR *dir, long loc)
2328 {
2329 rewinddir (dir);
2330 while (loc > dir->__d_position)
2331 if (!::readdir (dir))
2332 break;
2333 }
2334
2335 void
2336 fhandler_disk_file::rewinddir (DIR *dir)
2337 {
2338 d_cachepos (dir) = 0;
2339 dir->__d_position = 0;
2340 d_mounts (dir)->rewind ();
2341 }
2342
2343 int
2344 fhandler_disk_file::closedir (DIR *dir)
2345 {
2346 int res = 0;
2347
2348 delete d_mounts (dir);
2349 syscall_printf ("%d = closedir(%p, %s)", res, dir, get_name ());
2350 return res;
2351 }
2352
2353 fhandler_cygdrive::fhandler_cygdrive () :
2354 fhandler_disk_file (), ndrives (0), pdrive (NULL)
2355 {
2356 }
2357
2358 int
2359 fhandler_cygdrive::open (int flags, mode_t mode)
2360 {
2361 if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
2362 {
2363 set_errno (EEXIST);
2364 return 0;
2365 }
2366 if (flags & O_WRONLY)
2367 {
2368 set_errno (EISDIR);
2369 return 0;
2370 }
2371 /* Open a fake handle to \\Device\\Null */
2372 return open_null (flags);
2373 }
2374
2375 void
2376 fhandler_cygdrive::set_drives ()
2377 {
2378 pdrive = pdrive_buf;
2379 ndrives = GetLogicalDriveStrings (sizeof pdrive_buf, pdrive_buf) / DRVSZ;
2380 }
2381
2382 int
2383 fhandler_cygdrive::fstat (struct stat *buf)
2384 {
2385 fhandler_base::fstat (buf);
2386 buf->st_ino = 2;
2387 buf->st_mode = S_IFDIR | STD_RBITS | STD_XBITS;
2388 buf->st_nlink = 1;
2389 return 0;
2390 }
2391
2392 int __reg2
2393 fhandler_cygdrive::fstatvfs (struct statvfs *sfs)
2394 {
2395 /* Virtual file system. Just return an empty buffer with a few values
2396 set to something useful. Just as on Linux. */
2397 memset (sfs, 0, sizeof (*sfs));
2398 sfs->f_bsize = sfs->f_frsize = 4096;
2399 sfs->f_flag = ST_RDONLY;
2400 sfs->f_namemax = NAME_MAX;
2401 return 0;
2402 }
2403
2404 DIR *
2405 fhandler_cygdrive::opendir (int fd)
2406 {
2407 DIR *dir;
2408
2409 dir = fhandler_disk_file::opendir (fd);
2410 if (dir && !ndrives)
2411 set_drives ();
2412
2413 return dir;
2414 }
2415
2416 int
2417 fhandler_cygdrive::readdir (DIR *dir, dirent *de)
2418 {
2419 WCHAR drive[] = L"X:";
2420
2421 while (true)
2422 {
2423 if (!pdrive || !*pdrive)
2424 {
2425 if (!(dir->__flags & dirent_saw_dot))
2426 {
2427 de->d_name[0] = '.';
2428 de->d_name[1] = '\0';
2429 de->d_ino = 2;
2430 }
2431 return ENMFILE;
2432 }
2433 disk_type dt = get_disk_type ((drive[0] = *pdrive, drive));
2434 if (dt == DT_SHARE_SMB)
2435 {
2436 /* Calling NetUseGetInfo on SMB drives allows to fetch the
2437 current state of the drive without trying to open a file
2438 descriptor on the share (GetFileAttributes). This avoids
2439 waiting for SMB timeouts. Of course, there's a downside:
2440 If a drive becomes availabe again, it can take a couple of
2441 minutes to recognize it. As long as this didn't happen,
2442 the drive will not show up in the cygdrive dir. */
2443 PUSE_INFO_1 pui1;
2444 DWORD status;
2445
2446 if (NetUseGetInfo (NULL, drive, 1, (PBYTE *) &pui1) == NERR_Success)
2447 {
2448 status = pui1->ui1_status;
2449 NetApiBufferFree (pui1);
2450 if (status == USE_OK)
2451 break;
2452 }
2453 }
2454 else if (dt != DT_FLOPPY
2455 && GetFileAttributes (pdrive) != INVALID_FILE_ATTRIBUTES)
2456 break;
2457 pdrive = strchr (pdrive, '\0') + 1;
2458 }
2459 *de->d_name = cyg_tolower (*pdrive);
2460 de->d_name[1] = '\0';
2461 user_shared->warned_msdos = true;
2462 de->d_ino = readdir_get_ino (pdrive, false);
2463 dir->__d_position++;
2464 pdrive = strchr (pdrive, '\0') + 1;
2465 syscall_printf ("%p = readdir (%p) (%s)", &de, dir, de->d_name);
2466 return 0;
2467 }
2468
2469 void
2470 fhandler_cygdrive::rewinddir (DIR *dir)
2471 {
2472 pdrive = pdrive_buf;
2473 dir->__d_position = 0;
2474 }
2475
2476 int
2477 fhandler_cygdrive::closedir (DIR *dir)
2478 {
2479 pdrive = pdrive_buf;
2480 return 0;
2481 }
This page took 0.13911 seconds and 4 git commands to generate.