Cygwin: pinfo: use stpcpy where appropriate
[newlib-cygwin.git] / winsup / cygwin / fhandler / disk_file.cc
blobd08fe9160d350a5412ef5a2da8e37a1cc77bebf7
1 /* fhandler_disk_file.cc
3 This file is part of Cygwin.
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. */
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 #include <aio.h>
28 #include <fcntl.h>
29 #include <cygwin/fs.h>
31 #define _LIBC
32 #include <dirent.h>
34 enum __DIR_mount_type {
35 __DIR_mount_none = 0,
36 __DIR_mount_target,
37 __DIR_mount_virt_target
40 class __DIR_mounts
42 int count;
43 const char *parent_dir;
44 size_t parent_dir_len;
45 UNICODE_STRING mounts[MAX_MOUNTS];
46 bool found[MAX_MOUNTS + 3];
47 UNICODE_STRING cygdrive;
49 #define __DIR_PROC (MAX_MOUNTS)
50 #define __DIR_CYGDRIVE (MAX_MOUNTS+1)
51 #define __DIR_DEV (MAX_MOUNTS+2)
53 public:
54 __DIR_mounts (const char *posix_path)
55 : parent_dir (posix_path)
57 parent_dir_len = strlen (parent_dir);
58 count = mount_table->get_mounts_here (parent_dir, parent_dir_len, mounts,
59 &cygdrive);
60 rewind ();
62 ~__DIR_mounts ()
64 mount_table->free_mounts_here (mounts, count, &cygdrive);
66 /* For an entry within this dir, check if a mount point exists. */
67 bool check_mount (PUNICODE_STRING fname)
69 if (parent_dir_len == 1) /* root dir */
71 if (RtlEqualUnicodeString (fname, &ro_u_proc, FALSE))
73 found[__DIR_PROC] = true;
74 return true;
76 if (RtlEqualUnicodeString (fname, &ro_u_dev, FALSE))
78 found[__DIR_DEV] = true;
79 return true;
81 if (fname->Length / sizeof (WCHAR) == mount_table->cygdrive_len - 2
82 && RtlEqualUnicodeString (fname, &cygdrive, FALSE))
84 found[__DIR_CYGDRIVE] = true;
85 return true;
88 for (int i = 0; i < count; ++i)
89 if (RtlEqualUnicodeString (fname, &mounts[i], FALSE))
91 found[i] = true;
92 return true;
94 return false;
96 /* On each call, add another mount point within this directory, which is
97 not backed by a real subdir. */
98 __DIR_mount_type check_missing_mount (PUNICODE_STRING retname = NULL)
100 for (int i = 0; i < count; ++i)
101 if (!found[i])
103 found[i] = true;
104 if (retname)
105 *retname = mounts[i];
106 return __DIR_mount_target;
108 if (parent_dir_len == 1) /* root dir */
110 if (!found[__DIR_PROC])
112 found[__DIR_PROC] = true;
113 if (retname)
114 *retname = ro_u_proc;
115 return __DIR_mount_virt_target;
117 if (!found[__DIR_DEV])
119 found[__DIR_DEV] = true;
120 if (retname)
121 *retname = ro_u_dev;
122 return __DIR_mount_virt_target;
124 if (!found[__DIR_CYGDRIVE])
126 found[__DIR_CYGDRIVE] = true;
127 if (cygdrive.Length > 0)
129 if (retname)
130 *retname = cygdrive;
131 return __DIR_mount_virt_target;
135 return __DIR_mount_none;
137 void rewind () { memset (found, 0, sizeof found); }
140 inline bool
141 path_conv::isgood_inode (ino_t ino) const
143 /* If the FS doesn't support nonambiguous inode numbers anyway, bail out
144 immediately. */
145 if (!hasgood_inode ())
146 return false;
147 /* If the inode numbers are 64 bit numbers or if it's a local FS, they
148 are to be trusted. */
149 if (ino > UINT32_MAX || !isremote ())
150 return true;
151 /* The inode numbers returned from a remote NT4 NTFS are ephemeral
152 32 bit numbers. */
153 if (fs_is_ntfs ())
154 return false;
155 /* Starting with version 3.5.4, Samba returns the real inode numbers, if
156 the file is on the same device as the root of the share (Samba function
157 get_FileIndex). 32 bit inode numbers returned by older versions (likely
158 < 3.0) are ephemeral. */
159 if (fs_is_samba () && fs.samba_version () < 0x03050400)
160 return false;
161 /* Otherwise, trust the inode numbers unless proved otherwise. */
162 return true;
165 /* Check reparse point to determine if it should be treated as a
166 posix symlink or as a normal file/directory. Logic is explained
167 in detail in check_reparse_point_target in path.cc. */
168 static inline bool
169 readdir_check_reparse_point (POBJECT_ATTRIBUTES attr, bool remote)
171 NTSTATUS status;
172 HANDLE reph;
173 IO_STATUS_BLOCK io;
174 tmp_pathbuf tp;
175 UNICODE_STRING symbuf;
176 bool ret = false;
178 status = NtOpenFile (&reph, READ_CONTROL, attr, &io, FILE_SHARE_VALID_FLAGS,
179 FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT);
180 if (NT_SUCCESS (status))
182 PREPARSE_DATA_BUFFER rp = (PREPARSE_DATA_BUFFER) tp.c_get ();
183 ret = (check_reparse_point_target (reph, remote, rp, &symbuf) > 0);
184 NtClose (reph);
186 return ret;
189 inline ino_t
190 path_conv::get_ino_by_handle (HANDLE hdl)
192 IO_STATUS_BLOCK io;
193 FILE_INTERNAL_INFORMATION fii;
195 if (NT_SUCCESS (NtQueryInformationFile (hdl, &io, &fii, sizeof fii,
196 FileInternalInformation))
197 && isgood_inode (fii.IndexNumber.QuadPart))
198 return fii.IndexNumber.QuadPart;
199 return 0;
202 /* For files on NFS shares, we request an EA of type NfsV3Attributes.
203 This returns the content of a struct fattr3 as defined in RFC 1813.
204 The content is the NFS equivalent of struct stat. so there's not much
205 to do here except for copying. */
207 fhandler_base::fstat_by_nfs_ea (struct stat *buf)
209 fattr3 *nfs_attr = pc.nfsattr ();
210 PWCHAR domain;
211 cyg_ldap cldap;
212 bool ldap_open = false;
214 /* NFS stumbles over its own caching. If you write to the file,
215 a subsequent fstat does not return the actual size of the file,
216 but the size at the time the handle has been opened. Unless
217 access through another handle invalidates the caching within the
218 NFS client. Skip this for Cygwin-created Symlinks playing FIFOs
219 (this sets the filler1 member to NF3FIFO). */
220 if (get_handle () && nfs_attr->filler1 != NF3FIFO)
222 if (get_access () & GENERIC_WRITE)
223 FlushFileBuffers (get_handle ());
224 pc.get_finfo (get_handle ());
226 buf->st_dev = nfs_attr->fsid;
227 buf->st_ino = nfs_attr->fileid;
228 buf->st_mode = (nfs_attr->mode & 0xfff)
229 | nfs_type_mapping[nfs_attr->type & 7];
230 buf->st_nlink = nfs_attr->nlink;
231 if (cygheap->pg.nss_pwd_db ())
233 /* Try to map UNIX uid/gid to Cygwin uid/gid. If there's no mapping in
234 the cache, try to fetch it from the configured RFC 2307 domain (see
235 last comment in cygheap_domain_info::init() for more information) and
236 add it to the mapping cache. */
237 buf->st_uid = cygheap->ugid_cache.get_uid (nfs_attr->uid);
238 if (buf->st_uid == ILLEGAL_UID)
240 uid_t map_uid = ILLEGAL_UID;
242 domain = cygheap->dom.get_rfc2307_domain ();
243 if ((ldap_open = (cldap.open (domain) == NO_ERROR)))
244 map_uid = cldap.remap_uid (nfs_attr->uid);
245 if (map_uid == ILLEGAL_UID)
246 map_uid = MAP_UNIX_TO_CYGWIN_ID (nfs_attr->uid);
247 cygheap->ugid_cache.add_uid (nfs_attr->uid, map_uid);
248 buf->st_uid = map_uid;
251 else /* fake files being owned by current user. */
252 buf->st_uid = myself->uid;
253 if (cygheap->pg.nss_grp_db ())
255 /* See above */
256 buf->st_gid = cygheap->ugid_cache.get_gid (nfs_attr->gid);
257 if (buf->st_gid == ILLEGAL_GID)
259 gid_t map_gid = ILLEGAL_GID;
261 domain = cygheap->dom.get_rfc2307_domain ();
262 if ((ldap_open || cldap.open (domain) == NO_ERROR))
263 map_gid = cldap.remap_gid (nfs_attr->gid);
264 if (map_gid == ILLEGAL_GID)
265 map_gid = MAP_UNIX_TO_CYGWIN_ID (nfs_attr->gid);
266 cygheap->ugid_cache.add_gid (nfs_attr->gid, map_gid);
267 buf->st_gid = map_gid;
270 else /* fake files being owned by current group. */
271 buf->st_gid = myself->gid;
272 buf->st_rdev = makedev (nfs_attr->rdev.specdata1,
273 nfs_attr->rdev.specdata2);
274 buf->st_size = nfs_attr->size;
275 buf->st_blksize = PREFERRED_IO_BLKSIZE;
276 buf->st_blocks = (nfs_attr->used + S_BLKSIZE - 1) / S_BLKSIZE;
277 buf->st_atim.tv_sec = nfs_attr->atime.tv_sec;
278 buf->st_atim.tv_nsec = nfs_attr->atime.tv_nsec;
279 buf->st_mtim.tv_sec = nfs_attr->mtime.tv_sec;
280 buf->st_mtim.tv_nsec = nfs_attr->mtime.tv_nsec;
281 buf->st_ctim.tv_sec = nfs_attr->ctime.tv_sec;
282 buf->st_ctim.tv_nsec = nfs_attr->ctime.tv_nsec;
283 return 0;
287 fhandler_base::fstat_by_handle (struct stat *buf)
289 HANDLE h = get_stat_handle ();
290 NTSTATUS status = 0;
292 /* If the file has been opened for other purposes than stat, we can't rely
293 on the information stored in pc.fai. So we overwrite them here. */
294 if (get_handle ())
296 status = pc.get_finfo (h);
297 if (!NT_SUCCESS (status))
299 debug_printf ("%y = NtQueryInformationFile(%S, FileAllInformation)",
300 status, pc.get_nt_native_path ());
301 return -1;
304 if (pc.isgood_inode (pc.fai ()->InternalInformation.IndexNumber.QuadPart))
305 ino = pc.fai ()->InternalInformation.IndexNumber.QuadPart;
306 return fstat_helper (buf);
310 fhandler_base::fstat_by_name (struct stat *buf)
312 NTSTATUS status;
313 OBJECT_ATTRIBUTES attr;
314 IO_STATUS_BLOCK io;
315 UNICODE_STRING dirname;
316 UNICODE_STRING basename;
317 HANDLE dir;
318 struct {
319 FILE_ID_BOTH_DIR_INFORMATION fdi;
320 WCHAR buf[NAME_MAX + 1];
321 } fdi_buf;
323 if (!ino && pc.hasgood_inode () && !pc.has_buggy_fileid_dirinfo ())
325 RtlSplitUnicodePath (pc.get_nt_native_path (), &dirname, &basename);
326 InitializeObjectAttributes (&attr, &dirname, pc.objcaseinsensitive (),
327 NULL, NULL);
328 status = NtOpenFile (&dir, SYNCHRONIZE | FILE_LIST_DIRECTORY,
329 &attr, &io, FILE_SHARE_VALID_FLAGS,
330 FILE_SYNCHRONOUS_IO_NONALERT
331 | FILE_OPEN_FOR_BACKUP_INTENT
332 | FILE_DIRECTORY_FILE);
333 if (!NT_SUCCESS (status))
334 debug_printf ("%y = NtOpenFile(%S)", status,
335 pc.get_nt_native_path ());
336 else
338 status = NtQueryDirectoryFile (dir, NULL, NULL, NULL, &io,
339 &fdi_buf.fdi, sizeof fdi_buf,
340 FileIdBothDirectoryInformation,
341 TRUE, &basename, TRUE);
342 NtClose (dir);
343 if (!NT_SUCCESS (status))
344 debug_printf ("%y = NtQueryDirectoryFile(%S)", status,
345 pc.get_nt_native_path ());
346 else
347 ino = fdi_buf.fdi.FileId.QuadPart;
350 return fstat_helper (buf);
354 fhandler_base::fstat_fs (struct stat *buf)
356 int res = -1;
357 int oret;
358 int open_flags = O_RDONLY | O_BINARY;
360 if (get_stat_handle ())
362 if (!nohandle ())
364 if (pc.fs_is_nfs ())
365 res = fstat_by_nfs_ea (buf);
366 else if (!is_fs_special () || get_flags () & O_PATH)
367 res = fstat_by_handle (buf);
369 if (res)
370 res = fstat_by_name (buf);
371 return res;
373 /* First try to open with generic read access. This allows to read the file
374 in fstat_helper (when checking for executability) without having to
375 re-open it. Opening a file can take a lot of time on network drives
376 so we try to avoid that. */
377 oret = open_fs (open_flags, 0);
378 if (!oret)
380 query_open (query_read_attributes);
381 oret = open_fs (open_flags, 0);
383 if (oret)
385 /* We now have a valid handle, regardless of the "nohandle" state.
386 Since fhandler_base::close only calls CloseHandle if !nohandle,
387 we have to set it to false before calling close and restore
388 the state afterwards. */
389 res = pc.fs_is_nfs () ? fstat_by_nfs_ea (buf) : fstat_by_handle (buf);
390 bool no_handle = nohandle ();
391 nohandle (false);
392 close_fs ();
393 nohandle (no_handle);
394 set_handle (NULL);
396 if (res)
397 res = fstat_by_name (buf);
399 return res;
403 fhandler_base::fstat_helper (struct stat *buf)
405 IO_STATUS_BLOCK st;
406 FILE_COMPRESSION_INFORMATION fci;
407 HANDLE h = get_stat_handle ();
408 PFILE_ALL_INFORMATION pfai = pc.fai ();
409 ULONG attributes = pc.file_attributes ();
411 to_timestruc_t (&pfai->BasicInformation.LastAccessTime, &buf->st_atim);
412 to_timestruc_t (&pfai->BasicInformation.LastWriteTime, &buf->st_mtim);
413 /* If the ChangeTime is 0, the underlying FS doesn't support this timestamp
414 (FAT for instance). If so, it's faked using LastWriteTime. */
415 to_timestruc_t (pfai->BasicInformation.ChangeTime.QuadPart
416 ? &pfai->BasicInformation.ChangeTime
417 : &pfai->BasicInformation.LastWriteTime,
418 &buf->st_ctim);
419 to_timestruc_t (&pfai->BasicInformation.CreationTime, &buf->st_birthtim);
420 buf->st_dev = get_dev ();
421 /* CV 2011-01-13: Observations on the Cygwin mailing list point to an
422 interesting behaviour in some Windows versions. Apparently the size of
423 a directory is computed at the time the directory is first scanned. This
424 can result in two subsequent NtQueryInformationFile calls to return size
425 0 in the first call and size > 0 in the second call. This in turn can
426 affect applications like newer tar.
427 FIXME: Is the allocation size affected as well? */
428 buf->st_size = pc.isdir ()
430 : (off_t) pfai->StandardInformation.EndOfFile.QuadPart;
431 /* The number of links to a directory includes the number of subdirectories
432 in the directory, since all those subdirectories point to it. However,
433 this is painfully slow, so we do without it. */
434 buf->st_nlink = pc.fai()->StandardInformation.NumberOfLinks;
436 /* Enforce namehash as inode number on untrusted file systems. */
437 buf->st_ino = ino ?: get_ino ();
439 buf->st_blksize = PREFERRED_IO_BLKSIZE;
441 if (buf->st_size == 0
442 && pfai->StandardInformation.AllocationSize.QuadPart == 0LL)
443 /* File is empty and no blocks are preallocated. */
444 buf->st_blocks = 0;
445 else if (pfai->StandardInformation.AllocationSize.QuadPart > 0LL)
446 /* A successful NtQueryInformationFile returns the allocation size
447 correctly for compressed and sparse files as well.
448 Allocation size 0 is ignored here because (at least) Windows 10
449 1607 always returns 0 for CompactOS compressed files. */
450 buf->st_blocks = (pfai->StandardInformation.AllocationSize.QuadPart
451 + S_BLKSIZE - 1) / S_BLKSIZE;
452 else if ((pfai->StandardInformation.AllocationSize.QuadPart == 0LL
453 || ::has_attribute (attributes, FILE_ATTRIBUTE_COMPRESSED
454 | FILE_ATTRIBUTE_SPARSE_FILE))
455 && h && !is_fs_special ()
456 && !NtQueryInformationFile (h, &st, (PVOID) &fci, sizeof fci,
457 FileCompressionInformation))
458 /* Otherwise we request the actual amount of bytes allocated for
459 compressed, sparsed and CompactOS files. */
460 buf->st_blocks = (fci.CompressedFileSize.QuadPart + S_BLKSIZE - 1)
461 / S_BLKSIZE;
462 else
463 /* Otherwise compute no. of blocks from file size. */
464 buf->st_blocks = (buf->st_size + S_BLKSIZE - 1) / S_BLKSIZE;
466 buf->st_mode = 0;
467 /* Using a side effect: get_file_attributes checks for directory.
468 This is used, to set S_ISVTX, if needed. */
469 if (pc.isdir ())
470 buf->st_mode = S_IFDIR;
471 else if (pc.issymlink ())
473 buf->st_size = pc.get_symlink_length ();
474 get_file_attribute (h, pc, buf->st_mode,
475 &buf->st_uid, &buf->st_gid);
476 /* symlinks are everything for everyone! */
477 buf->st_mode = S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO;
478 goto done;
480 else if (pc.issocket ())
481 buf->st_mode = S_IFSOCK;
483 if (!get_file_attribute (h, pc, buf->st_mode, &buf->st_uid, &buf->st_gid))
485 /* If read-only attribute is set, modify ntsec return value */
486 if (::has_attribute (attributes, FILE_ATTRIBUTE_READONLY)
487 && !pc.isdir () && !pc.issymlink () && !pc.is_fs_special ())
488 buf->st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
490 if (buf->st_mode & S_IFMT)
491 /* nothing */;
492 else if (!is_fs_special ())
493 buf->st_mode |= S_IFREG;
494 else
496 buf->st_dev = buf->st_rdev = dev ();
497 buf->st_mode = dev ().mode ();
498 buf->st_size = 0;
501 else
503 buf->st_mode |= STD_RBITS;
505 if (!::has_attribute (attributes, FILE_ATTRIBUTE_READONLY))
506 buf->st_mode |= STD_WBITS;
507 /* | S_IWGRP | S_IWOTH; we don't give write to group etc */
509 if (pc.isdir ())
510 buf->st_mode |= S_IFDIR | STD_WBITS | STD_XBITS;
511 else if (buf->st_mode & S_IFMT)
512 /* nothing */;
513 else if (is_fs_special ())
515 buf->st_dev = buf->st_rdev = dev ();
516 buf->st_mode = dev ().mode ();
517 buf->st_size = 0;
519 else
521 buf->st_mode |= S_IFREG;
522 /* Check suffix for executable file. */
523 if (pc.exec_state () != is_executable)
525 PUNICODE_STRING path = pc.get_nt_native_path ();
527 if (RtlEqualUnicodePathSuffix (path, &ro_u_exe, TRUE)
528 || RtlEqualUnicodePathSuffix (path, &ro_u_lnk, TRUE))
529 pc.set_exec ();
531 /* No known suffix, check file header. This catches binaries and
532 shebang scripts. */
533 if (pc.exec_state () == dont_know_if_executable)
535 OBJECT_ATTRIBUTES attr;
536 NTSTATUS status = 0;
537 IO_STATUS_BLOCK io;
539 /* We have to re-open the file. Either the file is not opened
540 for reading, or the read will change the file position of the
541 original handle. */
542 status = NtOpenFile (&h, SYNCHRONIZE | FILE_READ_DATA,
543 pc.init_reopen_attr (attr, h), &io,
544 FILE_SHARE_VALID_FLAGS,
545 FILE_OPEN_FOR_BACKUP_INTENT
546 | FILE_SYNCHRONOUS_IO_NONALERT);
547 if (!NT_SUCCESS (status))
548 debug_printf ("%y = NtOpenFile(%S)", status,
549 pc.get_nt_native_path ());
550 else
552 LARGE_INTEGER off = { QuadPart:0LL };
553 char magic[3];
555 status = NtReadFile (h, NULL, NULL, NULL,
556 &io, magic, 3, &off, NULL);
557 if (!NT_SUCCESS (status))
558 debug_printf ("%y = NtReadFile(%S)", status,
559 pc.get_nt_native_path ());
560 else if (has_exec_chars (magic, io.Information))
562 /* Heureka, it's an executable */
563 pc.set_exec ();
564 buf->st_mode |= STD_XBITS;
566 NtClose (h);
570 if (pc.exec_state () == is_executable)
571 buf->st_mode |= STD_XBITS;
573 /* This fakes the permissions of all files to match the current umask. */
574 buf->st_mode &= ~(cygheap->umask);
575 /* If the FS supports ACLs, we're here because we couldn't even open
576 the file for READ_CONTROL access. Chances are high that the file's
577 security descriptor has no ACE for "Everyone", so we should not fake
578 any access for "others". */
579 if (has_acls ())
580 buf->st_mode &= ~(S_IROTH | S_IWOTH | S_IXOTH);
583 done:
584 syscall_printf ("0 = fstat (%S, %p) st_size=%D, st_mode=0%o, st_ino=%D"
585 "st_atim=%lx.%lx st_ctim=%lx.%lx "
586 "st_mtim=%lx.%lx st_birthtim=%lx.%lx",
587 pc.get_nt_native_path (), buf,
588 buf->st_size, buf->st_mode, buf->st_ino,
589 buf->st_atim.tv_sec, buf->st_atim.tv_nsec,
590 buf->st_ctim.tv_sec, buf->st_ctim.tv_nsec,
591 buf->st_mtim.tv_sec, buf->st_mtim.tv_nsec,
592 buf->st_birthtim.tv_sec, buf->st_birthtim.tv_nsec);
593 return 0;
597 fhandler_disk_file::fstat (struct stat *buf)
599 return fstat_fs (buf);
603 fhandler_disk_file::fstatvfs (struct statvfs *sfs)
605 int ret = -1, opened = 0;
606 IO_STATUS_BLOCK io;
607 /* We must not use the stat handle here, even if it exists. The handle
608 has been opened with FILE_OPEN_REPARSE_POINT, thus, in case of a volume
609 mount point, it points to the FS of the mount point, rather than to the
610 mounted FS. */
611 HANDLE fh = get_handle ();
613 if (!fh)
615 OBJECT_ATTRIBUTES attr;
616 opened = NT_SUCCESS (NtOpenFile (&fh, READ_CONTROL,
617 pc.get_object_attr (attr, sec_none_nih),
618 &io, FILE_SHARE_VALID_FLAGS,
619 FILE_OPEN_FOR_BACKUP_INTENT));
620 if (!opened)
622 /* Can't open file. Try again with parent dir. */
623 UNICODE_STRING dirname;
624 RtlSplitUnicodePath (pc.get_nt_native_path (), &dirname, NULL);
625 attr.ObjectName = &dirname;
626 opened = NT_SUCCESS (NtOpenFile (&fh, READ_CONTROL, &attr, &io,
627 FILE_SHARE_VALID_FLAGS,
628 FILE_OPEN_FOR_BACKUP_INTENT));
629 if (!opened)
630 goto out;
634 ret = fstatvfs_by_handle (fh, sfs);
635 out:
636 if (opened)
637 NtClose (fh);
638 syscall_printf ("%d = fstatvfs(%s, %p)", ret, get_name (), sfs);
639 return ret;
643 fhandler_base::fstatvfs_by_handle (HANDLE fh, struct statvfs *sfs)
645 int ret = -1;
646 NTSTATUS status;
647 IO_STATUS_BLOCK io;
648 FILE_FS_FULL_SIZE_INFORMATION full_fsi;
650 sfs->f_files = ULONG_MAX;
651 sfs->f_ffree = ULONG_MAX;
652 sfs->f_favail = ULONG_MAX;
653 sfs->f_fsid = pc.fs_serial_number ();
654 sfs->f_flag = pc.fs_flags ();
655 sfs->f_namemax = pc.fs_name_len ();
656 /* Get allocation related information. */
657 status = NtQueryVolumeInformationFile (fh, &io, &full_fsi, sizeof full_fsi,
658 FileFsFullSizeInformation);
659 if (NT_SUCCESS (status))
661 sfs->f_bsize = full_fsi.BytesPerSector * full_fsi.SectorsPerAllocationUnit;
662 sfs->f_frsize = sfs->f_bsize;
663 sfs->f_blocks = (fsblkcnt_t) full_fsi.TotalAllocationUnits.QuadPart;
664 sfs->f_bfree = (fsblkcnt_t)
665 full_fsi.ActualAvailableAllocationUnits.QuadPart;
666 sfs->f_bavail = (fsblkcnt_t)
667 full_fsi.CallerAvailableAllocationUnits.QuadPart;
668 if (sfs->f_bfree > sfs->f_bavail)
670 /* Quotas active. We can't trust TotalAllocationUnits. */
671 NTFS_VOLUME_DATA_BUFFER nvdb;
673 status = NtFsControlFile (fh, NULL, NULL, NULL, &io,
674 FSCTL_GET_NTFS_VOLUME_DATA,
675 NULL, 0, &nvdb, sizeof nvdb);
676 if (!NT_SUCCESS (status))
677 debug_printf ("%y = NtFsControlFile(%S, FSCTL_GET_NTFS_VOLUME_DATA)",
678 status, pc.get_nt_native_path ());
679 else
680 sfs->f_blocks = (fsblkcnt_t) nvdb.TotalClusters.QuadPart;
682 ret = 0;
684 else if (status == STATUS_INVALID_PARAMETER /* Netapp */
685 || status == STATUS_INVALID_INFO_CLASS)
687 FILE_FS_SIZE_INFORMATION fsi;
688 status = NtQueryVolumeInformationFile (fh, &io, &fsi, sizeof fsi,
689 FileFsSizeInformation);
690 if (NT_SUCCESS (status))
692 sfs->f_bsize = fsi.BytesPerSector * fsi.SectorsPerAllocationUnit;
693 sfs->f_frsize = sfs->f_bsize;
694 sfs->f_blocks = (fsblkcnt_t) fsi.TotalAllocationUnits.QuadPart;
695 sfs->f_bfree = sfs->f_bavail =
696 (fsblkcnt_t) fsi.AvailableAllocationUnits.QuadPart;
697 ret = 0;
699 else
700 debug_printf ("%y = NtQueryVolumeInformationFile"
701 "(%S, FileFsSizeInformation)",
702 status, pc.get_nt_native_path ());
704 else
705 debug_printf ("%y = NtQueryVolumeInformationFile"
706 "(%S, FileFsFullSizeInformation)",
707 status, pc.get_nt_native_path ());
708 return ret;
712 fhandler_disk_file::fchmod (mode_t mode)
714 int ret = -1;
715 int oret = 0;
716 NTSTATUS status;
717 IO_STATUS_BLOCK io;
719 if (pc.is_fs_special ()
720 /* For NFS, only handle Cygwin FIFOs specially. Changing mode of
721 native FIFOs will work with the default code below. */
722 && (!pc.fs_is_nfs () || pc.nfsattr ()->filler1 == NF3FIFO))
723 return chmod_device (pc, mode);
725 if (!get_handle ())
727 query_open (query_write_dac);
728 if (!(oret = open (O_BINARY, 0)))
730 /* Need WRITE_DAC to write ACLs. */
731 if (pc.has_acls ())
732 return -1;
733 /* Otherwise FILE_WRITE_ATTRIBUTES is sufficient. */
734 query_open (query_write_attributes);
735 if (!(oret = open (O_BINARY, 0)))
736 return -1;
740 if (pc.fs_is_nfs ())
742 /* chmod on NFS shares works by writing an EA of type NfsV3Attributes.
743 Only type and mode have to be set. Apparently type isn't checked
744 for consistency, so it's sufficent to set it to NF3REG all the time. */
745 struct {
746 FILE_FULL_EA_INFORMATION ffei;
747 char buf[sizeof (NFS_V3_ATTR) + sizeof (fattr3)];
748 } ffei_buf;
749 ffei_buf.ffei.NextEntryOffset = 0;
750 ffei_buf.ffei.Flags = 0;
751 ffei_buf.ffei.EaNameLength = sizeof (NFS_V3_ATTR) - 1;
752 ffei_buf.ffei.EaValueLength = sizeof (fattr3);
753 strcpy (ffei_buf.ffei.EaName, NFS_V3_ATTR);
754 fattr3 *nfs_attr = (fattr3 *) (ffei_buf.ffei.EaName
755 + ffei_buf.ffei.EaNameLength + 1);
756 memset (nfs_attr, 0, sizeof (fattr3));
757 nfs_attr->type = NF3REG;
758 nfs_attr->mode = mode;
759 status = NtSetEaFile (get_handle (), &io,
760 &ffei_buf.ffei, sizeof ffei_buf);
761 if (!NT_SUCCESS (status))
762 __seterrno_from_nt_status (status);
763 else
764 ret = 0;
765 goto out;
768 if (pc.has_acls ())
770 security_descriptor sd, sd_ret;
771 uid_t uid;
772 gid_t gid;
773 tmp_pathbuf tp;
774 aclent_t *aclp;
775 bool standard_acl = false;
776 int nentries, idx;
777 mode_t attr = pc.isdir () ? S_IFDIR : 0;
779 if (!get_file_sd (get_handle (), pc, sd, false))
781 aclp = (aclent_t *) tp.c_get ();
782 if ((nentries = get_posix_access (sd, attr, &uid, &gid,
783 aclp, MAX_ACL_ENTRIES,
784 &standard_acl)) >= 0)
786 /* Overwrite ACL permissions as required by POSIX 1003.1e
787 draft 17. */
788 aclp[0].a_perm = (mode >> 6) & S_IRWXO;
790 /* POSIXly correct: If CLASS_OBJ is present, chmod only modifies
791 CLASS_OBJ, not GROUP_OBJ.
793 Deliberate deviation from POSIX 1003.1e: If the ACL is a
794 "standard" ACL, that is, it only contains POSIX permissions
795 as well as entries for the Administrators group and SYSTEM,
796 then it's kind of a POSIX-only ACL in a twisted, Windowsy
797 way. If so, we change GROUP_OBJ and CLASS_OBJ perms. */
798 if (standard_acl
799 && (idx = searchace (aclp, nentries, GROUP_OBJ)) >= 0)
800 aclp[idx].a_perm = (mode >> 3) & S_IRWXO;
801 if (nentries > MIN_ACL_ENTRIES
802 && (idx = searchace (aclp, nentries, CLASS_OBJ)) >= 0)
803 aclp[idx].a_perm = (mode >> 3) & S_IRWXO;
805 if ((idx = searchace (aclp, nentries, OTHER_OBJ)) >= 0)
806 aclp[idx].a_perm = mode & S_IRWXO;
807 if (pc.isdir ())
808 mode |= S_IFDIR;
809 if (set_posix_access (mode, uid, gid, aclp, nentries, sd_ret,
810 pc.fs_is_samba ()))
811 ret = set_file_sd (get_handle (), pc, sd_ret, false);
816 /* If the mode has any write bits set, the DOS R/O flag is in the way. */
817 if (mode & (S_IWUSR | S_IWGRP | S_IWOTH))
818 pc &= (DWORD) ~FILE_ATTRIBUTE_READONLY;
819 else if (!pc.has_acls ()) /* Never set DOS R/O if security is used. */
820 pc |= (DWORD) FILE_ATTRIBUTE_READONLY;
821 if (S_ISSOCK (mode))
822 pc |= (DWORD) FILE_ATTRIBUTE_SYSTEM;
824 status = NtSetAttributesFile (get_handle (), pc.file_attributes ());
825 /* MVFS needs a good amount of kicking to be convinced that it has to write
826 back metadata changes and to invalidate the cached metadata information
827 stored for the given handle. This method to open a second handle to
828 the file and write the same metadata information twice has been found
829 experimentally: http://cygwin.com/ml/cygwin/2009-07/msg00533.html */
830 if (pc.fs_is_mvfs () && NT_SUCCESS (status) && !oret)
832 OBJECT_ATTRIBUTES attr;
833 HANDLE fh;
835 if (NT_SUCCESS (NtOpenFile (&fh, FILE_WRITE_ATTRIBUTES,
836 pc.init_reopen_attr (attr, get_handle ()),
837 &io, FILE_SHARE_VALID_FLAGS,
838 FILE_OPEN_FOR_BACKUP_INTENT)))
840 NtSetAttributesFile (fh, pc.file_attributes ());
841 NtClose (fh);
844 /* Correct NTFS security attributes have higher priority */
845 if (!pc.has_acls ())
847 if (!NT_SUCCESS (status))
848 __seterrno_from_nt_status (status);
849 else
850 ret = 0;
853 out:
854 if (oret)
855 close_fs ();
857 return ret;
861 fhandler_disk_file::fchown (uid_t uid, gid_t gid)
863 int oret = 0;
864 int ret = -1;
865 security_descriptor sd, sd_ret;
866 mode_t attr = pc.isdir () ? S_IFDIR : 0;
867 uid_t old_uid;
868 gid_t old_gid;
869 tmp_pathbuf tp;
870 aclent_t *aclp;
871 int nentries;
873 if (!pc.has_acls ())
875 /* fake - if not supported, pretend we're like win95
876 where it just works */
877 /* FIXME: Could be supported on NFS when user->uid mapping is in place. */
878 return 0;
881 if (!get_handle ())
883 query_open (query_write_control);
884 if (!(oret = fhandler_disk_file::open (O_BINARY, 0)))
885 return -1;
888 if (get_file_sd (get_handle (), pc, sd, false))
889 goto out;
891 aclp = (aclent_t *) tp.c_get ();
892 if ((nentries = get_posix_access (sd, attr, &old_uid, &old_gid,
893 aclp, MAX_ACL_ENTRIES)) < 0)
894 goto out;
896 /* According to POSIX, chown can be a no-op if uid is (uid_t)-1 and
897 gid is (gid_t)-1. Otherwise, even if uid and gid are unchanged,
898 we must ensure that ctime is updated. */
899 if (uid == ILLEGAL_UID && gid == ILLEGAL_GID)
901 ret = 0;
902 goto out;
904 if (uid == ILLEGAL_UID)
905 uid = old_uid;
906 else if (gid == ILLEGAL_GID)
907 gid = old_gid;
909 /* Windows ACLs can contain permissions for one group, while being owned by
910 another user/group. The permission bits returned above are pretty much
911 useless then. Creating a new ACL with these useless permissions results
912 in a potentially broken symlink. So what we do here is to set the
913 underlying permissions of symlinks to a sensible value which allows the
914 world to read the symlink and only the new owner to change it. */
915 if (pc.issymlink ())
916 for (int idx = 0; idx < nentries; ++idx)
918 aclp[idx].a_perm |= S_IROTH;
919 if (aclp[idx].a_type & USER_OBJ)
920 aclp[idx].a_perm |= S_IWOTH;
923 if (set_posix_access (attr, uid, gid, aclp, nentries, sd_ret,
924 pc.fs_is_samba ()))
925 ret = set_file_sd (get_handle (), pc, sd_ret, true);
927 /* If you're running a Samba server with no winbind, the uid<->SID mapping
928 is disfunctional. Even trying to chown to your own account fails since
929 the account used on the server is the UNIX account which gets used for
930 the standard user mapping. This is a default mechanism which doesn't
931 know your real Windows SID. There are two possible error codes in
932 different Samba releases for this situation, one of them unfortunately
933 the not very significant STATUS_ACCESS_DENIED. Instead of relying on
934 the error codes, we're using the below very simple heuristic.
935 If set_file_sd failed, and the original user account was either already
936 unknown, or one of the standard UNIX accounts, we're faking success. */
937 if (ret == -1 && pc.fs_is_samba ())
939 PSID sid;
941 if (uid == old_uid
942 || ((sid = sidfromuid (old_uid, NULL)) != NO_SID
943 && RtlEqualPrefixSid (sid,
944 well_known_samba_unix_user_fake_sid)))
946 debug_printf ("Faking chown worked on standalone Samba");
947 ret = 0;
951 out:
952 if (oret)
953 close_fs ();
955 return ret;
959 fhandler_disk_file::facl (int cmd, int nentries, aclent_t *aclbufp)
961 int res = -1;
962 int oret = 0;
964 if (!pc.has_acls ())
966 cant_access_acl:
967 switch (cmd)
970 case SETACL:
971 /* Open for writing required to be able to set ctime
972 (even though setting the ACL is just pretended). */
973 if (!get_handle ())
974 oret = open (O_WRONLY | O_BINARY, 0);
975 res = 0;
976 break;
977 case GETACL:
978 if (!aclbufp)
979 set_errno (EFAULT);
980 else if (nentries < MIN_ACL_ENTRIES)
981 set_errno (ENOSPC);
982 else
984 struct stat st;
985 if (!fstat (&st))
987 aclbufp[0].a_type = USER_OBJ;
988 aclbufp[0].a_id = st.st_uid;
989 aclbufp[0].a_perm = (st.st_mode & S_IRWXU) >> 6;
990 aclbufp[1].a_type = GROUP_OBJ;
991 aclbufp[1].a_id = st.st_gid;
992 aclbufp[1].a_perm = (st.st_mode & S_IRWXG) >> 3;
993 aclbufp[2].a_type = OTHER_OBJ;
994 aclbufp[2].a_id = ILLEGAL_GID;
995 aclbufp[2].a_perm = st.st_mode & S_IRWXO;
996 res = MIN_ACL_ENTRIES;
999 break;
1000 case GETACLCNT:
1001 res = MIN_ACL_ENTRIES;
1002 break;
1003 default:
1004 set_errno (EINVAL);
1005 break;
1008 else
1010 if ((cmd == SETACL && !get_handle ())
1011 || (cmd != SETACL && !get_stat_handle ()))
1013 query_open (cmd == SETACL ? query_write_dac : query_read_control);
1014 if (!(oret = open (O_BINARY, 0)))
1016 if (cmd == GETACL || cmd == GETACLCNT)
1017 goto cant_access_acl;
1018 return -1;
1021 switch (cmd)
1023 case SETACL:
1024 if (!aclsort (nentries, 0, aclbufp))
1026 bool rw = false;
1027 res = setacl (get_handle (), pc, nentries, aclbufp, rw);
1028 if (rw)
1030 IO_STATUS_BLOCK io;
1031 FILE_BASIC_INFORMATION fbi;
1032 fbi.CreationTime.QuadPart
1033 = fbi.LastAccessTime.QuadPart
1034 = fbi.LastWriteTime.QuadPart
1035 = fbi.ChangeTime.QuadPart = 0LL;
1036 fbi.FileAttributes = (pc.file_attributes ()
1037 & ~FILE_ATTRIBUTE_READONLY)
1038 ?: FILE_ATTRIBUTE_NORMAL;
1039 NtSetInformationFile (get_handle (), &io, &fbi, sizeof fbi,
1040 FileBasicInformation);
1043 break;
1044 case GETACL:
1045 if (!aclbufp)
1046 set_errno(EFAULT);
1047 else {
1048 res = getacl (get_stat_handle (), pc, nentries, aclbufp);
1049 /* For this ENOSYS case, see security.cc:get_file_attribute(). */
1050 if (res == -1 && get_errno () == ENOSYS)
1051 goto cant_access_acl;
1053 break;
1054 case GETACLCNT:
1055 res = getacl (get_stat_handle (), pc, 0, NULL);
1056 /* Ditto. */
1057 if (res == -1 && get_errno () == ENOSYS)
1058 goto cant_access_acl;
1059 break;
1060 default:
1061 set_errno (EINVAL);
1062 break;
1066 if (oret)
1067 close_fs ();
1069 return res;
1072 ssize_t
1073 fhandler_disk_file::fgetxattr (const char *name, void *value, size_t size)
1075 if (pc.is_fs_special ())
1077 set_errno (ENOTSUP);
1078 return -1;
1080 return read_ea (get_handle (), pc, name, (char *) value, size);
1084 fhandler_disk_file::fsetxattr (const char *name, const void *value, size_t size,
1085 int flags)
1087 if (pc.is_fs_special ())
1089 set_errno (ENOTSUP);
1090 return -1;
1092 return write_ea (get_handle (), pc, name, (const char *) value, size, flags);
1096 fhandler_disk_file::fadvise (off_t offset, off_t length, int advice)
1098 if (advice < POSIX_FADV_NORMAL || advice > POSIX_FADV_NOREUSE)
1099 return EINVAL;
1101 /* Windows only supports advice flags for the whole file. We're using
1102 a simplified test here so that we don't have to ask for the actual
1103 file size. Length == 0 means all bytes starting at offset anyway.
1104 So we only actually follow the advice, if it's given for offset == 0. */
1105 if (offset != 0)
1106 return 0;
1108 /* We only support normal and sequential mode for now. Everything which
1109 is not POSIX_FADV_SEQUENTIAL is treated like POSIX_FADV_NORMAL. */
1110 if (advice != POSIX_FADV_SEQUENTIAL)
1111 advice = POSIX_FADV_NORMAL;
1113 IO_STATUS_BLOCK io;
1114 FILE_MODE_INFORMATION fmi;
1115 NTSTATUS status = NtQueryInformationFile (get_handle (), &io,
1116 &fmi, sizeof fmi,
1117 FileModeInformation);
1118 if (NT_SUCCESS (status))
1120 fmi.Mode &= ~FILE_SEQUENTIAL_ONLY;
1121 if (advice == POSIX_FADV_SEQUENTIAL)
1122 fmi.Mode |= FILE_SEQUENTIAL_ONLY;
1123 status = NtSetInformationFile (get_handle (), &io, &fmi, sizeof fmi,
1124 FileModeInformation);
1125 if (NT_SUCCESS (status))
1126 return 0;
1127 __seterrno_from_nt_status (status);
1130 return geterrno_from_nt_status (status);
1134 fhandler_disk_file::falloc_allocate (int mode, off_t offset, off_t length)
1136 NTSTATUS status;
1137 IO_STATUS_BLOCK io;
1138 FILE_STANDARD_INFORMATION fsi;
1139 FILE_END_OF_FILE_INFORMATION feofi;
1140 FILE_ALLOCATION_INFORMATION fai = { 0 };
1142 /* Fetch EOF */
1143 status = NtQueryInformationFile (get_handle (), &io, &fsi, sizeof fsi,
1144 FileStandardInformation);
1145 if (!NT_SUCCESS (status))
1146 return geterrno_from_nt_status (status);
1148 switch (mode)
1150 case 0:
1151 /* For posix_fallocate(3), truncating the file is a no-op. However,
1152 for sparse files we still have to allocate the blocks within
1153 offset and offset + length which are currently in holes, due to
1154 the following POSIX requirement:
1155 "If posix_fallocate() returns successfully, subsequent writes to
1156 the specified file data shall not fail due to the lack of free
1157 space on the file system storage media." */
1158 if (offset + length <= fsi.EndOfFile.QuadPart)
1160 if (!has_attribute (FILE_ATTRIBUTE_SPARSE_FILE))
1161 return 0;
1162 feofi.EndOfFile.QuadPart = fsi.EndOfFile.QuadPart;
1164 else
1165 feofi.EndOfFile.QuadPart = offset + length;
1166 break;
1167 case __FALLOC_FL_TRUNCATE:
1168 /* For ftruncate(2), offset is 0. Just use length as is. */
1169 feofi.EndOfFile.QuadPart = length;
1171 /* Make file sparse only when called through ftruncate and the mount
1172 mode supports sparse files. Also, make sure that the new region
1173 actually spans over at least one sparsifiable chunk. */
1174 if (pc.support_sparse ()
1175 && !has_attribute (FILE_ATTRIBUTE_SPARSE_FILE)
1176 && span_sparse_chunk (feofi.EndOfFile.QuadPart,
1177 fsi.EndOfFile.QuadPart))
1179 status = NtFsControlFile (get_handle (), NULL, NULL, NULL, &io,
1180 FSCTL_SET_SPARSE, NULL, 0, NULL, 0);
1181 if (NT_SUCCESS (status))
1182 pc.file_attributes (pc.file_attributes ()
1183 | FILE_ATTRIBUTE_SPARSE_FILE);
1184 debug_printf ("%y = NtFsControlFile(%S, FSCTL_SET_SPARSE)",
1185 status, pc.get_nt_native_path ());
1187 break;
1188 case FALLOC_FL_KEEP_SIZE:
1189 /* Keep track of the allocation size for overallocation below.
1190 Note that overallocation in Windows is only temporary!
1191 As soon as the last open handle to the file is closed, the
1192 overallocation gets removed by the system. Also, overallocation
1193 for sparse files fails silently, so just don't bother. */
1194 if (offset + length > fsi.EndOfFile.QuadPart
1195 && !has_attribute (FILE_ATTRIBUTE_SPARSE_FILE))
1196 fai.AllocationSize.QuadPart = offset + length;
1198 feofi.EndOfFile.QuadPart = fsi.EndOfFile.QuadPart;
1199 break;
1202 /* Now set the new EOF */
1203 if (feofi.EndOfFile.QuadPart != fsi.EndOfFile.QuadPart)
1205 status = NtSetInformationFile (get_handle (), &io,
1206 &feofi, sizeof feofi,
1207 FileEndOfFileInformation);
1208 if (!NT_SUCCESS (status))
1209 return geterrno_from_nt_status (status);
1212 /* If called via fallocate(2) or posix_fallocate(3), allocate blocks in
1213 sparse file holes. */
1214 if (mode != __FALLOC_FL_TRUNCATE
1215 && length
1216 && has_attribute (FILE_ATTRIBUTE_SPARSE_FILE))
1218 int res = falloc_zero_range (mode | __FALLOC_FL_ZERO_HOLES,
1219 offset, length);
1220 if (res)
1221 return res;
1224 /* Last but not least, set the new allocation size, if any */
1225 if (fai.AllocationSize.QuadPart)
1227 /* This is not fatal. Just note a failure in the debug output. */
1228 status = NtSetInformationFile (get_handle (), &io,
1229 &fai, sizeof fai,
1230 FileAllocationInformation);
1231 if (!NT_SUCCESS (status))
1232 debug_printf ("%y = NtSetInformationFile(%S, "
1233 "FileAllocationInformation)",
1234 status, pc.get_nt_native_path ());
1237 return 0;
1241 fhandler_disk_file::falloc_punch_hole (off_t offset, off_t length)
1243 NTSTATUS status;
1244 IO_STATUS_BLOCK io;
1245 FILE_STANDARD_INFORMATION fsi;
1246 FILE_ZERO_DATA_INFORMATION fzi;
1248 /* Fetch EOF */
1249 status = NtQueryInformationFile (get_handle (), &io, &fsi, sizeof fsi,
1250 FileStandardInformation);
1251 if (!NT_SUCCESS (status))
1252 return geterrno_from_nt_status (status);
1254 if (offset > fsi.EndOfFile.QuadPart) /* no-op */
1255 return 0;
1257 if (offset + length > fsi.EndOfFile.QuadPart)
1258 length = fsi.EndOfFile.QuadPart - offset;
1260 /* If the file isn't sparse yet, make it so. */
1261 if (!has_attribute (FILE_ATTRIBUTE_SPARSE_FILE))
1263 status = NtFsControlFile (get_handle (), NULL, NULL, NULL, &io,
1264 FSCTL_SET_SPARSE, NULL, 0, NULL, 0);
1265 debug_printf ("%y = NtFsControlFile(%S, FSCTL_SET_SPARSE)",
1266 status, pc.get_nt_native_path ());
1267 if (!NT_SUCCESS (status))
1268 return geterrno_from_nt_status (status);
1269 pc.file_attributes (pc.file_attributes () | FILE_ATTRIBUTE_SPARSE_FILE);
1272 /* Now punch a hole. For once, FSCTL_SET_ZERO_DATA does it exactly as per
1273 fallocate(FALLOC_FL_PUNCH_HOLE) specs. */
1274 fzi.FileOffset.QuadPart = offset;
1275 fzi.BeyondFinalZero.QuadPart = offset + length;
1276 status = NtFsControlFile (get_handle (), NULL, NULL, NULL, &io,
1277 FSCTL_SET_ZERO_DATA, &fzi, sizeof fzi, NULL, 0);
1278 if (!NT_SUCCESS (status))
1279 return geterrno_from_nt_status (status);
1281 return 0;
1285 fhandler_disk_file::falloc_zero_range (int mode, off_t offset, off_t length)
1287 NTSTATUS status;
1288 IO_STATUS_BLOCK io;
1289 FILE_STANDARD_INFORMATION fsi;
1290 FILE_ALLOCATED_RANGE_BUFFER inp, *out = NULL;
1291 OBJECT_ATTRIBUTES attr;
1292 HANDLE zo_handle;
1293 tmp_pathbuf tp;
1294 size_t data_chunk_count = 0;
1296 /* Fetch EOF */
1297 status = NtQueryInformationFile (get_handle (), &io, &fsi, sizeof fsi,
1298 FileStandardInformation);
1299 if (!NT_SUCCESS (status))
1300 return geterrno_from_nt_status (status);
1302 /* offset and length must not exceed EOF with FALLOC_FL_KEEP_SIZE */
1303 if (mode & FALLOC_FL_KEEP_SIZE)
1305 if (offset > fsi.EndOfFile.QuadPart) /* no-op */
1306 return 0;
1308 if (offset + length > fsi.EndOfFile.QuadPart)
1309 length = fsi.EndOfFile.QuadPart - offset;
1312 /* If the file is sparse, fetch the data ranges within the file
1313 to be able to recognize holes. */
1314 if (has_attribute (FILE_ATTRIBUTE_SPARSE_FILE))
1316 inp.FileOffset.QuadPart = offset;
1317 inp.Length.QuadPart = length;
1318 out = (FILE_ALLOCATED_RANGE_BUFFER *) tp.t_get ();
1319 status = NtFsControlFile (get_handle (), NULL, NULL, NULL,
1320 &io, FSCTL_QUERY_ALLOCATED_RANGES,
1321 &inp, sizeof inp, out, 2 * NT_MAX_PATH);
1322 if (!NT_ERROR (status))
1323 data_chunk_count = io.Information / sizeof *out;
1326 /* Re-open the file and use this handle ever after, so as not to
1327 move the file pointer of the original file object. */
1328 status = NtOpenFile (&zo_handle, SYNCHRONIZE | GENERIC_WRITE,
1329 pc.init_reopen_attr (attr, get_handle ()), &io,
1330 FILE_SHARE_VALID_FLAGS, get_options ());
1331 if (!NT_SUCCESS (status))
1332 return geterrno_from_nt_status (status);
1334 /* FILE_SPARSE_GRANULARITY == 2 * NT_MAX_PATH ==> fits exactly */
1335 char *nullbuf = tp.t_get ();
1336 memset (nullbuf, 0, FILE_SPARSE_GRANULARITY);
1337 int res = 0;
1339 /* Split range into chunks of size FILE_SPARSE_GRANULARITY and handle
1340 them according to being data or hole */
1341 LARGE_INTEGER off = { QuadPart:offset };
1342 size_t start_idx = 0;
1343 while (length > 0)
1345 off_t chunk_len;
1346 bool in_data = true;
1348 if (off.QuadPart % FILE_SPARSE_GRANULARITY) /* First block */
1349 chunk_len = roundup2 (off.QuadPart, FILE_SPARSE_GRANULARITY) - off.QuadPart;
1350 else
1351 chunk_len = FILE_SPARSE_GRANULARITY;
1352 if (chunk_len > length) /* First or last block */
1353 chunk_len = length;
1355 /* Check if the current chunk is within data or hole */
1356 if (has_attribute (FILE_ATTRIBUTE_SPARSE_FILE)
1357 && off.QuadPart < fsi.EndOfFile.QuadPart)
1359 in_data = false;
1360 for (size_t idx = start_idx; idx < data_chunk_count; ++idx)
1361 if (off.QuadPart >= out[idx].FileOffset.QuadPart)
1363 /* Skip entries with lower start address next time. */
1364 start_idx = idx;
1365 if (off.QuadPart < out[idx].FileOffset.QuadPart
1366 + out[idx].Length.QuadPart)
1368 in_data = true;
1369 break;
1374 /* Eventually, write zeros into the block. Completely zero out data
1375 blocks, just write a single zero to former holes in sparse files.
1376 If __FALLOC_FL_ZERO_HOLES has been specified, only write to holes. */
1377 if (!(mode & __FALLOC_FL_ZERO_HOLES) || !in_data)
1379 status = NtWriteFile (zo_handle, NULL, NULL, NULL, &io, nullbuf,
1380 in_data ? chunk_len : 1, &off, NULL);
1381 if (!NT_SUCCESS (status))
1383 res = geterrno_from_nt_status (status);
1384 break;
1388 off.QuadPart += chunk_len;
1389 length -= chunk_len;
1392 NtClose (zo_handle);
1393 return res;
1397 fhandler_disk_file::fallocate (int mode, off_t offset, off_t length)
1399 if (length < 0 || !get_handle ())
1400 return EINVAL;
1401 if (pc.isdir ())
1402 return EISDIR;
1403 if (!(get_access () & GENERIC_WRITE))
1404 return EBADF;
1406 switch (mode)
1408 case 0:
1409 case __FALLOC_FL_TRUNCATE:
1410 case FALLOC_FL_KEEP_SIZE:
1411 return falloc_allocate (mode, offset, length);
1412 case FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE:
1413 /* Only if the filesystem supports it... */
1414 if (!(pc.fs_flags () & FILE_SUPPORTS_SPARSE_FILES))
1415 return EOPNOTSUPP;
1416 return falloc_punch_hole (offset, length);
1417 case FALLOC_FL_ZERO_RANGE:
1418 case FALLOC_FL_ZERO_RANGE | FALLOC_FL_KEEP_SIZE:
1419 return falloc_zero_range (mode, offset, length);
1420 default:
1421 break;
1423 return EINVAL;
1427 fhandler_disk_file::link (const char *newpath)
1429 size_t nlen = strlen (newpath);
1430 path_conv newpc (newpath, PC_SYM_NOFOLLOW | PC_POSIX | PC_NULLEMPTY, stat_suffixes);
1431 if (newpc.error)
1433 set_errno (newpc.error);
1434 return -1;
1437 if (newpc.exists ())
1439 syscall_printf ("file '%S' exists?", newpc.get_nt_native_path ());
1440 set_errno (EEXIST);
1441 return -1;
1444 if (isdirsep (newpath[nlen - 1]) || has_dot_last_component (newpath, false))
1446 set_errno (ENOENT);
1447 return -1;
1450 char new_buf[nlen + 5];
1451 if (!newpc.error)
1453 /* If the original file is a lnk special file,
1454 and if the original file has a .lnk suffix, add one to the hardlink
1455 as well. */
1456 if (pc.is_lnk_special ()
1457 && RtlEqualUnicodePathSuffix (pc.get_nt_native_path (),
1458 &ro_u_lnk, TRUE))
1460 /* Shortcut hack. */
1461 stpcpy (stpcpy (new_buf, newpath), ".lnk");
1462 newpath = new_buf;
1463 newpc.check (newpath, PC_SYM_NOFOLLOW);
1465 else if (!pc.isdir ()
1466 && pc.is_binary ()
1467 && RtlEqualUnicodePathSuffix (pc.get_nt_native_path (),
1468 &ro_u_exe, TRUE)
1469 && !RtlEqualUnicodePathSuffix (newpc.get_nt_native_path (),
1470 &ro_u_exe, TRUE))
1472 /* Executable hack. */
1473 stpcpy (stpcpy (new_buf, newpath), ".exe");
1474 newpath = new_buf;
1475 newpc.check (newpath, PC_SYM_NOFOLLOW);
1479 /* We only need READ_CONTROL access so the handle returned in pc is
1480 sufficient. And if the file couldn't be opened with READ_CONTROL
1481 access in path_conv, we won't be able to do it here anyway. */
1482 HANDLE fh = get_stat_handle ();
1483 if (!fh)
1485 set_errno (EACCES);
1486 return -1;
1488 PUNICODE_STRING tgt = newpc.get_nt_native_path ();
1489 ULONG size = sizeof (FILE_LINK_INFORMATION) + tgt->Length;
1490 PFILE_LINK_INFORMATION pfli = (PFILE_LINK_INFORMATION) alloca (size);
1491 pfli->ReplaceIfExists = FALSE;
1492 pfli->RootDirectory = NULL;
1493 memcpy (pfli->FileName, tgt->Buffer, pfli->FileNameLength = tgt->Length);
1495 NTSTATUS status;
1496 IO_STATUS_BLOCK io;
1497 status = NtSetInformationFile (fh, &io, pfli, size, FileLinkInformation);
1498 if (!NT_SUCCESS (status))
1500 if (status == STATUS_INVALID_DEVICE_REQUEST
1501 || status == STATUS_NOT_SUPPORTED)
1502 /* FS doesn't support hard links. Linux returns EPERM. */
1503 set_errno (EPERM);
1504 else
1505 __seterrno_from_nt_status (status);
1506 return -1;
1508 else if ((pc.file_attributes () & O_TMPFILE_FILE_ATTRS)
1509 == O_TMPFILE_FILE_ATTRS)
1511 /* An O_TMPFILE file has FILE_ATTRIBUTE_TEMPORARY and
1512 FILE_ATTRIBUTE_HIDDEN set. After a successful hardlink the file is
1513 not temporary anymore in the usual sense. So we remove these
1514 attributes here.
1516 Note that we don't create a reopen attribute for the original
1517 link but rather a normal attribute for the just created link.
1518 The reason is a curious behaviour of Windows: If we remove the
1519 O_TMPFILE attributes on the original link, the new link will not
1520 show up in file system listings (not even native ones from , e.g.,
1521 `cmd /c dir'), long after the original link has been closed and
1522 removed. The file and its metadata will be kept in memory only
1523 as long as Windows sees fit. By opening the new link, we request
1524 the attribute changes on the new link, so after closing it Windows
1525 will have an increased interest to write back the metadata. */
1526 OBJECT_ATTRIBUTES attr;
1527 status = NtOpenFile (&fh, FILE_WRITE_ATTRIBUTES,
1528 newpc.get_object_attr (attr, sec_none_nih), &io,
1529 FILE_SHARE_VALID_FLAGS, FILE_OPEN_FOR_BACKUP_INTENT);
1530 if (!NT_SUCCESS (status))
1531 debug_printf ("Opening for removing TEMPORARY attrib failed, "
1532 "status = %y", status);
1533 else
1535 FILE_BASIC_INFORMATION fbi;
1537 fbi.CreationTime.QuadPart = fbi.LastAccessTime.QuadPart
1538 = fbi.LastWriteTime.QuadPart = fbi.ChangeTime.QuadPart = 0LL;
1539 fbi.FileAttributes = (pc.file_attributes () & ~O_TMPFILE_FILE_ATTRS)
1540 ?: FILE_ATTRIBUTE_NORMAL;
1541 status = NtSetInformationFile (fh, &io, &fbi, sizeof fbi,
1542 FileBasicInformation);
1543 if (!NT_SUCCESS (status))
1544 debug_printf ("Removing the TEMPORARY attrib failed, status = %y",
1545 status);
1546 NtClose (fh);
1549 return 0;
1553 fhandler_disk_file::utimens (const struct timespec *tvp)
1555 return utimens_fs (tvp);
1559 fhandler_base::utimens_fs (const struct timespec *tvp)
1561 struct timespec timeofday;
1562 struct timespec tmp[2];
1563 bool closeit = false;
1565 if (!get_handle ())
1567 query_open (query_write_attributes);
1568 if (!open_fs (O_BINARY, 0))
1570 /* It's documented in MSDN that FILE_WRITE_ATTRIBUTES is sufficient
1571 to change the timestamps. Unfortunately it's not sufficient for a
1572 remote HPFS which requires GENERIC_WRITE, so we just retry to open
1573 for writing, though this fails for R/O files of course. */
1574 query_open (no_query);
1575 if (!open_fs (O_WRONLY | O_BINARY, 0))
1577 syscall_printf ("Opening file failed");
1578 return -1;
1581 closeit = true;
1584 clock_gettime (CLOCK_REALTIME, &timeofday);
1585 if (!tvp)
1586 tmp[1] = tmp[0] = timeofday;
1587 else
1589 if ((tvp[0].tv_nsec < UTIME_NOW || tvp[0].tv_nsec >= NSPERSEC)
1590 || (tvp[1].tv_nsec < UTIME_NOW || tvp[1].tv_nsec >= NSPERSEC))
1592 if (closeit)
1593 close_fs ();
1594 set_errno (EINVAL);
1595 return -1;
1597 tmp[0] = (tvp[0].tv_nsec == UTIME_NOW) ? timeofday : tvp[0];
1598 tmp[1] = (tvp[1].tv_nsec == UTIME_NOW) ? timeofday : tvp[1];
1600 debug_printf ("incoming lastaccess %ly %ly", tmp[0].tv_sec, tmp[0].tv_nsec);
1602 IO_STATUS_BLOCK io;
1603 FILE_BASIC_INFORMATION fbi;
1605 fbi.CreationTime.QuadPart = 0LL;
1606 /* UTIME_OMIT is handled in timespec_to_filetime by setting FILETIME to 0. */
1607 timespec_to_filetime (&tmp[0], &fbi.LastAccessTime);
1608 timespec_to_filetime (&tmp[1], &fbi.LastWriteTime);
1609 fbi.ChangeTime.QuadPart = 0LL;
1610 fbi.FileAttributes = 0;
1611 NTSTATUS status = NtSetInformationFile (get_handle (), &io, &fbi, sizeof fbi,
1612 FileBasicInformation);
1613 /* For this special case for MVFS see the comment in
1614 fhandler_disk_file::fchmod. */
1615 if (pc.fs_is_mvfs () && NT_SUCCESS (status) && !closeit)
1617 OBJECT_ATTRIBUTES attr;
1618 HANDLE fh;
1620 if (NT_SUCCESS (NtOpenFile (&fh, FILE_WRITE_ATTRIBUTES,
1621 pc.init_reopen_attr (attr, get_handle ()),
1622 &io, FILE_SHARE_VALID_FLAGS,
1623 FILE_OPEN_FOR_BACKUP_INTENT)))
1625 NtSetInformationFile (fh, &io, &fbi, sizeof fbi,
1626 FileBasicInformation);
1627 NtClose (fh);
1630 if (closeit)
1631 close_fs ();
1632 /* Opening a directory on a 9x share from a NT machine works(!), but
1633 then NtSetInformationFile fails with STATUS_NOT_SUPPORTED. Oh well... */
1634 if (!NT_SUCCESS (status) && status != STATUS_NOT_SUPPORTED)
1636 __seterrno_from_nt_status (status);
1637 return -1;
1639 return 0;
1642 fhandler_disk_file::fhandler_disk_file () :
1643 fhandler_base (), prw_handle (NULL)
1647 fhandler_disk_file::fhandler_disk_file (path_conv &pc) :
1648 fhandler_base (), prw_handle (NULL)
1650 set_name (pc);
1654 fhandler_disk_file::open (int flags, mode_t mode)
1656 return open_fs (flags, mode);
1660 fhandler_disk_file::close ()
1662 /* Close extra pread/pwrite handle, if it exists. */
1663 if (prw_handle)
1664 NtClose (prw_handle);
1665 return fhandler_base::close ();
1669 fhandler_disk_file::fcntl (int cmd, intptr_t arg)
1671 int res;
1673 switch (cmd)
1675 case F_LCK_MANDATORY: /* Mandatory locking only works on files. */
1676 mandatory_locking (!!arg);
1677 need_fork_fixup (true);
1678 res = 0;
1679 break;
1680 default:
1681 res = fhandler_base::fcntl (cmd, arg);
1682 break;
1684 return res;
1688 fhandler_disk_file::dup (fhandler_base *child, int flags)
1690 fhandler_disk_file *fhc = (fhandler_disk_file *) child;
1692 int ret = fhandler_base::dup (child, flags);
1693 if (!ret && prw_handle
1694 && !DuplicateHandle (GetCurrentProcess (), prw_handle,
1695 GetCurrentProcess (), &fhc->prw_handle,
1696 0, FALSE, DUPLICATE_SAME_ACCESS))
1697 fhc->prw_handle = NULL;
1698 return ret;
1701 void
1702 fhandler_disk_file::fixup_after_fork (HANDLE parent)
1704 prw_handle = NULL;
1705 mandatory_locking (false);
1706 fhandler_base::fixup_after_fork (parent);
1710 fhandler_base::open_fs (int flags, mode_t mode)
1712 /* Unfortunately NT allows to open directories for writing, but that's
1713 disallowed according to SUSv3. */
1714 if (pc.isdir () && (flags & O_ACCMODE) != O_RDONLY)
1716 set_errno (EISDIR);
1717 return 0;
1720 bool new_file = !exists ();
1722 int res = fhandler_base::open (flags, mode);
1723 if (res)
1725 /* The file info in pc is wrong at this point for newly created files.
1726 Refresh it before fetching any file info. */
1727 if (new_file)
1728 pc.get_finfo (get_handle ());
1730 if (pc.isgood_inode (pc.get_ino ()))
1731 ino = pc.get_ino ();
1734 syscall_printf ("%d = fhandler_disk_file::open(%S, %y)", res,
1735 pc.get_nt_native_path (), flags);
1736 return res;
1739 /* POSIX demands that pread/pwrite don't change the current file position.
1740 While NtReadFile/NtWriteFile support atomic seek-and-io, both change the
1741 file pointer if the file handle has been opened for synchonous I/O.
1742 Using this handle for pread/pwrite would break atomicity, because the
1743 read/write operation would have to be followed by a seek back to the old
1744 file position. What we do is to open another handle to the file on the
1745 first call to either pread or pwrite. This is used for any subsequent
1746 pread/pwrite. Thus the current file position of the "normal" file
1747 handle is not touched.
1749 FIXME:
1751 Note that this is just a hack. The problem with this approach is that
1752 a change to the file permissions might disallow to open the file with
1753 the required permissions to read or write. This appears to be a border case,
1754 but that's exactly what git does. It creates the file for reading and
1755 writing and after writing it, it chmods the file to read-only. Then it
1756 calls pread on the file to examine the content. This works, but if git
1757 would use the original handle to pwrite to the file, it would be broken
1758 with our approach.
1760 One way to implement this is to open the pread/pwrite handle right at
1761 file open time. We would simply maintain two handles, which wouldn't
1762 be much of a problem given how we do that for other fhandler types as
1763 well.
1765 However, ultimately fhandler_disk_file should become a derived class of
1766 fhandler_base_overlapped. Each raw_read or raw_write would fetch the
1767 actual file position, read/write from there, and then set the file
1768 position again. Fortunately, while the file position is not maintained
1769 by the I/O manager, it can be fetched and set to a new value by all
1770 processes holding a handle to that file object. Pread and pwrite differ
1771 from raw_read and raw_write just by not touching the current file pos.
1772 Actually they could be merged with raw_read/raw_write if we add a position
1773 parameter to the latter. */
1776 fhandler_disk_file::prw_open (bool write, void *aio)
1778 NTSTATUS status;
1779 IO_STATUS_BLOCK io;
1780 OBJECT_ATTRIBUTES attr;
1781 ULONG options = get_options ();
1783 /* If async i/o is intended, turn off the default synchronous operation */
1784 if (aio)
1785 options &= ~FILE_SYNCHRONOUS_IO_NONALERT;
1787 /* First try to open with the original access mask */
1788 ACCESS_MASK access = get_access ();
1789 status = NtOpenFile (&prw_handle, access,
1790 pc.init_reopen_attr (attr, get_handle ()), &io,
1791 FILE_SHARE_VALID_FLAGS, options);
1792 if (status == STATUS_ACCESS_DENIED)
1794 /* If we get an access denied, chmod has been called. Try again
1795 with just the required rights to perform the called function. */
1796 access &= write ? ~GENERIC_READ : ~GENERIC_WRITE;
1797 status = NtOpenFile (&prw_handle, access, &attr, &io,
1798 FILE_SHARE_VALID_FLAGS, options);
1800 debug_printf ("%y = NtOpenFile (%p, %y, %S, io, %y, %y)",
1801 status, prw_handle, access, pc.get_nt_native_path (),
1802 FILE_SHARE_VALID_FLAGS, options);
1803 if (!NT_SUCCESS (status))
1805 __seterrno_from_nt_status (status);
1806 return -1;
1809 /* record prw_handle's asyncness for subsequent pread/pwrite operations */
1810 prw_handle_isasync = !!aio;
1811 return 0;
1814 ssize_t
1815 fhandler_disk_file::pread (void *buf, size_t count, off_t offset, void *aio)
1817 struct aiocb *aiocb = (struct aiocb *) aio;
1818 ssize_t res;
1820 if ((get_flags () & O_ACCMODE) == O_WRONLY)
1822 set_errno (EBADF);
1823 return -1;
1826 /* In binary mode, we can use an atomic NtReadFile call.
1827 Windows mandatory locking semantics disallow to use another HANDLE. */
1828 if (rbinary () && !mandatory_locking ())
1830 extern int is_at_eof (HANDLE h);
1831 NTSTATUS status;
1832 IO_STATUS_BLOCK io;
1833 LARGE_INTEGER off = { QuadPart:offset };
1834 HANDLE evt = aio ? (HANDLE) aiocb->aio_wincb.event : NULL;
1835 PIO_STATUS_BLOCK pio = aio ? (PIO_STATUS_BLOCK) &aiocb->aio_wincb : &io;
1837 /* If existing prw_handle asyncness doesn't match this call's, re-open */
1838 if (prw_handle && (prw_handle_isasync != !!aio))
1839 NtClose (prw_handle), prw_handle = NULL;
1841 if (!prw_handle && prw_open (false, aio))
1842 goto non_atomic;
1843 status = NtReadFile (prw_handle, evt, NULL, NULL, pio, buf, count,
1844 &off, NULL);
1845 if (status == STATUS_END_OF_FILE)
1846 res = 0;
1847 else if (!NT_SUCCESS (status))
1849 if (pc.isdir ())
1851 set_errno (EISDIR);
1852 return -1;
1854 if (status == (NTSTATUS) STATUS_ACCESS_VIOLATION)
1856 if (is_at_eof (prw_handle))
1858 res = 0;
1859 goto out;
1861 switch (mmap_is_attached_or_noreserve (buf, count))
1863 case MMAP_NORESERVE_COMMITED:
1864 status = NtReadFile (prw_handle, evt, NULL, NULL, pio,
1865 buf, count, &off, NULL);
1866 if (NT_SUCCESS (status))
1868 res = aio ? (ssize_t) aiocb->aio_wincb.info
1869 : io.Information;
1870 goto out;
1872 break;
1873 case MMAP_RAISE_SIGBUS:
1874 raise (SIGBUS);
1875 default:
1876 break;
1879 __seterrno_from_nt_status (status);
1880 return -1;
1882 else
1884 res = aio ? (ssize_t) aiocb->aio_wincb.info : io.Information;
1885 goto out;
1888 else
1890 non_atomic:
1891 /* Text mode stays slow and non-atomic. */
1892 off_t curpos = lseek (0, SEEK_CUR);
1893 if (curpos < 0 || lseek (offset, SEEK_SET) < 0)
1894 res = -1;
1895 else
1897 size_t tmp_count = count;
1898 read (buf, tmp_count);
1899 if (lseek (curpos, SEEK_SET) >= 0)
1900 res = (ssize_t) tmp_count;
1901 else
1902 res = -1;
1905 /* If this was a disallowed async request, simulate its conclusion */
1906 if (aio)
1908 aiocb->aio_rbytes = res;
1909 aiocb->aio_errno = res == -1 ? get_errno () : 0;
1910 SetEvent ((HANDLE) aiocb->aio_wincb.event);
1913 out:
1914 debug_printf ("%d = pread(%p, %ld, %D, %p)\n", res, buf, count, offset, aio);
1915 return res;
1918 ssize_t
1919 fhandler_disk_file::pwrite (void *buf, size_t count, off_t offset, void *aio)
1921 struct aiocb *aiocb = (struct aiocb *) aio;
1922 ssize_t res;
1924 if ((get_flags () & O_ACCMODE) == O_RDONLY)
1926 set_errno (EBADF);
1927 return -1;
1930 /* In binary mode, we can use an atomic NtWriteFile call.
1931 Windows mandatory locking semantics disallow to use another HANDLE. */
1932 if (wbinary () && !mandatory_locking ())
1934 NTSTATUS status;
1935 IO_STATUS_BLOCK io;
1936 FILE_STANDARD_INFORMATION fsi;
1937 LARGE_INTEGER off = { QuadPart:offset };
1938 HANDLE evt = aio ? (HANDLE) aiocb->aio_wincb.event : NULL;
1939 PIO_STATUS_BLOCK pio = aio ? (PIO_STATUS_BLOCK) &aiocb->aio_wincb : &io;
1941 /* If existing prw_handle asyncness doesn't match this call's, re-open */
1942 if (prw_handle && (prw_handle_isasync != !!aio))
1943 NtClose (prw_handle), prw_handle = NULL;
1945 /* If the file system supports sparse files and the application is
1946 writing beyond EOF spanning more than one sparsifiable chunk,
1947 convert the file to a sparse file. */
1948 if (pc.support_sparse ()
1949 && !has_attribute (FILE_ATTRIBUTE_SPARSE_FILE)
1950 && NT_SUCCESS (NtQueryInformationFile (get_handle (),
1951 &io, &fsi, sizeof fsi,
1952 FileStandardInformation))
1953 && span_sparse_chunk (offset, fsi.EndOfFile.QuadPart))
1955 NTSTATUS status;
1956 status = NtFsControlFile (get_handle (), NULL, NULL, NULL,
1957 &io, FSCTL_SET_SPARSE, NULL, 0, NULL, 0);
1958 if (NT_SUCCESS (status))
1959 pc.file_attributes (pc.file_attributes ()
1960 | FILE_ATTRIBUTE_SPARSE_FILE);
1961 debug_printf ("%y = NtFsControlFile(%S, FSCTL_SET_SPARSE)",
1962 status, pc.get_nt_native_path ());
1964 if (!prw_handle && prw_open (true, aio))
1965 goto non_atomic;
1966 status = NtWriteFile (prw_handle, evt, NULL, NULL, pio, buf, count,
1967 &off, NULL);
1968 if (!NT_SUCCESS (status))
1970 __seterrno_from_nt_status (status);
1971 return -1;
1973 res = aio ? (ssize_t) aiocb->aio_wincb.info : io.Information;
1974 goto out;
1976 else
1978 non_atomic:
1979 /* Text mode stays slow and non-atomic. */
1980 off_t curpos = lseek (0, SEEK_CUR);
1981 if (curpos < 0 || lseek (offset, SEEK_SET) < 0)
1982 res = curpos;
1983 else
1985 res = (ssize_t) write (buf, count);
1986 if (lseek (curpos, SEEK_SET) < 0)
1987 res = -1;
1990 /* If this was a disallowed async request, simulate its conclusion */
1991 if (aio)
1993 aiocb->aio_rbytes = res;
1994 aiocb->aio_errno = res == -1 ? get_errno () : 0;
1995 SetEvent ((HANDLE) aiocb->aio_wincb.event);
1998 out:
1999 debug_printf ("%d = pwrite(%p, %ld, %D, %p)\n", res, buf, count, offset, aio);
2000 return res;
2004 fhandler_disk_file::mkdir (mode_t mode)
2006 int res = -1;
2007 SECURITY_ATTRIBUTES sa = sec_none_nih;
2008 NTSTATUS status;
2009 HANDLE dir;
2010 OBJECT_ATTRIBUTES attr;
2011 IO_STATUS_BLOCK io;
2012 PFILE_FULL_EA_INFORMATION p = NULL;
2013 ULONG plen = 0;
2014 ULONG access = FILE_LIST_DIRECTORY | SYNCHRONIZE;
2016 if (pc.fs_is_nfs ())
2018 /* When creating a dir on an NFS share, we have to set the
2019 file mode by writing a NFS fattr3 structure with the
2020 correct mode bits set. */
2021 plen = sizeof (FILE_FULL_EA_INFORMATION) + sizeof (NFS_V3_ATTR)
2022 + sizeof (fattr3);
2023 p = (PFILE_FULL_EA_INFORMATION) alloca (plen);
2024 p->NextEntryOffset = 0;
2025 p->Flags = 0;
2026 p->EaNameLength = sizeof (NFS_V3_ATTR) - 1;
2027 p->EaValueLength = sizeof (fattr3);
2028 strcpy (p->EaName, NFS_V3_ATTR);
2029 fattr3 *nfs_attr = (fattr3 *) (p->EaName + p->EaNameLength + 1);
2030 memset (nfs_attr, 0, sizeof (fattr3));
2031 nfs_attr->type = NF3DIR;
2032 nfs_attr->mode = (mode & 07777) & ~cygheap->umask;
2034 else if (has_acls () && !isremote ())
2035 /* If the filesystem supports ACLs, we will overwrite the DACL after the
2036 call to NtCreateFile. This requires a handle with READ_CONTROL and
2037 WRITE_DAC access, otherwise get_file_sd and set_file_sd both have to
2038 open the file again.
2039 FIXME: On remote NTFS shares open sometimes fails because even the
2040 creator of the file doesn't have the right to change the DACL.
2041 I don't know what setting that is or how to recognize such a share,
2042 so for now we don't request WRITE_DAC on remote drives. */
2043 access |= READ_CONTROL | WRITE_DAC;
2044 /* Setting case sensitivity requires FILE_WRITE_ATTRIBUTES. */
2045 if (wincap.has_case_sensitive_dirs ()
2046 && !pc.isremote () && pc.fs_is_ntfs ())
2047 access |= FILE_WRITE_ATTRIBUTES;
2048 status = NtCreateFile (&dir, access, pc.get_object_attr (attr, sa), &io, NULL,
2049 FILE_ATTRIBUTE_DIRECTORY, FILE_SHARE_VALID_FLAGS,
2050 FILE_CREATE,
2051 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT
2052 | FILE_OPEN_FOR_BACKUP_INTENT,
2053 p, plen);
2054 if (NT_SUCCESS (status))
2056 /* Set the "directory attribute" so that pc.isdir() returns correct
2057 value in subsequent function calls. */
2058 pc.file_attributes (FILE_ATTRIBUTE_DIRECTORY);
2059 if (has_acls ())
2060 set_created_file_access (dir, pc, mode & 07777);
2061 #if 0
2062 /* FIXME: This default behaviour badly breaks interoperability.
2063 Inspecting the content of case-sensitive directories
2064 on remote machines results in lots of errors like
2065 disappearing diretories and files, file not found, etc. */
2067 /* Starting with Windows 10 1803, try to create all dirs below the
2068 installation root as case-sensitive. If STATUS_NOT_SUPPORTED
2069 is returned, WSL isn't installed (unfortunately a requirement
2070 for this functionality. */
2071 if (wincap.has_case_sensitive_dirs ()
2072 && !pc.isremote () && pc.fs_is_ntfs ())
2074 PUNICODE_STRING new_dir = pc.get_nt_native_path ();
2075 PUNICODE_STRING root_dir = &cygheap->installation_root;
2077 if (RtlEqualUnicodePathPrefix (new_dir, root_dir, TRUE)
2078 && new_dir->Buffer[root_dir->Length / sizeof (WCHAR)] == L'\\')
2080 FILE_CASE_SENSITIVE_INFORMATION fcsi;
2082 fcsi.Flags = FILE_CS_FLAG_CASE_SENSITIVE_DIR;
2083 status = NtSetInformationFile (dir, &io, &fcsi, sizeof fcsi,
2084 FileCaseSensitiveInformation);
2085 if (!NT_SUCCESS (status))
2087 debug_printf ("Setting dir case sensitivity, status %y",
2088 status);
2089 if (status == STATUS_NOT_SUPPORTED)
2091 debug_printf ("Dir case sensitivity requires WSL");
2092 wincap.disable_case_sensitive_dirs ();
2097 #endif
2098 NtClose (dir);
2099 res = 0;
2101 else
2102 __seterrno_from_nt_status (status);
2104 return res;
2108 fhandler_disk_file::rmdir ()
2110 if (!pc.isdir ())
2112 set_errno (ENOTDIR);
2113 return -1;
2115 if (!pc.exists ())
2117 set_errno (ENOENT);
2118 return -1;
2121 NTSTATUS status = unlink_nt (pc, false);
2123 if (!NT_SUCCESS (status))
2125 __seterrno_from_nt_status (status);
2126 return -1;
2128 return 0;
2131 /* This is the minimal number of entries which fit into the readdir cache.
2132 The number of bytes allocated by the cache is determined by this number,
2133 To tune caching, just tweak this number. To get a feeling for the size,
2134 the size of the readdir cache is DIR_NUM_ENTRIES * 624 + 4 bytes. */
2136 #define DIR_NUM_ENTRIES 100 /* Cache size 62404 bytes */
2138 #define DIR_BUF_SIZE (DIR_NUM_ENTRIES \
2139 * (sizeof (FILE_ID_BOTH_DIR_INFORMATION) \
2140 + (NAME_MAX + 1) * sizeof (WCHAR)))
2142 struct __DIR_cache
2144 char __cache[DIR_BUF_SIZE];
2145 ULONG __pos;
2148 #define d_cachepos(d) (((__DIR_cache *) (d)->__d_dirname)->__pos)
2149 #define d_cache(d) (((__DIR_cache *) (d)->__d_dirname)->__cache)
2151 #define d_mounts(d) ((__DIR_mounts *) (d)->__d_internal)
2153 DIR *
2154 fhandler_disk_file::opendir (int fd)
2156 DIR *dir;
2157 DIR *res = NULL;
2159 if (!pc.isdir ())
2160 set_errno (ENOTDIR);
2161 else if ((dir = (DIR *) malloc (sizeof (DIR))) == NULL)
2162 set_errno (ENOMEM);
2163 else if ((dir->__d_dirname = (char *) malloc ( sizeof (struct __DIR_cache)))
2164 == NULL)
2166 set_errno (ENOMEM);
2167 goto free_dir;
2169 else if ((dir->__d_dirent =
2170 (struct dirent *) malloc (sizeof (struct dirent))) == NULL)
2172 set_errno (ENOMEM);
2173 goto free_dirname;
2175 else
2177 cygheap_fdnew cfd;
2178 if (cfd < 0 && fd < 0)
2179 goto free_dirent;
2181 dir->__d_dirent->__d_version = __DIRENT_VERSION;
2182 dir->__d_cookie = __DIRENT_COOKIE;
2183 dir->__handle = INVALID_HANDLE_VALUE;
2184 dir->__d_position = 0;
2185 dir->__flags = (get_name ()[0] == '/' && get_name ()[1] == '\0')
2186 ? dirent_isroot : 0;
2187 dir->__d_internal = 0;
2189 if (pc.iscygdrive ())
2191 if (fd < 0 && !open (O_RDONLY, 0))
2192 goto free_mounts;
2194 else
2196 dir->__d_internal = (uintptr_t) new __DIR_mounts (get_name ());
2197 d_cachepos (dir) = 0;
2198 if (fd < 0)
2200 /* opendir() case. Initialize with given directory name and
2201 NULL directory handle. */
2202 OBJECT_ATTRIBUTES attr;
2203 NTSTATUS status;
2204 IO_STATUS_BLOCK io;
2205 /* Tools like ls(1) call dirfd() to fetch the directory
2206 descriptor for calls to facl or fstat. The tight access mask
2207 used so far is not sufficient to reuse the handle for these
2208 calls, instead the facl/fstat calls find the handle to be
2209 unusable and have to re-open the file for reading attributes
2210 and control data. So, what we do here is to try to open the
2211 directory with more relaxed access mask which enables to use
2212 the handle for the aforementioned purpose. This should work
2213 in almost all cases. Only if it doesn't work due to
2214 permission problems, we drop the additional access bits and
2215 try again. */
2216 ACCESS_MASK fstat_mask = READ_CONTROL | FILE_READ_ATTRIBUTES;
2220 status = NtOpenFile (&get_handle (),
2221 SYNCHRONIZE | FILE_LIST_DIRECTORY
2222 | fstat_mask,
2223 pc.get_object_attr (attr, sec_none_nih),
2224 &io, FILE_SHARE_VALID_FLAGS,
2225 FILE_SYNCHRONOUS_IO_NONALERT
2226 | FILE_OPEN_FOR_BACKUP_INTENT
2227 | FILE_DIRECTORY_FILE);
2228 if (!NT_SUCCESS (status))
2230 if (status == STATUS_ACCESS_DENIED && fstat_mask)
2231 fstat_mask = 0;
2232 else
2234 __seterrno_from_nt_status (status);
2235 goto free_mounts;
2239 while (!NT_SUCCESS (status));
2242 /* FileIdBothDirectoryInformation was unsupported on XP when
2243 accessing UDF. It's not clear if the call isn't also unsupported
2244 on other OS/FS combinations. Instead of testing for yet another
2245 error code, use FileIdBothDirectoryInformation only on FSes
2246 supporting persistent ACLs.
2248 NFS clients hide dangling symlinks from directory queries,
2249 unless you use the FileNamesInformation info class.
2250 FileIdBothDirectoryInformation works fine, but only if the NFS
2251 share is mounted to a drive letter. TODO: We don't test that
2252 here for now, but it might be worth to test if there's a speed
2253 gain in using FileIdBothDirectoryInformation, because it doesn't
2254 require to open the file to read the inode number. */
2255 if (pc.hasgood_inode ())
2257 dir->__flags |= dirent_set_d_ino;
2258 if (pc.fs_is_nfs ())
2259 dir->__flags |= dirent_nfs_d_ino;
2260 else if (!pc.has_buggy_fileid_dirinfo ())
2261 dir->__flags |= dirent_get_d_ino;
2264 if (fd >= 0)
2265 dir->__d_fd = fd;
2266 else
2268 /* Filling cfd with `this' (aka storing this in the file
2269 descriptor table should only happen after it's clear that
2270 opendir doesn't fail, otherwise we end up cfree'ing the
2271 fhandler twice, once in opendir() in dir.cc, the second
2272 time on exit. Nasty, nasty... */
2273 cfd = this;
2274 dir->__d_fd = cfd;
2276 set_close_on_exec (true);
2277 dir->__fh = this;
2278 res = dir;
2281 syscall_printf ("%p = opendir (%s)", res, get_name ());
2282 return res;
2284 free_mounts:
2285 delete d_mounts (dir);
2286 free_dirent:
2287 free (dir->__d_dirent);
2288 free_dirname:
2289 free (dir->__d_dirname);
2290 free_dir:
2291 free (dir);
2292 return res;
2295 ino_t
2296 readdir_get_ino (const char *path, bool dot_dot)
2298 char *fname;
2299 struct stat st;
2300 HANDLE hdl;
2301 OBJECT_ATTRIBUTES attr;
2302 IO_STATUS_BLOCK io;
2303 ino_t ino = 0;
2305 if (dot_dot)
2307 fname = (char *) alloca (strlen (path) + 4);
2308 char *c = stpcpy (fname, path);
2309 if (c[-1] != '/')
2310 *c++ = '/';
2311 strcpy (c, "..");
2312 path = fname;
2314 path_conv pc (path, PC_SYM_NOFOLLOW | PC_POSIX | PC_KEEP_HANDLE);
2315 if (pc.isspecial ())
2317 if (!stat_worker (pc, &st))
2318 ino = st.st_ino;
2320 else if (!pc.hasgood_inode ())
2321 ino = hash_path_name (0, pc.get_nt_native_path ());
2322 else if ((hdl = pc.handle ()) != NULL
2323 || NT_SUCCESS (NtOpenFile (&hdl, READ_CONTROL,
2324 pc.get_object_attr (attr, sec_none_nih),
2325 &io, FILE_SHARE_VALID_FLAGS,
2326 FILE_OPEN_FOR_BACKUP_INTENT
2327 | (pc.is_known_reparse_point ()
2328 ? FILE_OPEN_REPARSE_POINT : 0)))
2331 ino = pc.get_ino_by_handle (hdl);
2332 if (!ino)
2333 ino = hash_path_name (0, pc.get_nt_native_path ());
2335 return ino;
2339 fhandler_disk_file::readdir_helper (DIR *dir, dirent *de, DWORD w32_err,
2340 DWORD attr, PUNICODE_STRING fname)
2342 if (w32_err)
2344 switch (d_mounts (dir)->check_missing_mount (fname))
2346 case __DIR_mount_none:
2347 fname->Length = 0;
2348 return geterrno_from_win_error (w32_err);
2349 case __DIR_mount_virt_target:
2350 de->d_type = DT_DIR;
2351 break;
2352 default:
2353 break;
2355 attr = 0;
2356 dir->__flags &= ~dirent_set_d_ino;
2359 /* Set d_type if type can be determined from file attributes. For .lnk
2360 symlinks, d_type will be reset below. Reparse points can be NTFS
2361 symlinks, even if they have the FILE_ATTRIBUTE_DIRECTORY flag set. */
2362 if (attr && !(attr & ~FILE_ATTRIBUTE_VALID_FLAGS))
2364 if (attr & FILE_ATTRIBUTE_DIRECTORY)
2365 de->d_type = DT_DIR;
2366 /* FILE_ATTRIBUTE_SYSTEM might denote system-bit type symlinks. */
2367 else if (!(attr & FILE_ATTRIBUTE_SYSTEM))
2368 de->d_type = DT_REG;
2371 /* Check for reparse points that can be treated as posix symlinks.
2372 Mountpoints and unknown or unhandled reparse points will be treated
2373 as normal file/directory/unknown. In all cases, returning the INO of
2374 the reparse point (not of the target) matches behavior of posix systems.
2376 if (attr & FILE_ATTRIBUTE_REPARSE_POINT)
2378 OBJECT_ATTRIBUTES oattr;
2380 InitializeObjectAttributes (&oattr, fname, pc.objcaseinsensitive (),
2381 get_handle (), NULL);
2382 if (readdir_check_reparse_point (&oattr, isremote ()))
2383 de->d_type = DT_LNK;
2386 /* Check for Windows shortcut. If it's a Cygwin or U/WIN symlink, drop the
2387 .lnk suffix and set d_type accordingly. */
2388 if ((attr & (FILE_ATTRIBUTE_DIRECTORY
2389 | FILE_ATTRIBUTE_REPARSE_POINT
2390 | FILE_ATTRIBUTE_READONLY)) == FILE_ATTRIBUTE_READONLY
2391 && fname->Length > 4 * sizeof (WCHAR))
2393 UNICODE_STRING uname;
2395 RtlInitCountedUnicodeString (&uname,
2396 fname->Buffer
2397 + fname->Length / sizeof (WCHAR) - 4,
2398 4 * sizeof (WCHAR));
2399 if (RtlEqualUnicodeString (&uname, &ro_u_lnk, TRUE))
2401 tmp_pathbuf tp;
2402 char *file = tp.c_get ();
2403 char *p = stpcpy (file, pc.get_posix ());
2404 if (p[-1] != '/')
2405 *p++ = '/';
2406 sys_wcstombs (p, NT_MAX_PATH - (p - file),
2407 fname->Buffer, fname->Length / sizeof (WCHAR));
2408 path_conv fpath (file, PC_SYM_NOFOLLOW);
2409 if (fpath.issymlink ())
2411 fname->Length -= 4 * sizeof (WCHAR);
2412 de->d_type = DT_LNK;
2414 else if (fpath.isfifo ())
2416 fname->Length -= 4 * sizeof (WCHAR);
2417 de->d_type = DT_FIFO;
2419 else if (fpath.is_fs_special ())
2421 fname->Length -= 4 * sizeof (WCHAR);
2422 de->d_type = S_ISCHR (fpath.dev.mode ()) ? DT_CHR : DT_BLK;
2427 sys_wcstombs (de->d_name, NAME_MAX + 1, fname->Buffer,
2428 fname->Length / sizeof (WCHAR));
2430 /* Don't try to optimize relative to dir->__d_position. On several
2431 filesystems it's no safe bet that "." and ".." entries always
2432 come first. */
2433 if (de->d_name[0] == '.')
2435 if (de->d_name[1] == '\0')
2436 dir->__flags |= dirent_saw_dot;
2437 else if (de->d_name[1] == '.' && de->d_name[2] == '\0')
2438 dir->__flags |= dirent_saw_dot_dot;
2440 return 0;
2444 fhandler_disk_file::readdir (DIR *dir, dirent *de)
2446 int res = 0;
2447 NTSTATUS status = STATUS_SUCCESS;
2448 PFILE_ID_BOTH_DIR_INFORMATION buf = NULL;
2449 PWCHAR FileName;
2450 ULONG FileNameLength;
2451 ULONG FileAttributes;
2452 IO_STATUS_BLOCK io;
2453 UNICODE_STRING fname;
2455 /* d_cachepos always refers to the next cache entry to use. If it's 0
2456 we must reload the cache. */
2457 FileAttributes = 0;
2458 if (d_cachepos (dir) == 0)
2460 if ((dir->__flags & dirent_get_d_ino))
2462 status = NtQueryDirectoryFile (get_handle (), NULL, NULL, NULL, &io,
2463 d_cache (dir), DIR_BUF_SIZE,
2464 FileIdBothDirectoryInformation,
2465 FALSE, NULL, dir->__d_position == 0);
2466 /* FileIdBothDirectoryInformation isn't supported for remote drives
2467 on NT4 and 2K systems. There are also hacked versions of
2468 Samba 3.0.x out there (Debian-based it seems), which return
2469 STATUS_NOT_SUPPORTED rather than handling this info class.
2470 We just fall back to using a standard directory query in
2471 this case and note this case using the dirent_get_d_ino flag. */
2472 if (!NT_SUCCESS (status) && status != STATUS_NO_MORE_FILES
2473 && (status == STATUS_INVALID_LEVEL
2474 || status == STATUS_NOT_SUPPORTED
2475 || status == STATUS_INVALID_PARAMETER
2476 || status == STATUS_INVALID_NETWORK_RESPONSE
2477 || status == STATUS_INVALID_INFO_CLASS))
2478 dir->__flags &= ~dirent_get_d_ino;
2479 /* Something weird happens on Samba up to version 3.0.21c, which is
2480 fixed in 3.0.22. FileIdBothDirectoryInformation seems to work
2481 nicely, but only up to the 128th entry in the directory. After
2482 reaching this entry, the next call to NtQueryDirectoryFile
2483 (FileIdBothDirectoryInformation) returns STATUS_INVALID_LEVEL.
2484 Why should we care, we can just switch to
2485 FileBothDirectoryInformation, isn't it? Nope! The next call to
2486 NtQueryDirectoryFile(FileBothDirectoryInformation) actually
2487 returns STATUS_NO_MORE_FILES, regardless how many files are left
2488 unread in the directory. This does not happen when using
2489 FileBothDirectoryInformation right from the start, but since
2490 we can't decide whether the server we're talking with has this
2491 bug or not, we end up serving Samba shares always in the slow
2492 mode using FileBothDirectoryInformation. So, what we do here is
2493 to implement the solution suggested by Andrew Tridgell, we just
2494 reread all entries up to dir->d_position using
2495 FileBothDirectoryInformation.
2496 However, We do *not* mark this server as broken and fall back to
2497 using FileBothDirectoryInformation further on. This would slow
2498 down every access to such a server, even for directories under
2499 128 entries. Also, bigger dirs only suffer from one additional
2500 call per full directory scan, which shouldn't be too big a hit.
2501 This can easily be changed if necessary. */
2502 if (status == STATUS_INVALID_LEVEL && dir->__d_position)
2504 d_cachepos (dir) = 0;
2505 for (int cnt = 0; cnt < dir->__d_position; ++cnt)
2507 if (d_cachepos (dir) == 0)
2509 status = NtQueryDirectoryFile (get_handle (), NULL, NULL,
2510 NULL, &io, d_cache (dir),
2511 DIR_BUF_SIZE,
2512 FileBothDirectoryInformation,
2513 FALSE, NULL, cnt == 0);
2514 if (!NT_SUCCESS (status))
2515 goto go_ahead;
2517 buf = (PFILE_ID_BOTH_DIR_INFORMATION) (d_cache (dir)
2518 + d_cachepos (dir));
2519 if (buf->NextEntryOffset == 0)
2520 d_cachepos (dir) = 0;
2521 else
2522 d_cachepos (dir) += buf->NextEntryOffset;
2524 goto go_ahead;
2527 /* NFS must use FileNamesInformation! Any other information class
2528 skips all symlinks. */
2529 if (!(dir->__flags & dirent_get_d_ino))
2530 status = NtQueryDirectoryFile (get_handle (), NULL, NULL, NULL, &io,
2531 d_cache (dir), DIR_BUF_SIZE,
2532 (dir->__flags & dirent_nfs_d_ino)
2533 ? FileNamesInformation
2534 : FileBothDirectoryInformation,
2535 FALSE, NULL, dir->__d_position == 0);
2538 go_ahead:
2540 if (status == STATUS_NO_MORE_FILES)
2541 /*nothing*/;
2542 else if (!NT_SUCCESS (status))
2543 debug_printf ("NtQueryDirectoryFile failed, status %y, win32 error %u",
2544 status, RtlNtStatusToDosError (status));
2545 else
2547 buf = (PFILE_ID_BOTH_DIR_INFORMATION) (d_cache (dir) + d_cachepos (dir));
2548 if (buf->NextEntryOffset == 0)
2549 d_cachepos (dir) = 0;
2550 else
2551 d_cachepos (dir) += buf->NextEntryOffset;
2552 if ((dir->__flags & dirent_get_d_ino))
2554 FileName = buf->FileName;
2555 FileNameLength = buf->FileNameLength;
2556 FileAttributes = buf->FileAttributes;
2557 if ((dir->__flags & dirent_set_d_ino))
2558 de->d_ino = buf->FileId.QuadPart;
2560 else if ((dir->__flags & dirent_nfs_d_ino))
2562 FileName = ((PFILE_NAMES_INFORMATION) buf)->FileName;
2563 FileNameLength = ((PFILE_NAMES_INFORMATION) buf)->FileNameLength;
2565 else
2567 FileName = ((PFILE_BOTH_DIR_INFORMATION) buf)->FileName;
2568 FileNameLength =
2569 ((PFILE_BOTH_DIR_INFORMATION) buf)->FileNameLength;
2570 FileAttributes =
2571 ((PFILE_BOTH_DIR_INFORMATION) buf)->FileAttributes;
2573 RtlInitCountedUnicodeString (&fname, FileName, FileNameLength);
2574 d_mounts (dir)->check_mount (&fname);
2575 if (de->d_ino == 0 && (dir->__flags & dirent_set_d_ino))
2577 /* Don't try to optimize relative to dir->__d_position. On several
2578 filesystems it's no safe bet that "." and ".." entries always
2579 come first. */
2580 if (FileNameLength == sizeof (WCHAR) && FileName[0] == '.')
2581 de->d_ino = pc.get_ino_by_handle (get_handle ());
2582 else if (FileNameLength == 2 * sizeof (WCHAR)
2583 && FileName[0] == L'.' && FileName[1] == L'.')
2585 if (!(dir->__flags & dirent_isroot))
2586 de->d_ino = readdir_get_ino (get_name (), true);
2587 else
2588 de->d_ino = pc.get_ino_by_handle (get_handle ());
2590 else
2592 OBJECT_ATTRIBUTES attr;
2593 HANDLE hdl;
2594 NTSTATUS f_status;
2596 InitializeObjectAttributes (&attr, &fname,
2597 pc.objcaseinsensitive (),
2598 get_handle (), NULL);
2599 /* FILE_OPEN_REPARSE_POINT on NFS is a no-op, so the normal
2600 NtOpenFile here returns the inode number of the symlink target,
2601 rather than the inode number of the symlink itself.
2603 Worse, trying to open a symlink without setting the special
2604 "ActOnSymlink" EA triggers a bug in Windows 7 which results
2605 in a timeout of up to 20 seconds, followed by two exceptions
2606 in the NT kernel.
2608 Since both results are far from desirable, we open symlinks
2609 on NFS so that we get the right inode and a happy W7.
2610 And, since some filesystems choke on the EAs, we don't
2611 use them unconditionally. */
2612 f_status = (dir->__flags & dirent_nfs_d_ino)
2613 ? NtCreateFile (&hdl,
2614 READ_CONTROL | FILE_READ_ATTRIBUTES,
2615 &attr, &io, NULL, 0,
2616 FILE_SHARE_VALID_FLAGS, FILE_OPEN,
2617 FILE_OPEN_FOR_BACKUP_INTENT,
2618 &nfs_aol_ffei, sizeof nfs_aol_ffei)
2619 : NtOpenFile (&hdl, READ_CONTROL, &attr, &io,
2620 FILE_SHARE_VALID_FLAGS,
2621 FILE_OPEN_FOR_BACKUP_INTENT
2622 | FILE_OPEN_REPARSE_POINT);
2623 if (NT_SUCCESS (f_status))
2625 /* We call NtQueryInformationFile here, rather than
2626 pc.get_ino_by_handle(), otherwise we can't short-circuit
2627 dirent_set_d_ino correctly. */
2628 FILE_INTERNAL_INFORMATION fii;
2629 f_status = NtQueryInformationFile (hdl, &io, &fii, sizeof fii,
2630 FileInternalInformation);
2631 /* On NFS fetch the (faked, but useful) DOS attribute.
2632 We need it to recognize shortcut FIFOs. */
2633 if ((dir->__flags & dirent_nfs_d_ino))
2635 FILE_BASIC_INFORMATION fbi;
2637 if (NT_SUCCESS (NtQueryInformationFile (hdl, &io, &fbi,
2638 sizeof fbi, FileBasicInformation)))
2639 FileAttributes = fbi.FileAttributes;
2641 NtClose (hdl);
2642 if (NT_SUCCESS (f_status))
2644 if (pc.isgood_inode (fii.IndexNumber.QuadPart))
2645 de->d_ino = fii.IndexNumber.QuadPart;
2646 else
2647 /* Untrusted file system. Don't try to fetch inode
2648 number again. */
2649 dir->__flags &= ~dirent_set_d_ino;
2656 if (!(res = readdir_helper (dir, de, RtlNtStatusToDosError (status),
2657 FileAttributes, &fname)))
2658 dir->__d_position++;
2659 else if (!(dir->__flags & dirent_saw_dot))
2661 strcpy (de->d_name , ".");
2662 de->d_ino = pc.get_ino_by_handle (get_handle ());
2663 de->d_type = DT_DIR;
2664 dir->__d_position++;
2665 dir->__flags |= dirent_saw_dot;
2666 res = 0;
2668 else if (!(dir->__flags & dirent_saw_dot_dot))
2670 strcpy (de->d_name , "..");
2671 if (!(dir->__flags & dirent_isroot))
2672 de->d_ino = readdir_get_ino (get_name (), true);
2673 else
2674 de->d_ino = pc.get_ino_by_handle (get_handle ());
2675 de->d_type = DT_DIR;
2676 dir->__d_position++;
2677 dir->__flags |= dirent_saw_dot_dot;
2678 res = 0;
2681 syscall_printf ("%d = readdir(%p, %p) (L\"%lS\" > \"%ls\") (attr %y > type %d)",
2682 res, dir, &de, res ? NULL : &fname, res ? "***" : de->d_name,
2683 FileAttributes, de->d_type);
2684 return res;
2687 long
2688 fhandler_disk_file::telldir (DIR *dir)
2690 return dir->__d_position;
2693 void
2694 fhandler_disk_file::seekdir (DIR *dir, long loc)
2696 rewinddir (dir);
2697 while (loc > dir->__d_position)
2698 if (!::readdir (dir))
2699 break;
2702 void
2703 fhandler_disk_file::rewinddir (DIR *dir)
2705 d_cachepos (dir) = 0;
2706 dir->__d_position = 0;
2707 d_mounts (dir)->rewind ();
2711 fhandler_disk_file::closedir (DIR *dir)
2713 int res = 0;
2715 delete d_mounts (dir);
2716 syscall_printf ("%d = closedir(%p, %s)", res, dir, get_name ());
2717 return res;
2720 uint64_t
2721 fhandler_disk_file::fs_ioc_getflags ()
2723 NTSTATUS status;
2724 IO_STATUS_BLOCK io;
2725 FILE_BASIC_INFORMATION fbi;
2726 FILE_CASE_SENSITIVE_INFORMATION fcsi;
2727 uint64_t flags = 0;
2729 status = NtQueryInformationFile (get_handle (), &io, &fbi, sizeof fbi,
2730 FileBasicInformation);
2731 if (NT_SUCCESS (status))
2733 flags = (uint64_t) fbi.FileAttributes & FS_FL_USER_VISIBLE;
2734 pc.file_attributes (fbi.FileAttributes);
2736 else
2737 flags = (uint64_t) pc.file_attributes () & FS_FL_USER_VISIBLE;
2738 if (pc.isdir () && wincap.has_case_sensitive_dirs ()
2739 && !pc.isremote () && pc.fs_is_ntfs ())
2741 fcsi.Flags = 0;
2742 status = NtQueryInformationFile (get_handle (), &io,
2743 &fcsi, sizeof fcsi,
2744 FileCaseSensitiveInformation);
2745 if (NT_SUCCESS (status)
2746 && (fcsi.Flags & FILE_CS_FLAG_CASE_SENSITIVE_DIR))
2747 flags |= FS_CASESENS_FL;
2749 return flags;
2752 /* Settable DOS attributes */
2753 #define FS_FL_SETATTRIBS (FS_READONLY_FL \
2754 | FS_HIDDEN_FL \
2755 | FS_SYSTEM_FL \
2756 | FS_ARCHIVE_FL \
2757 | FS_TEMP_FL \
2758 | FS_NOTINDEXED_FL)
2761 fhandler_disk_file::fs_ioc_setflags (uint64_t flags)
2763 int ret = -1;
2764 uint64_t old_flags;
2765 HANDLE fh;
2766 NTSTATUS status;
2767 OBJECT_ATTRIBUTES attr;
2768 IO_STATUS_BLOCK io;
2769 FILE_BASIC_INFORMATION fbi;
2770 FILE_SET_SPARSE_BUFFER fssb;
2771 USHORT comp;
2772 FILE_CASE_SENSITIVE_INFORMATION fcsi;
2774 if ((get_access () & (GENERIC_WRITE | FILE_WRITE_ATTRIBUTES)) != 0)
2775 fh = get_handle ();
2776 else
2778 status = NtOpenFile (&fh, FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES,
2779 pc.init_reopen_attr (attr, get_handle ()), &io,
2780 FILE_SHARE_VALID_FLAGS,
2781 FILE_OPEN_FOR_BACKUP_INTENT);
2782 if (!NT_SUCCESS (status))
2784 fh = get_handle ();
2785 __seterrno_from_nt_status (status);
2786 goto out;
2789 old_flags = fs_ioc_getflags ();
2790 if ((old_flags & FS_FL_SETATTRIBS) != (flags & FS_FL_SETATTRIBS))
2792 fbi.CreationTime.QuadPart
2793 = fbi.LastAccessTime.QuadPart
2794 = fbi.LastWriteTime.QuadPart
2795 = fbi.ChangeTime.QuadPart = 0LL;
2796 fbi.FileAttributes = (ULONG) old_flags;
2797 fbi.FileAttributes &= ~FS_FL_SETATTRIBS;
2798 fbi.FileAttributes |= (flags & FS_FL_SETATTRIBS);
2799 if (fbi.FileAttributes == 0)
2800 fbi.FileAttributes = FILE_ATTRIBUTE_NORMAL;
2801 status = NtSetInformationFile (fh, &io, &fbi, sizeof fbi,
2802 FileBasicInformation);
2803 if (!NT_SUCCESS (status))
2805 __seterrno_from_nt_status (status);
2806 goto out;
2809 if (!pc.isdir() && (flags & FS_SPARSE_FL) != (old_flags & FS_SPARSE_FL))
2811 fssb.SetSparse = (flags & FS_SPARSE_FL) ? TRUE : FALSE;
2812 status = NtFsControlFile (fh, NULL, NULL, NULL, &io,
2813 FSCTL_SET_SPARSE, &fssb, sizeof fssb, NULL, 0);
2814 if (!NT_SUCCESS (status))
2816 __seterrno_from_nt_status (status);
2817 goto out;
2820 if (pc.isdir () && (flags & FS_CASESENS_FL) != (old_flags & FS_CASESENS_FL))
2822 if (wincap.has_case_sensitive_dirs ()
2823 && !pc.isremote () && pc.fs_is_ntfs ())
2825 fcsi.Flags = (flags & FS_CASESENS_FL)
2826 ? FILE_CS_FLAG_CASE_SENSITIVE_DIR : 0;
2827 status = NtSetInformationFile (fh, &io, &fcsi, sizeof fcsi,
2828 FileCaseSensitiveInformation);
2829 if (!NT_SUCCESS (status))
2831 __seterrno_from_nt_status (status);
2832 goto out;
2835 else
2837 set_errno (ENOTSUP);
2838 goto out;
2841 if ((flags & FS_COMPRESSED_FL) != (old_flags & FS_COMPRESSED_FL))
2843 if (fh != get_handle ())
2844 NtClose (fh);
2845 fh = NULL;
2846 if ((get_access () & (GENERIC_WRITE | GENERIC_READ))
2847 != (GENERIC_WRITE | GENERIC_READ))
2849 status = NtOpenFile (&fh, GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
2850 pc.init_reopen_attr (attr, get_handle ()), &io,
2851 FILE_SHARE_VALID_FLAGS,
2852 FILE_SYNCHRONOUS_IO_NONALERT
2853 | FILE_OPEN_FOR_BACKUP_INTENT);
2854 if (!NT_SUCCESS (status))
2856 fh = get_handle ();
2857 __seterrno_from_nt_status (status);
2858 goto out;
2861 comp = (flags & FS_COMPRESSED_FL)
2862 ? COMPRESSION_FORMAT_DEFAULT : COMPRESSION_FORMAT_NONE;
2863 status = NtFsControlFile (fh, NULL, NULL, NULL, &io,
2864 FSCTL_SET_COMPRESSION, &comp, sizeof comp,
2865 NULL, 0);
2866 if (!NT_SUCCESS (status))
2868 __seterrno_from_nt_status (status);
2869 goto out;
2872 if (!pc.isdir() && (flags & FS_ENCRYPT_FL) != (old_flags & FS_ENCRYPT_FL))
2874 tmp_pathbuf tp;
2875 PWCHAR path = tp.w_get ();
2876 BOOL cret;
2878 /* EncryptFileW/DecryptFileW needs exclusive access. */
2879 if (fh != get_handle ())
2880 NtClose (fh);
2881 NtClose (get_handle ());
2882 set_handle (NULL);
2884 pc.get_wide_win32_path (path);
2885 cret = (flags & FS_ENCRYPT_FL)
2886 ? EncryptFileW (path) : DecryptFileW (path, 0);
2887 status = NtOpenFile (&fh, get_access (),
2888 pc.get_object_attr (attr, sec_none_nih), &io,
2889 FILE_SHARE_VALID_FLAGS,
2890 FILE_SYNCHRONOUS_IO_NONALERT
2891 | FILE_OPEN_FOR_BACKUP_INTENT);
2892 if (!NT_SUCCESS (status))
2894 __seterrno_from_nt_status (status);
2895 return -1;
2897 set_handle (fh);
2898 if (!cret)
2900 __seterrno ();
2901 goto out;
2904 ret = 0;
2905 out:
2906 status = NtQueryInformationFile (fh, &io, &fbi, sizeof fbi,
2907 FileBasicInformation);
2908 if (NT_SUCCESS (status))
2909 pc.file_attributes (fbi.FileAttributes);
2910 if (fh != get_handle ())
2911 NtClose (fh);
2912 return ret;
2916 fhandler_disk_file::ioctl (unsigned int cmd, void *p)
2918 int ret = -1;
2919 uint64_t flags = 0;
2921 switch (cmd)
2923 case FS_IOC_GETFLAGS:
2924 __try
2926 uint64_t *fp = (uint64_t *) p;
2927 *fp = fs_ioc_getflags ();
2928 ret = 0;
2930 __except (EFAULT) {}
2931 __endtry
2932 break;
2933 case FS_IOC_SETFLAGS:
2934 __try
2936 flags = *(__uint64_t *) p;
2938 __except (EFAULT)
2940 break;
2942 __endtry
2943 if (flags & ~FS_FL_USER_MODIFIABLE)
2945 set_errno (EINVAL);
2946 break;
2948 ret = fs_ioc_setflags (flags);
2949 break;
2950 default:
2951 ret = fhandler_base::ioctl (cmd, p);
2952 break;
2954 syscall_printf ("%d = ioctl_file(%x, %p)", ret, cmd, p);
2955 return ret;