]> cygwin.com Git - newlib-cygwin.git/blob - winsup/cygwin/fhandler/disk_file.cc
f4c21d3b7f824fd62f0d76e5fc8103594f7351da
[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 #include <aio.h>
28 #include <fcntl.h>
29 #include <cygwin/fs.h>
30
31 #define _LIBC
32 #include <dirent.h>
33
34 enum __DIR_mount_type {
35 __DIR_mount_none = 0,
36 __DIR_mount_target,
37 __DIR_mount_virt_target
38 };
39
40 class __DIR_mounts
41 {
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;
48
49 #define __DIR_PROC (MAX_MOUNTS)
50 #define __DIR_CYGDRIVE (MAX_MOUNTS+1)
51 #define __DIR_DEV (MAX_MOUNTS+2)
52
53 public:
54 __DIR_mounts (const char *posix_path)
55 : parent_dir (posix_path)
56 {
57 parent_dir_len = strlen (parent_dir);
58 count = mount_table->get_mounts_here (parent_dir, parent_dir_len, mounts,
59 &cygdrive);
60 rewind ();
61 }
62 ~__DIR_mounts ()
63 {
64 mount_table->free_mounts_here (mounts, count, &cygdrive);
65 }
66 /* For an entry within this dir, check if a mount point exists. */
67 bool check_mount (PUNICODE_STRING fname)
68 {
69 if (parent_dir_len == 1) /* root dir */
70 {
71 if (RtlEqualUnicodeString (fname, &ro_u_proc, FALSE))
72 {
73 found[__DIR_PROC] = true;
74 return true;
75 }
76 if (RtlEqualUnicodeString (fname, &ro_u_dev, FALSE))
77 {
78 found[__DIR_DEV] = true;
79 return true;
80 }
81 if (fname->Length / sizeof (WCHAR) == mount_table->cygdrive_len - 2
82 && RtlEqualUnicodeString (fname, &cygdrive, FALSE))
83 {
84 found[__DIR_CYGDRIVE] = true;
85 return true;
86 }
87 }
88 for (int i = 0; i < count; ++i)
89 if (RtlEqualUnicodeString (fname, &mounts[i], FALSE))
90 {
91 found[i] = true;
92 return true;
93 }
94 return false;
95 }
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)
99 {
100 for (int i = 0; i < count; ++i)
101 if (!found[i])
102 {
103 found[i] = true;
104 if (retname)
105 *retname = mounts[i];
106 return __DIR_mount_target;
107 }
108 if (parent_dir_len == 1) /* root dir */
109 {
110 if (!found[__DIR_PROC])
111 {
112 found[__DIR_PROC] = true;
113 if (retname)
114 *retname = ro_u_proc;
115 return __DIR_mount_virt_target;
116 }
117 if (!found[__DIR_DEV])
118 {
119 found[__DIR_DEV] = true;
120 if (retname)
121 *retname = ro_u_dev;
122 return __DIR_mount_virt_target;
123 }
124 if (!found[__DIR_CYGDRIVE])
125 {
126 found[__DIR_CYGDRIVE] = true;
127 if (cygdrive.Length > 0)
128 {
129 if (retname)
130 *retname = cygdrive;
131 return __DIR_mount_virt_target;
132 }
133 }
134 }
135 return __DIR_mount_none;
136 }
137 void rewind () { memset (found, 0, sizeof found); }
138 };
139
140 inline bool
141 path_conv::isgood_inode (ino_t ino) const
142 {
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 /* Starting with version 3.5.4, Samba returns the real inode numbers, if
152 the file is on the same device as the root of the share (Samba function
153 get_FileIndex). */
154 if (fs_is_samba () && fs.samba_version () < 0x03050400)
155 return false;
156 /* Otherwise, trust the inode numbers unless proved otherwise. */
157 return true;
158 }
159
160 /* Check reparse point to determine if it should be treated as a
161 posix symlink or as a normal file/directory. Logic is explained
162 in detail in check_reparse_point_target in path.cc. */
163 static inline bool
164 readdir_check_reparse_point (POBJECT_ATTRIBUTES attr, bool remote)
165 {
166 NTSTATUS status;
167 HANDLE reph;
168 IO_STATUS_BLOCK io;
169 tmp_pathbuf tp;
170 UNICODE_STRING symbuf;
171 bool ret = false;
172
173 status = NtOpenFile (&reph, READ_CONTROL, attr, &io, FILE_SHARE_VALID_FLAGS,
174 FILE_OPEN_NO_RECALL
175 | FILE_OPEN_FOR_BACKUP_INTENT
176 | FILE_OPEN_REPARSE_POINT);
177 if (NT_SUCCESS (status))
178 {
179 PREPARSE_DATA_BUFFER rp = (PREPARSE_DATA_BUFFER) tp.c_get ();
180 ret = (check_reparse_point_target (reph, remote, rp, &symbuf) > 0);
181 NtClose (reph);
182 }
183 return ret;
184 }
185
186 inline ino_t
187 path_conv::get_ino_by_handle (HANDLE hdl)
188 {
189 IO_STATUS_BLOCK io;
190 FILE_INTERNAL_INFORMATION fii;
191
192 if (NT_SUCCESS (NtQueryInformationFile (hdl, &io, &fii, sizeof fii,
193 FileInternalInformation))
194 && isgood_inode (fii.IndexNumber.QuadPart))
195 return fii.IndexNumber.QuadPart;
196 return 0;
197 }
198
199 /* For files on NFS shares, we request an EA of type NfsV3Attributes.
200 This returns the content of a struct fattr3 as defined in RFC 1813.
201 The content is the NFS equivalent of struct stat. so there's not much
202 to do here except for copying. */
203 int
204 fhandler_base::fstat_by_nfs_ea (struct stat *buf)
205 {
206 fattr3 *nfs_attr = pc.nfsattr ();
207 PWCHAR domain;
208 cyg_ldap cldap;
209 bool ldap_open = false;
210
211 /* NFS stumbles over its own caching. If you write to the file,
212 a subsequent fstat does not return the actual size of the file,
213 but the size at the time the handle has been opened. Unless
214 access through another handle invalidates the caching within the
215 NFS client. Skip this for Cygwin-created Symlinks playing FIFOs
216 (this sets the filler1 member to NF3FIFO). */
217 if (get_handle () && nfs_attr->filler1 != NF3FIFO)
218 {
219 if (get_access () & GENERIC_WRITE)
220 FlushFileBuffers (get_handle ());
221 pc.get_finfo (get_handle ());
222 }
223 buf->st_dev = nfs_attr->fsid;
224 buf->st_ino = nfs_attr->fileid;
225 buf->st_mode = (nfs_attr->mode & 0xfff)
226 | nfs_type_mapping[nfs_attr->type & 7];
227 buf->st_nlink = nfs_attr->nlink;
228 if (cygheap->pg.nss_pwd_db ())
229 {
230 /* Try to map UNIX uid/gid to Cygwin uid/gid. If there's no mapping in
231 the cache, try to fetch it from the configured RFC 2307 domain (see
232 last comment in cygheap_domain_info::init() for more information) and
233 add it to the mapping cache. */
234 buf->st_uid = cygheap->ugid_cache.get_uid (nfs_attr->uid);
235 if (buf->st_uid == ILLEGAL_UID)
236 {
237 uid_t map_uid = ILLEGAL_UID;
238
239 domain = cygheap->dom.get_rfc2307_domain ();
240 if ((ldap_open = (cldap.open (domain) == NO_ERROR)))
241 map_uid = cldap.remap_uid (nfs_attr->uid);
242 if (map_uid == ILLEGAL_UID)
243 map_uid = MAP_UNIX_TO_CYGWIN_ID (nfs_attr->uid);
244 cygheap->ugid_cache.add_uid (nfs_attr->uid, map_uid);
245 buf->st_uid = map_uid;
246 }
247 }
248 else /* fake files being owned by current user. */
249 buf->st_uid = myself->uid;
250 if (cygheap->pg.nss_grp_db ())
251 {
252 /* See above */
253 buf->st_gid = cygheap->ugid_cache.get_gid (nfs_attr->gid);
254 if (buf->st_gid == ILLEGAL_GID)
255 {
256 gid_t map_gid = ILLEGAL_GID;
257
258 domain = cygheap->dom.get_rfc2307_domain ();
259 if ((ldap_open || cldap.open (domain) == NO_ERROR))
260 map_gid = cldap.remap_gid (nfs_attr->gid);
261 if (map_gid == ILLEGAL_GID)
262 map_gid = MAP_UNIX_TO_CYGWIN_ID (nfs_attr->gid);
263 cygheap->ugid_cache.add_gid (nfs_attr->gid, map_gid);
264 buf->st_gid = map_gid;
265 }
266 }
267 else /* fake files being owned by current group. */
268 buf->st_gid = myself->gid;
269 buf->st_rdev = makedev (nfs_attr->rdev.specdata1,
270 nfs_attr->rdev.specdata2);
271 buf->st_size = nfs_attr->size;
272 buf->st_blksize = PREFERRED_IO_BLKSIZE;
273 buf->st_blocks = (nfs_attr->used + S_BLKSIZE - 1) / S_BLKSIZE;
274 buf->st_atim.tv_sec = nfs_attr->atime.tv_sec;
275 buf->st_atim.tv_nsec = nfs_attr->atime.tv_nsec;
276 buf->st_mtim.tv_sec = nfs_attr->mtime.tv_sec;
277 buf->st_mtim.tv_nsec = nfs_attr->mtime.tv_nsec;
278 buf->st_ctim.tv_sec = nfs_attr->ctime.tv_sec;
279 buf->st_ctim.tv_nsec = nfs_attr->ctime.tv_nsec;
280 return 0;
281 }
282
283 int
284 fhandler_base::fstat_by_handle (struct stat *buf)
285 {
286 HANDLE h = get_stat_handle ();
287 NTSTATUS status = 0;
288
289 /* If the file has been opened for other purposes than stat, we can't rely
290 on the information stored in pc.fai. So we overwrite them here. */
291 if (get_handle ())
292 {
293 status = pc.get_finfo (h);
294 if (!NT_SUCCESS (status))
295 {
296 debug_printf ("%y = NtQueryInformationFile(%S, FileAllInformation)",
297 status, pc.get_nt_native_path ());
298 return -1;
299 }
300 }
301 if (pc.isgood_inode (pc.fai ()->InternalInformation.IndexNumber.QuadPart))
302 ino = pc.fai ()->InternalInformation.IndexNumber.QuadPart;
303 return fstat_helper (buf);
304 }
305
306 int
307 fhandler_base::fstat_by_name (struct stat *buf)
308 {
309 NTSTATUS status;
310 OBJECT_ATTRIBUTES attr;
311 IO_STATUS_BLOCK io;
312 UNICODE_STRING dirname;
313 UNICODE_STRING basename;
314 HANDLE dir;
315 struct {
316 FILE_ID_BOTH_DIR_INFORMATION fdi;
317 WCHAR buf[NAME_MAX + 1];
318 } fdi_buf;
319
320 if (!ino && pc.hasgood_inode () && !pc.has_buggy_fileid_dirinfo ())
321 {
322 RtlSplitUnicodePath (pc.get_nt_native_path (), &dirname, &basename);
323 InitializeObjectAttributes (&attr, &dirname, pc.objcaseinsensitive (),
324 NULL, NULL);
325 status = NtOpenFile (&dir, SYNCHRONIZE | FILE_LIST_DIRECTORY,
326 &attr, &io, FILE_SHARE_VALID_FLAGS,
327 FILE_SYNCHRONOUS_IO_NONALERT
328 | FILE_OPEN_FOR_BACKUP_INTENT
329 | FILE_DIRECTORY_FILE);
330 if (!NT_SUCCESS (status))
331 debug_printf ("%y = NtOpenFile(%S)", status, &dirname);
332 else
333 {
334 status = NtQueryDirectoryFile (dir, NULL, NULL, NULL, &io,
335 &fdi_buf.fdi, sizeof fdi_buf,
336 FileIdBothDirectoryInformation,
337 TRUE, &basename, TRUE);
338 NtClose (dir);
339 if (!NT_SUCCESS (status))
340 debug_printf ("%y = NtQueryDirectoryFile(%S)", status, &dirname);
341 else
342 ino = fdi_buf.fdi.FileId.QuadPart;
343 }
344 }
345 return fstat_helper (buf);
346 }
347
348 int
349 fhandler_base::fstat_fs (struct stat *buf)
350 {
351 int res = -1;
352 int oret;
353 int open_flags = O_RDONLY | O_BINARY;
354
355 if (get_stat_handle ())
356 {
357 if (!nohandle ())
358 {
359 if (pc.fs_is_nfs ())
360 res = fstat_by_nfs_ea (buf);
361 else if (!is_fs_special () || get_flags () & O_PATH)
362 res = fstat_by_handle (buf);
363 }
364 if (res)
365 res = fstat_by_name (buf);
366 return res;
367 }
368 /* First try to open with generic read access. This allows to read the file
369 in fstat_helper (when checking for executability) without having to
370 re-open it. Opening a file can take a lot of time on network drives
371 so we try to avoid that. */
372 oret = open_fs (open_flags, 0);
373 if (!oret)
374 {
375 query_open (query_read_attributes);
376 oret = open_fs (open_flags, 0);
377 }
378 if (oret)
379 {
380 /* We now have a valid handle, regardless of the "nohandle" state.
381 Since fhandler_base::close only calls CloseHandle if !nohandle,
382 we have to set it to false before calling close and restore
383 the state afterwards. */
384 res = pc.fs_is_nfs () ? fstat_by_nfs_ea (buf) : fstat_by_handle (buf);
385 bool no_handle = nohandle ();
386 nohandle (false);
387 close_fs ();
388 nohandle (no_handle);
389 set_handle (NULL);
390 }
391 if (res)
392 res = fstat_by_name (buf);
393
394 return res;
395 }
396
397 int
398 fhandler_base::fstat_helper (struct stat *buf)
399 {
400 IO_STATUS_BLOCK st;
401 FILE_COMPRESSION_INFORMATION fci;
402 HANDLE h = get_stat_handle ();
403 PFILE_ALL_INFORMATION pfai = pc.fai ();
404 ULONG attributes = pc.file_attributes ();
405
406 to_timestruc_t (&pfai->BasicInformation.LastAccessTime, &buf->st_atim);
407 to_timestruc_t (&pfai->BasicInformation.LastWriteTime, &buf->st_mtim);
408 /* If the ChangeTime is 0, the underlying FS doesn't support this timestamp
409 (FAT for instance). If so, it's faked using LastWriteTime. */
410 to_timestruc_t (pfai->BasicInformation.ChangeTime.QuadPart
411 ? &pfai->BasicInformation.ChangeTime
412 : &pfai->BasicInformation.LastWriteTime,
413 &buf->st_ctim);
414 to_timestruc_t (&pfai->BasicInformation.CreationTime, &buf->st_birthtim);
415 buf->st_dev = get_dev ();
416 /* CV 2011-01-13: Observations on the Cygwin mailing list point to an
417 interesting behaviour in some Windows versions. Apparently the size of
418 a directory is computed at the time the directory is first scanned. This
419 can result in two subsequent NtQueryInformationFile calls to return size
420 0 in the first call and size > 0 in the second call. This in turn can
421 affect applications like newer tar.
422 FIXME: Is the allocation size affected as well? */
423 buf->st_size = pc.isdir ()
424 ? 0
425 : (off_t) pfai->StandardInformation.EndOfFile.QuadPart;
426 /* The number of links to a directory includes the number of subdirectories
427 in the directory, since all those subdirectories point to it. However,
428 this is painfully slow, so we do without it. */
429 buf->st_nlink = pc.fai()->StandardInformation.NumberOfLinks;
430
431 /* Enforce namehash as inode number on untrusted file systems. */
432 buf->st_ino = ino ?: get_ino ();
433
434 buf->st_blksize = PREFERRED_IO_BLKSIZE;
435
436 if (buf->st_size == 0
437 && pfai->StandardInformation.AllocationSize.QuadPart == 0LL)
438 /* File is empty and no blocks are preallocated. */
439 buf->st_blocks = 0;
440 else if (pfai->StandardInformation.AllocationSize.QuadPart > 0LL)
441 /* A successful NtQueryInformationFile returns the allocation size
442 correctly for compressed and sparse files as well.
443 Allocation size 0 is ignored here because (at least) Windows 10
444 1607 always returns 0 for CompactOS compressed files. */
445 buf->st_blocks = (pfai->StandardInformation.AllocationSize.QuadPart
446 + S_BLKSIZE - 1) / S_BLKSIZE;
447 else if ((pfai->StandardInformation.AllocationSize.QuadPart == 0LL
448 || ::has_attribute (attributes, FILE_ATTRIBUTE_COMPRESSED
449 | FILE_ATTRIBUTE_SPARSE_FILE))
450 && h && !is_fs_special ()
451 && !NtQueryInformationFile (h, &st, (PVOID) &fci, sizeof fci,
452 FileCompressionInformation))
453 /* Otherwise we request the actual amount of bytes allocated for
454 compressed, sparsed and CompactOS files. */
455 buf->st_blocks = (fci.CompressedFileSize.QuadPart + S_BLKSIZE - 1)
456 / S_BLKSIZE;
457 else
458 /* Otherwise compute no. of blocks from file size. */
459 buf->st_blocks = (buf->st_size + S_BLKSIZE - 1) / S_BLKSIZE;
460
461 buf->st_mode = 0;
462 /* Using a side effect: get_file_attributes checks for directory.
463 This is used, to set S_ISVTX, if needed. */
464 if (pc.isdir ())
465 buf->st_mode = S_IFDIR;
466 else if (pc.issymlink ())
467 {
468 buf->st_size = pc.get_symlink_length ();
469 get_file_attribute (h, pc, buf->st_mode,
470 &buf->st_uid, &buf->st_gid);
471 /* symlinks are everything for everyone! */
472 buf->st_mode = S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO;
473 goto done;
474 }
475 else if (pc.issocket ())
476 buf->st_mode = S_IFSOCK;
477
478 if (!get_file_attribute (h, pc, buf->st_mode, &buf->st_uid, &buf->st_gid))
479 {
480 /* If read-only attribute is set, modify ntsec return value */
481 if (::has_attribute (attributes, FILE_ATTRIBUTE_READONLY)
482 && !pc.isdir () && !pc.issymlink () && !pc.is_fs_special ())
483 buf->st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
484
485 if (buf->st_mode & S_IFMT)
486 /* nothing */;
487 else if (!is_fs_special ())
488 buf->st_mode |= S_IFREG;
489 else
490 {
491 buf->st_dev = buf->st_rdev = dev ();
492 buf->st_mode = dev ().mode ();
493 buf->st_size = 0;
494 }
495 }
496 else
497 {
498 buf->st_mode |= STD_RBITS;
499
500 if (!::has_attribute (attributes, FILE_ATTRIBUTE_READONLY))
501 buf->st_mode |= STD_WBITS;
502 /* | S_IWGRP | S_IWOTH; we don't give write to group etc */
503
504 if (pc.isdir ())
505 buf->st_mode |= S_IFDIR | STD_WBITS | STD_XBITS;
506 else if (buf->st_mode & S_IFMT)
507 /* nothing */;
508 else if (is_fs_special ())
509 {
510 buf->st_dev = buf->st_rdev = dev ();
511 buf->st_mode = dev ().mode ();
512 buf->st_size = 0;
513 }
514 else
515 {
516 buf->st_mode |= S_IFREG;
517 /* Check suffix for executable file. */
518 if (pc.exec_state () != is_executable)
519 {
520 PUNICODE_STRING path = pc.get_nt_native_path ();
521
522 if (RtlEqualUnicodePathSuffix (path, &ro_u_exe, TRUE)
523 || RtlEqualUnicodePathSuffix (path, &ro_u_lnk, 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
592 fhandler_disk_file::fstat (struct stat *buf)
593 {
594 return fstat_fs (buf);
595 }
596
597 int
598 fhandler_disk_file::fstatvfs (struct statvfs *sfs)
599 {
600 int ret = -1, opened = 0;
601 IO_STATUS_BLOCK io;
602 /* We must not use the stat handle here, even if it exists. The handle
603 has been opened with FILE_OPEN_REPARSE_POINT, thus, in case of a volume
604 mount point, it points to the FS of the mount point, rather than to the
605 mounted FS. */
606 HANDLE fh = get_handle ();
607
608 if (!fh)
609 {
610 OBJECT_ATTRIBUTES attr;
611 opened = NT_SUCCESS (NtOpenFile (&fh, READ_CONTROL,
612 pc.get_object_attr (attr, sec_none_nih),
613 &io, FILE_SHARE_VALID_FLAGS,
614 FILE_OPEN_NO_RECALL
615 | FILE_OPEN_FOR_BACKUP_INTENT));
616 if (!opened)
617 {
618 /* Can't open file. Try again with parent dir. */
619 UNICODE_STRING dirname;
620 RtlSplitUnicodePath (pc.get_nt_native_path (), &dirname, NULL);
621 attr.ObjectName = &dirname;
622 opened = NT_SUCCESS (NtOpenFile (&fh, READ_CONTROL, &attr, &io,
623 FILE_SHARE_VALID_FLAGS,
624 FILE_OPEN_NO_RECALL
625 | FILE_OPEN_FOR_BACKUP_INTENT));
626 if (!opened)
627 goto out;
628 }
629 }
630
631 ret = fstatvfs_by_handle (fh, sfs);
632 out:
633 if (opened)
634 NtClose (fh);
635 syscall_printf ("%d = fstatvfs(%s, %p)", ret, get_name (), sfs);
636 return ret;
637 }
638
639 int
640 fhandler_base::fstatvfs_by_handle (HANDLE fh, struct statvfs *sfs)
641 {
642 int ret = -1;
643 NTSTATUS status;
644 IO_STATUS_BLOCK io;
645 FILE_FS_FULL_SIZE_INFORMATION full_fsi;
646
647 sfs->f_files = ULONG_MAX;
648 sfs->f_ffree = ULONG_MAX;
649 sfs->f_favail = ULONG_MAX;
650 sfs->f_fsid = pc.fs_serial_number ();
651 sfs->f_flag = pc.fs_flags ();
652 sfs->f_namemax = pc.fs_name_len ();
653 /* Get allocation related information. */
654 status = NtQueryVolumeInformationFile (fh, &io, &full_fsi, sizeof full_fsi,
655 FileFsFullSizeInformation);
656 if (NT_SUCCESS (status))
657 {
658 sfs->f_bsize = full_fsi.BytesPerSector * full_fsi.SectorsPerAllocationUnit;
659 sfs->f_frsize = sfs->f_bsize;
660 sfs->f_blocks = (fsblkcnt_t) full_fsi.TotalAllocationUnits.QuadPart;
661 sfs->f_bfree = (fsblkcnt_t)
662 full_fsi.ActualAvailableAllocationUnits.QuadPart;
663 sfs->f_bavail = (fsblkcnt_t)
664 full_fsi.CallerAvailableAllocationUnits.QuadPart;
665 if (sfs->f_bfree > sfs->f_bavail)
666 {
667 /* Quotas active. We can't trust TotalAllocationUnits. */
668 NTFS_VOLUME_DATA_BUFFER nvdb;
669
670 status = NtFsControlFile (fh, NULL, NULL, NULL, &io,
671 FSCTL_GET_NTFS_VOLUME_DATA,
672 NULL, 0, &nvdb, sizeof nvdb);
673 if (!NT_SUCCESS (status))
674 debug_printf ("%y = NtFsControlFile(%S, FSCTL_GET_NTFS_VOLUME_DATA)",
675 status, pc.get_nt_native_path ());
676 else
677 sfs->f_blocks = (fsblkcnt_t) nvdb.TotalClusters.QuadPart;
678 }
679 ret = 0;
680 }
681 else if (status == STATUS_INVALID_PARAMETER /* Netapp */
682 || status == STATUS_INVALID_INFO_CLASS)
683 {
684 FILE_FS_SIZE_INFORMATION fsi;
685 status = NtQueryVolumeInformationFile (fh, &io, &fsi, sizeof fsi,
686 FileFsSizeInformation);
687 if (NT_SUCCESS (status))
688 {
689 sfs->f_bsize = fsi.BytesPerSector * fsi.SectorsPerAllocationUnit;
690 sfs->f_frsize = sfs->f_bsize;
691 sfs->f_blocks = (fsblkcnt_t) fsi.TotalAllocationUnits.QuadPart;
692 sfs->f_bfree = sfs->f_bavail =
693 (fsblkcnt_t) fsi.AvailableAllocationUnits.QuadPart;
694 ret = 0;
695 }
696 else
697 debug_printf ("%y = NtQueryVolumeInformationFile"
698 "(%S, FileFsSizeInformation)",
699 status, pc.get_nt_native_path ());
700 }
701 else
702 debug_printf ("%y = NtQueryVolumeInformationFile"
703 "(%S, FileFsFullSizeInformation)",
704 status, pc.get_nt_native_path ());
705 return ret;
706 }
707
708 int
709 fhandler_disk_file::fchmod (mode_t mode)
710 {
711 int ret = -1;
712 int oret = 0;
713 NTSTATUS status;
714 IO_STATUS_BLOCK io;
715
716 if (pc.is_fs_special ()
717 /* For NFS, only handle Cygwin FIFOs specially. Changing mode of
718 native FIFOs will work with the default code below. */
719 && (!pc.fs_is_nfs () || pc.nfsattr ()->filler1 == NF3FIFO))
720 return chmod_device (pc, mode);
721
722 if (!get_handle ())
723 {
724 query_open (query_write_dac);
725 if (!(oret = open (O_BINARY, 0)))
726 {
727 /* Need WRITE_DAC to write ACLs. */
728 if (pc.has_acls ())
729 return -1;
730 /* Otherwise FILE_WRITE_ATTRIBUTES is sufficient. */
731 query_open (query_write_attributes);
732 if (!(oret = open (O_BINARY, 0)))
733 return -1;
734 }
735 }
736
737 if (pc.fs_is_nfs ())
738 {
739 /* chmod on NFS shares works by writing an EA of type NfsV3Attributes.
740 Only type and mode have to be set. Apparently type isn't checked
741 for consistency, so it's sufficent to set it to NF3REG all the time. */
742 struct {
743 FILE_FULL_EA_INFORMATION ffei;
744 char buf[sizeof (NFS_V3_ATTR) + sizeof (fattr3)];
745 } ffei_buf;
746 ffei_buf.ffei.NextEntryOffset = 0;
747 ffei_buf.ffei.Flags = 0;
748 ffei_buf.ffei.EaNameLength = sizeof (NFS_V3_ATTR) - 1;
749 ffei_buf.ffei.EaValueLength = sizeof (fattr3);
750 strcpy (ffei_buf.ffei.EaName, NFS_V3_ATTR);
751 fattr3 *nfs_attr = (fattr3 *) (ffei_buf.ffei.EaName
752 + ffei_buf.ffei.EaNameLength + 1);
753 memset (nfs_attr, 0, sizeof (fattr3));
754 nfs_attr->type = NF3REG;
755 nfs_attr->mode = mode;
756 status = NtSetEaFile (get_handle (), &io,
757 &ffei_buf.ffei, sizeof ffei_buf);
758 if (!NT_SUCCESS (status))
759 __seterrno_from_nt_status (status);
760 else
761 ret = 0;
762 goto out;
763 }
764
765 if (pc.has_acls ())
766 {
767 security_descriptor sd, sd_ret;
768 uid_t uid;
769 gid_t gid;
770 tmp_pathbuf tp;
771 aclent_t *aclp;
772 bool standard_acl = false;
773 int nentries, idx;
774 mode_t attr = pc.isdir () ? S_IFDIR : 0;
775
776 if (!get_file_sd (get_handle (), pc, sd, false))
777 {
778 aclp = (aclent_t *) tp.c_get ();
779 if ((nentries = get_posix_access (sd, attr, &uid, &gid,
780 aclp, MAX_ACL_ENTRIES,
781 &standard_acl)) >= 0)
782 {
783 /* Overwrite ACL permissions as required by POSIX 1003.1e
784 draft 17. */
785 aclp[0].a_perm = (mode >> 6) & S_IRWXO;
786
787 /* POSIXly correct: If CLASS_OBJ is present, chmod only modifies
788 CLASS_OBJ, not GROUP_OBJ.
789
790 Deliberate deviation from POSIX 1003.1e: If the ACL is a
791 "standard" ACL, that is, it only contains POSIX permissions
792 as well as entries for the Administrators group and SYSTEM,
793 then it's kind of a POSIX-only ACL in a twisted, Windowsy
794 way. If so, we change GROUP_OBJ and CLASS_OBJ perms. */
795 if (standard_acl
796 && (idx = searchace (aclp, nentries, GROUP_OBJ)) >= 0)
797 aclp[idx].a_perm = (mode >> 3) & S_IRWXO;
798 if (nentries > MIN_ACL_ENTRIES
799 && (idx = searchace (aclp, nentries, CLASS_OBJ)) >= 0)
800 aclp[idx].a_perm = (mode >> 3) & S_IRWXO;
801
802 if ((idx = searchace (aclp, nentries, OTHER_OBJ)) >= 0)
803 aclp[idx].a_perm = mode & S_IRWXO;
804 if (pc.isdir ())
805 mode |= S_IFDIR;
806 if (set_posix_access (mode, uid, gid, aclp, nentries, sd_ret,
807 pc.fs_is_samba ()))
808 ret = set_file_sd (get_handle (), pc, sd_ret, false);
809 }
810 }
811 }
812
813 /* If the mode has any write bits set, the DOS R/O flag is in the way. */
814 if (mode & (S_IWUSR | S_IWGRP | S_IWOTH))
815 pc &= (DWORD) ~FILE_ATTRIBUTE_READONLY;
816 else if (!pc.has_acls ()) /* Never set DOS R/O if security is used. */
817 pc |= (DWORD) FILE_ATTRIBUTE_READONLY;
818 if (S_ISSOCK (mode))
819 pc |= (DWORD) FILE_ATTRIBUTE_SYSTEM;
820
821 status = NtSetAttributesFile (get_handle (), pc.file_attributes ());
822 /* MVFS needs a good amount of kicking to be convinced that it has to write
823 back metadata changes and to invalidate the cached metadata information
824 stored for the given handle. This method to open a second handle to
825 the file and write the same metadata information twice has been found
826 experimentally: http://cygwin.com/ml/cygwin/2009-07/msg00533.html */
827 if (pc.fs_is_mvfs () && NT_SUCCESS (status) && !oret)
828 {
829 OBJECT_ATTRIBUTES attr;
830 HANDLE fh;
831
832 if (NT_SUCCESS (NtOpenFile (&fh, FILE_WRITE_ATTRIBUTES,
833 pc.init_reopen_attr (attr, get_handle ()),
834 &io, FILE_SHARE_VALID_FLAGS,
835 FILE_OPEN_FOR_BACKUP_INTENT)))
836 {
837 NtSetAttributesFile (fh, pc.file_attributes ());
838 NtClose (fh);
839 }
840 }
841 /* Correct NTFS security attributes have higher priority */
842 if (!pc.has_acls ())
843 {
844 if (!NT_SUCCESS (status))
845 __seterrno_from_nt_status (status);
846 else
847 ret = 0;
848 }
849
850 out:
851 if (oret)
852 close_fs ();
853
854 return ret;
855 }
856
857 int
858 fhandler_disk_file::fchown (uid_t uid, gid_t gid)
859 {
860 int oret = 0;
861 int ret = -1;
862 security_descriptor sd, sd_ret;
863 mode_t attr = pc.isdir () ? S_IFDIR : 0;
864 uid_t old_uid;
865 gid_t old_gid;
866 tmp_pathbuf tp;
867 aclent_t *aclp;
868 int nentries;
869
870 if (!pc.has_acls ())
871 {
872 /* fake - if not supported, pretend we're like win95
873 where it just works */
874 /* FIXME: Could be supported on NFS when user->uid mapping is in place. */
875 return 0;
876 }
877
878 if (!get_handle ())
879 {
880 query_open (query_write_control);
881 if (!(oret = fhandler_disk_file::open (O_BINARY, 0)))
882 return -1;
883 }
884
885 if (get_file_sd (get_handle (), pc, sd, false))
886 goto out;
887
888 aclp = (aclent_t *) tp.c_get ();
889 if ((nentries = get_posix_access (sd, attr, &old_uid, &old_gid,
890 aclp, MAX_ACL_ENTRIES)) < 0)
891 goto out;
892
893 /* According to POSIX, chown can be a no-op if uid is (uid_t)-1 and
894 gid is (gid_t)-1. Otherwise, even if uid and gid are unchanged,
895 we must ensure that ctime is updated. */
896 if (uid == ILLEGAL_UID && gid == ILLEGAL_GID)
897 {
898 ret = 0;
899 goto out;
900 }
901 if (uid == ILLEGAL_UID)
902 uid = old_uid;
903 else if (gid == ILLEGAL_GID)
904 gid = old_gid;
905
906 /* Windows ACLs can contain permissions for one group, while being owned by
907 another user/group. The permission bits returned above are pretty much
908 useless then. Creating a new ACL with these useless permissions results
909 in a potentially broken symlink. So what we do here is to set the
910 underlying permissions of symlinks to a sensible value which allows the
911 world to read the symlink and only the new owner to change it. */
912 if (pc.issymlink ())
913 for (int idx = 0; idx < nentries; ++idx)
914 {
915 aclp[idx].a_perm |= S_IROTH;
916 if (aclp[idx].a_type & USER_OBJ)
917 aclp[idx].a_perm |= S_IWOTH;
918 }
919
920 if (set_posix_access (attr, uid, gid, aclp, nentries, sd_ret,
921 pc.fs_is_samba ()))
922 ret = set_file_sd (get_handle (), pc, sd_ret, true);
923
924 /* If you're running a Samba server with no winbind, the uid<->SID mapping
925 is disfunctional. Even trying to chown to your own account fails since
926 the account used on the server is the UNIX account which gets used for
927 the standard user mapping. This is a default mechanism which doesn't
928 know your real Windows SID. There are two possible error codes in
929 different Samba releases for this situation, one of them unfortunately
930 the not very significant STATUS_ACCESS_DENIED. Instead of relying on
931 the error codes, we're using the below very simple heuristic.
932 If set_file_sd failed, and the original user account was either already
933 unknown, or one of the standard UNIX accounts, we're faking success. */
934 if (ret == -1 && pc.fs_is_samba ())
935 {
936 PSID sid;
937
938 if (uid == old_uid
939 || ((sid = sidfromuid (old_uid, NULL)) != NO_SID
940 && RtlEqualPrefixSid (sid,
941 well_known_samba_unix_user_fake_sid)))
942 {
943 debug_printf ("Faking chown worked on standalone Samba");
944 ret = 0;
945 }
946 }
947
948 out:
949 if (oret)
950 close_fs ();
951
952 return ret;
953 }
954
955 int
956 fhandler_disk_file::facl (int cmd, int nentries, aclent_t *aclbufp)
957 {
958 int res = -1;
959 int oret = 0;
960
961 if (!pc.has_acls ())
962 {
963 cant_access_acl:
964 switch (cmd)
965 {
966
967 case SETACL:
968 /* Open for writing required to be able to set ctime
969 (even though setting the ACL is just pretended). */
970 if (!get_handle ())
971 oret = open (O_WRONLY | O_BINARY, 0);
972 res = 0;
973 break;
974 case GETACL:
975 if (!aclbufp)
976 set_errno (EFAULT);
977 else if (nentries < MIN_ACL_ENTRIES)
978 set_errno (ENOSPC);
979 else
980 {
981 struct stat st;
982 if (!fstat (&st))
983 {
984 aclbufp[0].a_type = USER_OBJ;
985 aclbufp[0].a_id = st.st_uid;
986 aclbufp[0].a_perm = (st.st_mode & S_IRWXU) >> 6;
987 aclbufp[1].a_type = GROUP_OBJ;
988 aclbufp[1].a_id = st.st_gid;
989 aclbufp[1].a_perm = (st.st_mode & S_IRWXG) >> 3;
990 aclbufp[2].a_type = OTHER_OBJ;
991 aclbufp[2].a_id = ILLEGAL_GID;
992 aclbufp[2].a_perm = st.st_mode & S_IRWXO;
993 res = MIN_ACL_ENTRIES;
994 }
995 }
996 break;
997 case GETACLCNT:
998 res = MIN_ACL_ENTRIES;
999 break;
1000 default:
1001 set_errno (EINVAL);
1002 break;
1003 }
1004 }
1005 else
1006 {
1007 if ((cmd == SETACL && !get_handle ())
1008 || (cmd != SETACL && !get_stat_handle ()))
1009 {
1010 query_open (cmd == SETACL ? query_write_dac : query_read_control);
1011 if (!(oret = open (O_BINARY, 0)))
1012 {
1013 if (cmd == GETACL || cmd == GETACLCNT)
1014 goto cant_access_acl;
1015 return -1;
1016 }
1017 }
1018 switch (cmd)
1019 {
1020 case SETACL:
1021 if (!aclsort (nentries, 0, aclbufp))
1022 {
1023 bool rw = false;
1024 res = setacl (get_handle (), pc, nentries, aclbufp, rw);
1025 if (rw)
1026 {
1027 IO_STATUS_BLOCK io;
1028 FILE_BASIC_INFORMATION fbi;
1029 fbi.CreationTime.QuadPart
1030 = fbi.LastAccessTime.QuadPart
1031 = fbi.LastWriteTime.QuadPart
1032 = fbi.ChangeTime.QuadPart = 0LL;
1033 fbi.FileAttributes = (pc.file_attributes ()
1034 & ~FILE_ATTRIBUTE_READONLY)
1035 ?: FILE_ATTRIBUTE_NORMAL;
1036 NtSetInformationFile (get_handle (), &io, &fbi, sizeof fbi,
1037 FileBasicInformation);
1038 }
1039 }
1040 break;
1041 case GETACL:
1042 if (!aclbufp)
1043 set_errno(EFAULT);
1044 else {
1045 res = getacl (get_stat_handle (), pc, nentries, aclbufp);
1046 /* For this ENOSYS case, see security.cc:get_file_attribute(). */
1047 if (res == -1 && get_errno () == ENOSYS)
1048 goto cant_access_acl;
1049 }
1050 break;
1051 case GETACLCNT:
1052 res = getacl (get_stat_handle (), pc, 0, NULL);
1053 /* Ditto. */
1054 if (res == -1 && get_errno () == ENOSYS)
1055 goto cant_access_acl;
1056 break;
1057 default:
1058 set_errno (EINVAL);
1059 break;
1060 }
1061 }
1062
1063 if (oret)
1064 close_fs ();
1065
1066 return res;
1067 }
1068
1069 ssize_t
1070 fhandler_disk_file::fgetxattr (const char *name, void *value, size_t size)
1071 {
1072 if (pc.is_fs_special ())
1073 {
1074 set_errno (ENOTSUP);
1075 return -1;
1076 }
1077 return read_ea (get_handle (), pc, name, (char *) value, size);
1078 }
1079
1080 int
1081 fhandler_disk_file::fsetxattr (const char *name, const void *value, size_t size,
1082 int flags)
1083 {
1084 if (pc.is_fs_special ())
1085 {
1086 set_errno (ENOTSUP);
1087 return -1;
1088 }
1089 return write_ea (get_handle (), pc, name, (const char *) value, size, flags);
1090 }
1091
1092 int
1093 fhandler_disk_file::fadvise (off_t offset, off_t length, int advice)
1094 {
1095 if (advice < POSIX_FADV_NORMAL || advice > POSIX_FADV_NOREUSE)
1096 return EINVAL;
1097
1098 /* Windows only supports advice flags for the whole file. We're using
1099 a simplified test here so that we don't have to ask for the actual
1100 file size. Length == 0 means all bytes starting at offset anyway.
1101 So we only actually follow the advice, if it's given for offset == 0. */
1102 if (offset != 0)
1103 return 0;
1104
1105 /* We only support normal and sequential mode for now. Everything which
1106 is not POSIX_FADV_SEQUENTIAL is treated like POSIX_FADV_NORMAL. */
1107 if (advice != POSIX_FADV_SEQUENTIAL)
1108 advice = POSIX_FADV_NORMAL;
1109
1110 IO_STATUS_BLOCK io;
1111 FILE_MODE_INFORMATION fmi;
1112 NTSTATUS status = NtQueryInformationFile (get_handle (), &io,
1113 &fmi, sizeof fmi,
1114 FileModeInformation);
1115 if (NT_SUCCESS (status))
1116 {
1117 fmi.Mode &= ~FILE_SEQUENTIAL_ONLY;
1118 if (advice == POSIX_FADV_SEQUENTIAL)
1119 fmi.Mode |= FILE_SEQUENTIAL_ONLY;
1120 status = NtSetInformationFile (get_handle (), &io, &fmi, sizeof fmi,
1121 FileModeInformation);
1122 if (NT_SUCCESS (status))
1123 return 0;
1124 __seterrno_from_nt_status (status);
1125 }
1126
1127 return geterrno_from_nt_status (status);
1128 }
1129
1130 int
1131 fhandler_disk_file::falloc_allocate (int mode, off_t offset, off_t length)
1132 {
1133 NTSTATUS status;
1134 IO_STATUS_BLOCK io;
1135 FILE_STANDARD_INFORMATION fsi;
1136 FILE_END_OF_FILE_INFORMATION feofi;
1137 FILE_ALLOCATION_INFORMATION fai = { 0 };
1138
1139 /* Fetch EOF */
1140 status = NtQueryInformationFile (get_handle (), &io, &fsi, sizeof fsi,
1141 FileStandardInformation);
1142 if (!NT_SUCCESS (status))
1143 return geterrno_from_nt_status (status);
1144
1145 switch (mode)
1146 {
1147 case 0:
1148 /* For posix_fallocate(3), truncating the file is a no-op. However,
1149 for sparse files we still have to allocate the blocks within
1150 offset and offset + length which are currently in holes, due to
1151 the following POSIX requirement:
1152 "If posix_fallocate() returns successfully, subsequent writes to
1153 the specified file data shall not fail due to the lack of free
1154 space on the file system storage media." */
1155 if (offset + length <= fsi.EndOfFile.QuadPart)
1156 {
1157 if (!has_attribute (FILE_ATTRIBUTE_SPARSE_FILE))
1158 return 0;
1159 feofi.EndOfFile.QuadPart = fsi.EndOfFile.QuadPart;
1160 }
1161 else
1162 feofi.EndOfFile.QuadPart = offset + length;
1163 break;
1164 case __FALLOC_FL_TRUNCATE:
1165 /* For ftruncate(2), offset is 0. Just use length as is. */
1166 feofi.EndOfFile.QuadPart = length;
1167
1168 /* Make file sparse only when called through ftruncate and the mount
1169 mode supports sparse files. Also, make sure that the new region
1170 actually spans over at least one sparsifiable chunk. */
1171 if (pc.support_sparse ()
1172 && !has_attribute (FILE_ATTRIBUTE_SPARSE_FILE)
1173 && span_sparse_chunk (feofi.EndOfFile.QuadPart,
1174 fsi.EndOfFile.QuadPart))
1175 {
1176 status = NtFsControlFile (get_handle (), NULL, NULL, NULL, &io,
1177 FSCTL_SET_SPARSE, NULL, 0, NULL, 0);
1178 if (NT_SUCCESS (status))
1179 pc.file_attributes (pc.file_attributes ()
1180 | FILE_ATTRIBUTE_SPARSE_FILE);
1181 debug_printf ("%y = NtFsControlFile(%S, FSCTL_SET_SPARSE)",
1182 status, pc.get_nt_native_path ());
1183 }
1184 break;
1185 case FALLOC_FL_KEEP_SIZE:
1186 /* Keep track of the allocation size for overallocation below.
1187 Note that overallocation in Windows is only temporary!
1188 As soon as the last open handle to the file is closed, the
1189 overallocation gets removed by the system. Also, overallocation
1190 for sparse files fails silently, so just don't bother. */
1191 if (offset + length > fsi.EndOfFile.QuadPart
1192 && !has_attribute (FILE_ATTRIBUTE_SPARSE_FILE))
1193 fai.AllocationSize.QuadPart = offset + length;
1194
1195 feofi.EndOfFile.QuadPart = fsi.EndOfFile.QuadPart;
1196 break;
1197 }
1198
1199 /* Now set the new EOF */
1200 if (feofi.EndOfFile.QuadPart != fsi.EndOfFile.QuadPart)
1201 {
1202 status = NtSetInformationFile (get_handle (), &io,
1203 &feofi, sizeof feofi,
1204 FileEndOfFileInformation);
1205 if (!NT_SUCCESS (status))
1206 return geterrno_from_nt_status (status);
1207 }
1208
1209 /* If called via fallocate(2) or posix_fallocate(3), allocate blocks in
1210 sparse file holes. */
1211 if (mode != __FALLOC_FL_TRUNCATE
1212 && length
1213 && has_attribute (FILE_ATTRIBUTE_SPARSE_FILE))
1214 {
1215 int res = falloc_zero_range (mode | __FALLOC_FL_ZERO_HOLES,
1216 offset, length);
1217 if (res)
1218 return res;
1219 }
1220
1221 /* Last but not least, set the new allocation size, if any */
1222 if (fai.AllocationSize.QuadPart)
1223 {
1224 /* This is not fatal. Just note a failure in the debug output. */
1225 status = NtSetInformationFile (get_handle (), &io,
1226 &fai, sizeof fai,
1227 FileAllocationInformation);
1228 if (!NT_SUCCESS (status))
1229 debug_printf ("%y = NtSetInformationFile(%S, "
1230 "FileAllocationInformation)",
1231 status, pc.get_nt_native_path ());
1232 }
1233
1234 return 0;
1235 }
1236
1237 int
1238 fhandler_disk_file::falloc_punch_hole (off_t offset, off_t length)
1239 {
1240 NTSTATUS status;
1241 IO_STATUS_BLOCK io;
1242 FILE_STANDARD_INFORMATION fsi;
1243 FILE_ZERO_DATA_INFORMATION fzi;
1244
1245 /* Fetch EOF */
1246 status = NtQueryInformationFile (get_handle (), &io, &fsi, sizeof fsi,
1247 FileStandardInformation);
1248 if (!NT_SUCCESS (status))
1249 return geterrno_from_nt_status (status);
1250
1251 if (offset > fsi.EndOfFile.QuadPart) /* no-op */
1252 return 0;
1253
1254 if (offset + length > fsi.EndOfFile.QuadPart)
1255 length = fsi.EndOfFile.QuadPart - offset;
1256
1257 /* If the file isn't sparse yet, make it so. */
1258 if (!has_attribute (FILE_ATTRIBUTE_SPARSE_FILE))
1259 {
1260 status = NtFsControlFile (get_handle (), NULL, NULL, NULL, &io,
1261 FSCTL_SET_SPARSE, NULL, 0, NULL, 0);
1262 debug_printf ("%y = NtFsControlFile(%S, FSCTL_SET_SPARSE)",
1263 status, pc.get_nt_native_path ());
1264 if (!NT_SUCCESS (status))
1265 return geterrno_from_nt_status (status);
1266 pc.file_attributes (pc.file_attributes () | FILE_ATTRIBUTE_SPARSE_FILE);
1267 }
1268
1269 /* Now punch a hole. For once, FSCTL_SET_ZERO_DATA does it exactly as per
1270 fallocate(FALLOC_FL_PUNCH_HOLE) specs. */
1271 fzi.FileOffset.QuadPart = offset;
1272 fzi.BeyondFinalZero.QuadPart = offset + length;
1273 status = NtFsControlFile (get_handle (), NULL, NULL, NULL, &io,
1274 FSCTL_SET_ZERO_DATA, &fzi, sizeof fzi, NULL, 0);
1275 if (!NT_SUCCESS (status))
1276 return geterrno_from_nt_status (status);
1277
1278 return 0;
1279 }
1280
1281 int
1282 fhandler_disk_file::falloc_zero_range (int mode, off_t offset, off_t length)
1283 {
1284 NTSTATUS status;
1285 IO_STATUS_BLOCK io;
1286 FILE_STANDARD_INFORMATION fsi;
1287 FILE_ALLOCATED_RANGE_BUFFER inp, *out = NULL;
1288 OBJECT_ATTRIBUTES attr;
1289 HANDLE zo_handle;
1290 tmp_pathbuf tp;
1291 size_t data_chunk_count = 0;
1292
1293 /* Fetch EOF */
1294 status = NtQueryInformationFile (get_handle (), &io, &fsi, sizeof fsi,
1295 FileStandardInformation);
1296 if (!NT_SUCCESS (status))
1297 return geterrno_from_nt_status (status);
1298
1299 /* offset and length must not exceed EOF with FALLOC_FL_KEEP_SIZE */
1300 if (mode & FALLOC_FL_KEEP_SIZE)
1301 {
1302 if (offset > fsi.EndOfFile.QuadPart) /* no-op */
1303 return 0;
1304
1305 if (offset + length > fsi.EndOfFile.QuadPart)
1306 length = fsi.EndOfFile.QuadPart - offset;
1307 }
1308
1309 /* If the file is sparse, fetch the data ranges within the file
1310 to be able to recognize holes. */
1311 if (has_attribute (FILE_ATTRIBUTE_SPARSE_FILE))
1312 {
1313 inp.FileOffset.QuadPart = offset;
1314 inp.Length.QuadPart = length;
1315 out = (FILE_ALLOCATED_RANGE_BUFFER *) tp.t_get ();
1316 status = NtFsControlFile (get_handle (), NULL, NULL, NULL,
1317 &io, FSCTL_QUERY_ALLOCATED_RANGES,
1318 &inp, sizeof inp, out, 2 * NT_MAX_PATH);
1319 if (!NT_ERROR (status))
1320 data_chunk_count = io.Information / sizeof *out;
1321 }
1322
1323 /* Re-open the file and use this handle ever after, so as not to
1324 move the file pointer of the original file object. */
1325 status = NtOpenFile (&zo_handle, SYNCHRONIZE | GENERIC_WRITE,
1326 pc.init_reopen_attr (attr, get_handle ()), &io,
1327 FILE_SHARE_VALID_FLAGS, get_options ());
1328 if (!NT_SUCCESS (status))
1329 return geterrno_from_nt_status (status);
1330
1331 /* FILE_SPARSE_GRANULARITY == 2 * NT_MAX_PATH ==> fits exactly */
1332 char *nullbuf = tp.t_get ();
1333 memset (nullbuf, 0, FILE_SPARSE_GRANULARITY);
1334 int res = 0;
1335
1336 /* Split range into chunks of size FILE_SPARSE_GRANULARITY and handle
1337 them according to being data or hole */
1338 LARGE_INTEGER off = { QuadPart:offset };
1339 size_t start_idx = 0;
1340 while (length > 0)
1341 {
1342 off_t chunk_len;
1343 bool in_data = true;
1344
1345 if (off.QuadPart % FILE_SPARSE_GRANULARITY) /* First block */
1346 chunk_len = roundup2 (off.QuadPart, FILE_SPARSE_GRANULARITY) - off.QuadPart;
1347 else
1348 chunk_len = FILE_SPARSE_GRANULARITY;
1349 if (chunk_len > length) /* First or last block */
1350 chunk_len = length;
1351
1352 /* Check if the current chunk is within data or hole */
1353 if (has_attribute (FILE_ATTRIBUTE_SPARSE_FILE)
1354 && off.QuadPart < fsi.EndOfFile.QuadPart)
1355 {
1356 in_data = false;
1357 for (size_t idx = start_idx; idx < data_chunk_count; ++idx)
1358 if (off.QuadPart >= out[idx].FileOffset.QuadPart)
1359 {
1360 /* Skip entries with lower start address next time. */
1361 start_idx = idx;
1362 if (off.QuadPart < out[idx].FileOffset.QuadPart
1363 + out[idx].Length.QuadPart)
1364 {
1365 in_data = true;
1366 break;
1367 }
1368 }
1369 }
1370
1371 /* Eventually, write zeros into the block. Completely zero out data
1372 blocks, just write a single zero to former holes in sparse files.
1373 If __FALLOC_FL_ZERO_HOLES has been specified, only write to holes. */
1374 if (!(mode & __FALLOC_FL_ZERO_HOLES) || !in_data)
1375 {
1376 status = NtWriteFile (zo_handle, NULL, NULL, NULL, &io, nullbuf,
1377 in_data ? chunk_len : 1, &off, NULL);
1378 if (!NT_SUCCESS (status))
1379 {
1380 res = geterrno_from_nt_status (status);
1381 break;
1382 }
1383 }
1384
1385 off.QuadPart += chunk_len;
1386 length -= chunk_len;
1387 }
1388
1389 NtClose (zo_handle);
1390 return res;
1391 }
1392
1393 int
1394 fhandler_disk_file::fallocate (int mode, off_t offset, off_t length)
1395 {
1396 if (length < 0 || !get_handle ())
1397 return EINVAL;
1398 if (pc.isdir ())
1399 return EISDIR;
1400 if (!(get_access () & GENERIC_WRITE))
1401 return EBADF;
1402
1403 switch (mode)
1404 {
1405 case 0:
1406 case __FALLOC_FL_TRUNCATE:
1407 case FALLOC_FL_KEEP_SIZE:
1408 return falloc_allocate (mode, offset, length);
1409 case FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE:
1410 /* Only if the filesystem supports it... */
1411 if (!(pc.fs_flags () & FILE_SUPPORTS_SPARSE_FILES))
1412 return EOPNOTSUPP;
1413 return falloc_punch_hole (offset, length);
1414 case FALLOC_FL_ZERO_RANGE:
1415 case FALLOC_FL_ZERO_RANGE | FALLOC_FL_KEEP_SIZE:
1416 return falloc_zero_range (mode, offset, length);
1417 default:
1418 break;
1419 }
1420 return EINVAL;
1421 }
1422
1423 int
1424 fhandler_disk_file::link (const char *newpath)
1425 {
1426 size_t nlen = strlen (newpath);
1427 path_conv newpc (newpath, PC_SYM_NOFOLLOW | PC_POSIX | PC_NULLEMPTY, stat_suffixes);
1428 if (newpc.error)
1429 {
1430 set_errno (newpc.error);
1431 return -1;
1432 }
1433
1434 if (newpc.exists ())
1435 {
1436 syscall_printf ("file '%S' exists?", newpc.get_nt_native_path ());
1437 set_errno (EEXIST);
1438 return -1;
1439 }
1440
1441 if (isdirsep (newpath[nlen - 1]) || has_dot_last_component (newpath, false))
1442 {
1443 set_errno (ENOENT);
1444 return -1;
1445 }
1446
1447 char new_buf[nlen + 5];
1448 if (!newpc.error)
1449 {
1450 /* If the original file is a lnk special file,
1451 and if the original file has a .lnk suffix, add one to the hardlink
1452 as well. */
1453 if (pc.is_lnk_special ()
1454 && RtlEqualUnicodePathSuffix (pc.get_nt_native_path (),
1455 &ro_u_lnk, TRUE))
1456 {
1457 /* Shortcut hack. */
1458 stpcpy (stpcpy (new_buf, newpath), ".lnk");
1459 newpath = new_buf;
1460 newpc.check (newpath, PC_SYM_NOFOLLOW);
1461 }
1462 else if (!pc.isdir ()
1463 && pc.is_binary ()
1464 && RtlEqualUnicodePathSuffix (pc.get_nt_native_path (),
1465 &ro_u_exe, TRUE)
1466 && !RtlEqualUnicodePathSuffix (newpc.get_nt_native_path (),
1467 &ro_u_exe, TRUE))
1468 {
1469 /* Executable hack. */
1470 stpcpy (stpcpy (new_buf, newpath), ".exe");
1471 newpath = new_buf;
1472 newpc.check (newpath, PC_SYM_NOFOLLOW);
1473 }
1474 }
1475
1476 /* We only need READ_CONTROL access so the handle returned in pc is
1477 sufficient. And if the file couldn't be opened with READ_CONTROL
1478 access in path_conv, we won't be able to do it here anyway. */
1479 HANDLE fh = get_stat_handle ();
1480 if (!fh)
1481 {
1482 set_errno (EACCES);
1483 return -1;
1484 }
1485 PUNICODE_STRING tgt = newpc.get_nt_native_path ();
1486 ULONG size = sizeof (FILE_LINK_INFORMATION) + tgt->Length;
1487 PFILE_LINK_INFORMATION pfli = (PFILE_LINK_INFORMATION) alloca (size);
1488 pfli->ReplaceIfExists = FALSE;
1489 pfli->RootDirectory = NULL;
1490 memcpy (pfli->FileName, tgt->Buffer, pfli->FileNameLength = tgt->Length);
1491
1492 NTSTATUS status;
1493 IO_STATUS_BLOCK io;
1494 status = NtSetInformationFile (fh, &io, pfli, size, FileLinkInformation);
1495 if (!NT_SUCCESS (status))
1496 {
1497 if (status == STATUS_INVALID_DEVICE_REQUEST
1498 || status == STATUS_NOT_SUPPORTED)
1499 /* FS doesn't support hard links. Linux returns EPERM. */
1500 set_errno (EPERM);
1501 else
1502 __seterrno_from_nt_status (status);
1503 return -1;
1504 }
1505 else if ((pc.file_attributes () & O_TMPFILE_FILE_ATTRS)
1506 == O_TMPFILE_FILE_ATTRS)
1507 {
1508 /* An O_TMPFILE file has FILE_ATTRIBUTE_TEMPORARY and
1509 FILE_ATTRIBUTE_HIDDEN set. After a successful hardlink the file is
1510 not temporary anymore in the usual sense. So we remove these
1511 attributes here.
1512
1513 Note that we don't create a reopen attribute for the original
1514 link but rather a normal attribute for the just created link.
1515 The reason is a curious behaviour of Windows: If we remove the
1516 O_TMPFILE attributes on the original link, the new link will not
1517 show up in file system listings (not even native ones from , e.g.,
1518 `cmd /c dir'), long after the original link has been closed and
1519 removed. The file and its metadata will be kept in memory only
1520 as long as Windows sees fit. By opening the new link, we request
1521 the attribute changes on the new link, so after closing it Windows
1522 will have an increased interest to write back the metadata. */
1523 OBJECT_ATTRIBUTES attr;
1524 status = NtOpenFile (&fh, FILE_WRITE_ATTRIBUTES,
1525 newpc.get_object_attr (attr, sec_none_nih), &io,
1526 FILE_SHARE_VALID_FLAGS, FILE_OPEN_FOR_BACKUP_INTENT);
1527 if (!NT_SUCCESS (status))
1528 debug_printf ("Opening for removing TEMPORARY attrib failed, "
1529 "status = %y", status);
1530 else
1531 {
1532 FILE_BASIC_INFORMATION fbi;
1533
1534 fbi.CreationTime.QuadPart = fbi.LastAccessTime.QuadPart
1535 = fbi.LastWriteTime.QuadPart = fbi.ChangeTime.QuadPart = 0LL;
1536 fbi.FileAttributes = (pc.file_attributes () & ~O_TMPFILE_FILE_ATTRS)
1537 ?: FILE_ATTRIBUTE_NORMAL;
1538 status = NtSetInformationFile (fh, &io, &fbi, sizeof fbi,
1539 FileBasicInformation);
1540 if (!NT_SUCCESS (status))
1541 debug_printf ("Removing the TEMPORARY attrib failed, status = %y",
1542 status);
1543 NtClose (fh);
1544 }
1545 }
1546 return 0;
1547 }
1548
1549 int
1550 fhandler_disk_file::utimens (const struct timespec *tvp)
1551 {
1552 return utimens_fs (tvp);
1553 }
1554
1555 int
1556 fhandler_base::utimens_fs (const struct timespec *tvp)
1557 {
1558 struct timespec timeofday;
1559 struct timespec tmp[2];
1560 bool closeit = false;
1561
1562 if (!get_handle ())
1563 {
1564 query_open (query_write_attributes);
1565 if (!open_fs (O_BINARY, 0))
1566 {
1567 /* It's documented in MSDN that FILE_WRITE_ATTRIBUTES is sufficient
1568 to change the timestamps. Unfortunately it's not sufficient for a
1569 remote HPFS which requires GENERIC_WRITE, so we just retry to open
1570 for writing, though this fails for R/O files of course. */
1571 query_open (no_query);
1572 if (!open_fs (O_WRONLY | O_BINARY, 0))
1573 {
1574 syscall_printf ("Opening file failed");
1575 return -1;
1576 }
1577 }
1578 closeit = true;
1579 }
1580
1581 clock_gettime (CLOCK_REALTIME, &timeofday);
1582 if (!tvp)
1583 tmp[1] = tmp[0] = timeofday;
1584 else
1585 {
1586 if ((tvp[0].tv_nsec < UTIME_NOW || tvp[0].tv_nsec >= NSPERSEC)
1587 || (tvp[1].tv_nsec < UTIME_NOW || tvp[1].tv_nsec >= NSPERSEC))
1588 {
1589 if (closeit)
1590 close_fs ();
1591 set_errno (EINVAL);
1592 return -1;
1593 }
1594 tmp[0] = (tvp[0].tv_nsec == UTIME_NOW) ? timeofday : tvp[0];
1595 tmp[1] = (tvp[1].tv_nsec == UTIME_NOW) ? timeofday : tvp[1];
1596 }
1597 debug_printf ("incoming lastaccess %ly %ly", tmp[0].tv_sec, tmp[0].tv_nsec);
1598
1599 IO_STATUS_BLOCK io;
1600 FILE_BASIC_INFORMATION fbi;
1601
1602 fbi.CreationTime.QuadPart = 0LL;
1603 /* UTIME_OMIT is handled in timespec_to_filetime by setting FILETIME to 0. */
1604 timespec_to_filetime (&tmp[0], &fbi.LastAccessTime);
1605 timespec_to_filetime (&tmp[1], &fbi.LastWriteTime);
1606 fbi.ChangeTime.QuadPart = 0LL;
1607 fbi.FileAttributes = 0;
1608 NTSTATUS status = NtSetInformationFile (get_handle (), &io, &fbi, sizeof fbi,
1609 FileBasicInformation);
1610 /* For this special case for MVFS see the comment in
1611 fhandler_disk_file::fchmod. */
1612 if (pc.fs_is_mvfs () && NT_SUCCESS (status) && !closeit)
1613 {
1614 OBJECT_ATTRIBUTES attr;
1615 HANDLE fh;
1616
1617 if (NT_SUCCESS (NtOpenFile (&fh, FILE_WRITE_ATTRIBUTES,
1618 pc.init_reopen_attr (attr, get_handle ()),
1619 &io, FILE_SHARE_VALID_FLAGS,
1620 FILE_OPEN_FOR_BACKUP_INTENT)))
1621 {
1622 NtSetInformationFile (fh, &io, &fbi, sizeof fbi,
1623 FileBasicInformation);
1624 NtClose (fh);
1625 }
1626 }
1627 if (closeit)
1628 close_fs ();
1629 /* Opening a directory on a 9x share from a NT machine works(!), but
1630 then NtSetInformationFile fails with STATUS_NOT_SUPPORTED. Oh well... */
1631 if (!NT_SUCCESS (status) && status != STATUS_NOT_SUPPORTED)
1632 {
1633 __seterrno_from_nt_status (status);
1634 return -1;
1635 }
1636 return 0;
1637 }
1638
1639 fhandler_disk_file::fhandler_disk_file () :
1640 fhandler_base (), prw_handle (NULL)
1641 {
1642 }
1643
1644 fhandler_disk_file::fhandler_disk_file (path_conv &pc) :
1645 fhandler_base (), prw_handle (NULL)
1646 {
1647 set_name (pc);
1648 }
1649
1650 int
1651 fhandler_disk_file::open (int flags, mode_t mode)
1652 {
1653 return open_fs (flags, mode);
1654 }
1655
1656 int
1657 fhandler_disk_file::close ()
1658 {
1659 /* Close extra pread/pwrite handle, if it exists. */
1660 if (prw_handle)
1661 NtClose (prw_handle);
1662 return fhandler_base::close ();
1663 }
1664
1665 int
1666 fhandler_disk_file::fcntl (int cmd, intptr_t arg)
1667 {
1668 int res;
1669
1670 switch (cmd)
1671 {
1672 case F_LCK_MANDATORY: /* Mandatory locking only works on files. */
1673 mandatory_locking (!!arg);
1674 need_fork_fixup (true);
1675 res = 0;
1676 break;
1677 default:
1678 res = fhandler_base::fcntl (cmd, arg);
1679 break;
1680 }
1681 return res;
1682 }
1683
1684 int
1685 fhandler_disk_file::dup (fhandler_base *child, int flags)
1686 {
1687 fhandler_disk_file *fhc = (fhandler_disk_file *) child;
1688
1689 int ret = fhandler_base::dup (child, flags);
1690 if (!ret && prw_handle
1691 && !DuplicateHandle (GetCurrentProcess (), prw_handle,
1692 GetCurrentProcess (), &fhc->prw_handle,
1693 0, FALSE, DUPLICATE_SAME_ACCESS))
1694 fhc->prw_handle = NULL;
1695 return ret;
1696 }
1697
1698 void
1699 fhandler_disk_file::fixup_after_fork (HANDLE parent)
1700 {
1701 prw_handle = NULL;
1702 mandatory_locking (false);
1703 fhandler_base::fixup_after_fork (parent);
1704 }
1705
1706 int
1707 fhandler_base::open_fs (int flags, mode_t mode)
1708 {
1709 /* Unfortunately NT allows to open directories for writing, but that's
1710 disallowed according to SUSv3. */
1711 if (pc.isdir () && (flags & O_ACCMODE) != O_RDONLY)
1712 {
1713 set_errno (EISDIR);
1714 return 0;
1715 }
1716
1717 bool new_file = !exists ();
1718
1719 int res = fhandler_base::open (flags, mode);
1720 if (res)
1721 {
1722 /* The file info in pc is wrong at this point for newly created files.
1723 Refresh it before fetching any file info. */
1724 if (new_file)
1725 pc.get_finfo (get_handle ());
1726
1727 if (pc.isgood_inode (pc.get_ino ()))
1728 ino = pc.get_ino ();
1729 }
1730
1731 syscall_printf ("%d = fhandler_disk_file::open(%S, %y)", res,
1732 pc.get_nt_native_path (), flags);
1733 return res;
1734 }
1735
1736 /* POSIX demands that pread/pwrite don't change the current file position.
1737 While NtReadFile/NtWriteFile support atomic seek-and-io, both change the
1738 file pointer if the file handle has been opened for synchonous I/O.
1739 Using this handle for pread/pwrite would break atomicity, because the
1740 read/write operation would have to be followed by a seek back to the old
1741 file position. What we do is to open another handle to the file on the
1742 first call to either pread or pwrite. This is used for any subsequent
1743 pread/pwrite. Thus the current file position of the "normal" file
1744 handle is not touched.
1745
1746 FIXME:
1747
1748 Note that this is just a hack. The problem with this approach is that
1749 a change to the file permissions might disallow to open the file with
1750 the required permissions to read or write. This appears to be a border case,
1751 but that's exactly what git does. It creates the file for reading and
1752 writing and after writing it, it chmods the file to read-only. Then it
1753 calls pread on the file to examine the content. This works, but if git
1754 would use the original handle to pwrite to the file, it would be broken
1755 with our approach.
1756
1757 One way to implement this is to open the pread/pwrite handle right at
1758 file open time. We would simply maintain two handles, which wouldn't
1759 be much of a problem given how we do that for other fhandler types as
1760 well.
1761
1762 However, ultimately fhandler_disk_file should become a derived class of
1763 fhandler_base_overlapped. Each raw_read or raw_write would fetch the
1764 actual file position, read/write from there, and then set the file
1765 position again. Fortunately, while the file position is not maintained
1766 by the I/O manager, it can be fetched and set to a new value by all
1767 processes holding a handle to that file object. Pread and pwrite differ
1768 from raw_read and raw_write just by not touching the current file pos.
1769 Actually they could be merged with raw_read/raw_write if we add a position
1770 parameter to the latter. */
1771
1772 int
1773 fhandler_disk_file::prw_open (bool write, void *aio)
1774 {
1775 NTSTATUS status;
1776 IO_STATUS_BLOCK io;
1777 OBJECT_ATTRIBUTES attr;
1778 ULONG options = get_options ();
1779
1780 /* If async i/o is intended, turn off the default synchronous operation */
1781 if (aio)
1782 options &= ~FILE_SYNCHRONOUS_IO_NONALERT;
1783
1784 /* First try to open with the original access mask */
1785 ACCESS_MASK access = get_access ();
1786 status = NtOpenFile (&prw_handle, access,
1787 pc.init_reopen_attr (attr, get_handle ()), &io,
1788 FILE_SHARE_VALID_FLAGS, options);
1789 if (status == STATUS_ACCESS_DENIED)
1790 {
1791 /* If we get an access denied, chmod has been called. Try again
1792 with just the required rights to perform the called function. */
1793 access &= write ? ~GENERIC_READ : ~GENERIC_WRITE;
1794 status = NtOpenFile (&prw_handle, access, &attr, &io,
1795 FILE_SHARE_VALID_FLAGS, options);
1796 }
1797 debug_printf ("%y = NtOpenFile (%p, %y, %S, io, %y, %y)",
1798 status, prw_handle, access, pc.get_nt_native_path (),
1799 FILE_SHARE_VALID_FLAGS, options);
1800 if (!NT_SUCCESS (status))
1801 {
1802 __seterrno_from_nt_status (status);
1803 return -1;
1804 }
1805
1806 /* record prw_handle's asyncness for subsequent pread/pwrite operations */
1807 prw_handle_isasync = !!aio;
1808 return 0;
1809 }
1810
1811 ssize_t
1812 fhandler_disk_file::pread (void *buf, size_t count, off_t offset, void *aio)
1813 {
1814 struct aiocb *aiocb = (struct aiocb *) aio;
1815 ssize_t res;
1816
1817 if ((get_flags () & O_ACCMODE) == O_WRONLY)
1818 {
1819 set_errno (EBADF);
1820 return -1;
1821 }
1822
1823 /* In binary mode, we can use an atomic NtReadFile call.
1824 Windows mandatory locking semantics disallow to use another HANDLE. */
1825 if (rbinary () && !mandatory_locking ())
1826 {
1827 extern int is_at_eof (HANDLE h);
1828 NTSTATUS status;
1829 IO_STATUS_BLOCK io;
1830 LARGE_INTEGER off = { QuadPart:offset };
1831 HANDLE evt = aio ? (HANDLE) aiocb->aio_wincb.event : NULL;
1832 PIO_STATUS_BLOCK pio = aio ? (PIO_STATUS_BLOCK) &aiocb->aio_wincb : &io;
1833
1834 /* If existing prw_handle asyncness doesn't match this call's, re-open */
1835 if (prw_handle && (prw_handle_isasync != !!aio))
1836 NtClose (prw_handle), prw_handle = NULL;
1837
1838 if (!prw_handle && prw_open (false, aio))
1839 goto non_atomic;
1840 status = NtReadFile (prw_handle, evt, NULL, NULL, pio, buf, count,
1841 &off, NULL);
1842 if (status == STATUS_END_OF_FILE)
1843 res = 0;
1844 else if (!NT_SUCCESS (status))
1845 {
1846 if (pc.isdir ())
1847 {
1848 set_errno (EISDIR);
1849 return -1;
1850 }
1851 if (status == (NTSTATUS) STATUS_ACCESS_VIOLATION)
1852 {
1853 if (is_at_eof (prw_handle))
1854 {
1855 res = 0;
1856 goto out;
1857 }
1858 switch (mmap_is_attached_or_noreserve (buf, count))
1859 {
1860 case MMAP_NORESERVE_COMMITED:
1861 status = NtReadFile (prw_handle, evt, NULL, NULL, pio,
1862 buf, count, &off, NULL);
1863 if (NT_SUCCESS (status))
1864 {
1865 res = aio ? (ssize_t) aiocb->aio_wincb.info
1866 : io.Information;
1867 goto out;
1868 }
1869 break;
1870 case MMAP_RAISE_SIGBUS:
1871 raise (SIGBUS);
1872 default:
1873 break;
1874 }
1875 }
1876 __seterrno_from_nt_status (status);
1877 return -1;
1878 }
1879 else
1880 {
1881 res = aio ? (ssize_t) aiocb->aio_wincb.info : io.Information;
1882 goto out;
1883 }
1884 }
1885 else
1886 {
1887 non_atomic:
1888 /* Text mode stays slow and non-atomic. */
1889 off_t curpos = lseek (0, SEEK_CUR);
1890 if (curpos < 0 || lseek (offset, SEEK_SET) < 0)
1891 res = -1;
1892 else
1893 {
1894 size_t tmp_count = count;
1895 read (buf, tmp_count);
1896 if (lseek (curpos, SEEK_SET) >= 0)
1897 res = (ssize_t) tmp_count;
1898 else
1899 res = -1;
1900 }
1901
1902 /* If this was a disallowed async request, simulate its conclusion */
1903 if (aio)
1904 {
1905 aiocb->aio_rbytes = res;
1906 aiocb->aio_errno = res == -1 ? get_errno () : 0;
1907 SetEvent ((HANDLE) aiocb->aio_wincb.event);
1908 }
1909 }
1910 out:
1911 debug_printf ("%d = pread(%p, %ld, %D, %p)\n", res, buf, count, offset, aio);
1912 return res;
1913 }
1914
1915 ssize_t
1916 fhandler_disk_file::pwrite (void *buf, size_t count, off_t offset, void *aio)
1917 {
1918 struct aiocb *aiocb = (struct aiocb *) aio;
1919 ssize_t res;
1920
1921 if ((get_flags () & O_ACCMODE) == O_RDONLY)
1922 {
1923 set_errno (EBADF);
1924 return -1;
1925 }
1926
1927 /* In binary mode, we can use an atomic NtWriteFile call.
1928 Windows mandatory locking semantics disallow to use another HANDLE. */
1929 if (wbinary () && !mandatory_locking ())
1930 {
1931 NTSTATUS status;
1932 IO_STATUS_BLOCK io;
1933 FILE_STANDARD_INFORMATION fsi;
1934 LARGE_INTEGER off = { QuadPart:offset };
1935 HANDLE evt = aio ? (HANDLE) aiocb->aio_wincb.event : NULL;
1936 PIO_STATUS_BLOCK pio = aio ? (PIO_STATUS_BLOCK) &aiocb->aio_wincb : &io;
1937
1938 /* If existing prw_handle asyncness doesn't match this call's, re-open */
1939 if (prw_handle && (prw_handle_isasync != !!aio))
1940 NtClose (prw_handle), prw_handle = NULL;
1941
1942 /* If the file system supports sparse files and the application is
1943 writing beyond EOF spanning more than one sparsifiable chunk,
1944 convert the file to a sparse file. */
1945 if (pc.support_sparse ()
1946 && !has_attribute (FILE_ATTRIBUTE_SPARSE_FILE)
1947 && NT_SUCCESS (NtQueryInformationFile (get_handle (),
1948 &io, &fsi, sizeof fsi,
1949 FileStandardInformation))
1950 && span_sparse_chunk (offset, fsi.EndOfFile.QuadPart))
1951 {
1952 NTSTATUS status;
1953 status = NtFsControlFile (get_handle (), NULL, NULL, NULL,
1954 &io, FSCTL_SET_SPARSE, NULL, 0, NULL, 0);
1955 if (NT_SUCCESS (status))
1956 pc.file_attributes (pc.file_attributes ()
1957 | FILE_ATTRIBUTE_SPARSE_FILE);
1958 debug_printf ("%y = NtFsControlFile(%S, FSCTL_SET_SPARSE)",
1959 status, pc.get_nt_native_path ());
1960 }
1961 if (!prw_handle && prw_open (true, aio))
1962 goto non_atomic;
1963 status = NtWriteFile (prw_handle, evt, NULL, NULL, pio, buf, count,
1964 &off, NULL);
1965 if (!NT_SUCCESS (status))
1966 {
1967 __seterrno_from_nt_status (status);
1968 return -1;
1969 }
1970 res = aio ? (ssize_t) aiocb->aio_wincb.info : io.Information;
1971 goto out;
1972 }
1973 else
1974 {
1975 non_atomic:
1976 /* Text mode stays slow and non-atomic. */
1977 off_t curpos = lseek (0, SEEK_CUR);
1978 if (curpos < 0 || lseek (offset, SEEK_SET) < 0)
1979 res = curpos;
1980 else
1981 {
1982 res = (ssize_t) write (buf, count);
1983 if (lseek (curpos, SEEK_SET) < 0)
1984 res = -1;
1985 }
1986
1987 /* If this was a disallowed async request, simulate its conclusion */
1988 if (aio)
1989 {
1990 aiocb->aio_rbytes = res;
1991 aiocb->aio_errno = res == -1 ? get_errno () : 0;
1992 SetEvent ((HANDLE) aiocb->aio_wincb.event);
1993 }
1994 }
1995 out:
1996 debug_printf ("%d = pwrite(%p, %ld, %D, %p)\n", res, buf, count, offset, aio);
1997 return res;
1998 }
1999
2000 int
2001 fhandler_disk_file::mkdir (mode_t mode)
2002 {
2003 int res = -1;
2004 SECURITY_ATTRIBUTES sa = sec_none_nih;
2005 NTSTATUS status;
2006 HANDLE dir;
2007 OBJECT_ATTRIBUTES attr;
2008 IO_STATUS_BLOCK io;
2009 PFILE_FULL_EA_INFORMATION p = NULL;
2010 ULONG plen = 0;
2011 ULONG access = FILE_LIST_DIRECTORY | SYNCHRONIZE;
2012
2013 if (pc.fs_is_nfs ())
2014 {
2015 /* When creating a dir on an NFS share, we have to set the
2016 file mode by writing a NFS fattr3 structure with the
2017 correct mode bits set. */
2018 plen = sizeof (FILE_FULL_EA_INFORMATION) + sizeof (NFS_V3_ATTR)
2019 + sizeof (fattr3);
2020 p = (PFILE_FULL_EA_INFORMATION) alloca (plen);
2021 p->NextEntryOffset = 0;
2022 p->Flags = 0;
2023 p->EaNameLength = sizeof (NFS_V3_ATTR) - 1;
2024 p->EaValueLength = sizeof (fattr3);
2025 strcpy (p->EaName, NFS_V3_ATTR);
2026 fattr3 *nfs_attr = (fattr3 *) (p->EaName + p->EaNameLength + 1);
2027 memset (nfs_attr, 0, sizeof (fattr3));
2028 nfs_attr->type = NF3DIR;
2029 nfs_attr->mode = (mode & 07777) & ~cygheap->umask;
2030 }
2031 else if (has_acls () && !isremote ())
2032 /* If the filesystem supports ACLs, we will overwrite the DACL after the
2033 call to NtCreateFile. This requires a handle with READ_CONTROL and
2034 WRITE_DAC access, otherwise get_file_sd and set_file_sd both have to
2035 open the file again.
2036 FIXME: On remote NTFS shares open sometimes fails because even the
2037 creator of the file doesn't have the right to change the DACL.
2038 I don't know what setting that is or how to recognize such a share,
2039 so for now we don't request WRITE_DAC on remote drives. */
2040 access |= READ_CONTROL | WRITE_DAC;
2041 /* Setting case sensitivity requires FILE_WRITE_ATTRIBUTES. */
2042 if (wincap.has_case_sensitive_dirs ()
2043 && !pc.isremote () && pc.fs_is_ntfs ())
2044 access |= FILE_WRITE_ATTRIBUTES;
2045 status = NtCreateFile (&dir, access, pc.get_object_attr (attr, sa), &io, NULL,
2046 FILE_ATTRIBUTE_DIRECTORY, FILE_SHARE_VALID_FLAGS,
2047 FILE_CREATE,
2048 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT
2049 | FILE_OPEN_FOR_BACKUP_INTENT,
2050 p, plen);
2051 if (NT_SUCCESS (status))
2052 {
2053 /* Set the "directory attribute" so that pc.isdir() returns correct
2054 value in subsequent function calls. */
2055 pc.file_attributes (FILE_ATTRIBUTE_DIRECTORY);
2056 if (has_acls ())
2057 set_created_file_access (dir, pc, mode & 07777);
2058 #if 0
2059 /* FIXME: This default behaviour badly breaks interoperability.
2060 Inspecting the content of case-sensitive directories
2061 on remote machines results in lots of errors like
2062 disappearing diretories and files, file not found, etc. */
2063
2064 /* Starting with Windows 10 1803, try to create all dirs below the
2065 installation root as case-sensitive. If STATUS_NOT_SUPPORTED
2066 is returned, WSL isn't installed (unfortunately a requirement
2067 for this functionality. */
2068 if (wincap.has_case_sensitive_dirs ()
2069 && !pc.isremote () && pc.fs_is_ntfs ())
2070 {
2071 PUNICODE_STRING new_dir = pc.get_nt_native_path ();
2072 PUNICODE_STRING root_dir = &cygheap->installation_root;
2073
2074 if (RtlEqualUnicodePathPrefix (new_dir, root_dir, TRUE)
2075 && new_dir->Buffer[root_dir->Length / sizeof (WCHAR)] == L'\\')
2076 {
2077 FILE_CASE_SENSITIVE_INFORMATION fcsi;
2078
2079 fcsi.Flags = FILE_CS_FLAG_CASE_SENSITIVE_DIR;
2080 status = NtSetInformationFile (dir, &io, &fcsi, sizeof fcsi,
2081 FileCaseSensitiveInformation);
2082 if (!NT_SUCCESS (status))
2083 {
2084 debug_printf ("Setting dir case sensitivity, status %y",
2085 status);
2086 if (status == STATUS_NOT_SUPPORTED)
2087 {
2088 debug_printf ("Dir case sensitivity requires WSL");
2089 wincap.disable_case_sensitive_dirs ();
2090 }
2091 }
2092 }
2093 }
2094 #endif
2095 NtClose (dir);
2096 res = 0;
2097 }
2098 else
2099 __seterrno_from_nt_status (status);
2100
2101 return res;
2102 }
2103
2104 int
2105 fhandler_disk_file::rmdir ()
2106 {
2107 if (!pc.isdir ())
2108 {
2109 set_errno (ENOTDIR);
2110 return -1;
2111 }
2112 if (!pc.exists ())
2113 {
2114 set_errno (ENOENT);
2115 return -1;
2116 }
2117
2118 NTSTATUS status = unlink_nt (pc, false);
2119
2120 if (!NT_SUCCESS (status))
2121 {
2122 __seterrno_from_nt_status (status);
2123 return -1;
2124 }
2125 return 0;
2126 }
2127
2128 /* This is the minimal number of entries which fit into the readdir cache.
2129 The number of bytes allocated by the cache is determined by this number,
2130 To tune caching, just tweak this number. To get a feeling for the size,
2131 the size of the readdir cache is DIR_NUM_ENTRIES * 624 + 4 bytes. */
2132
2133 #define DIR_NUM_ENTRIES 100 /* Cache size 62404 bytes */
2134
2135 #define DIR_BUF_SIZE (DIR_NUM_ENTRIES \
2136 * (sizeof (FILE_ID_BOTH_DIR_INFORMATION) \
2137 + (NAME_MAX + 1) * sizeof (WCHAR)))
2138
2139 struct __DIR_cache
2140 {
2141 char __cache[DIR_BUF_SIZE];
2142 ULONG __pos;
2143 };
2144
2145 #define d_cachepos(d) (((__DIR_cache *) (d)->__d_dirname)->__pos)
2146 #define d_cache(d) (((__DIR_cache *) (d)->__d_dirname)->__cache)
2147
2148 #define d_mounts(d) ((__DIR_mounts *) (d)->__d_internal)
2149
2150 DIR *
2151 fhandler_disk_file::opendir (int fd)
2152 {
2153 DIR *dir;
2154 DIR *res = NULL;
2155
2156 if ((dir = (DIR *) malloc (sizeof (DIR))) == NULL)
2157 set_errno (ENOMEM);
2158 else if ((dir->__d_dirname = (char *) malloc ( sizeof (struct __DIR_cache)))
2159 == NULL)
2160 {
2161 set_errno (ENOMEM);
2162 goto free_dir;
2163 }
2164 else if ((dir->__d_dirent =
2165 (struct dirent *) malloc (sizeof (struct dirent))) == NULL)
2166 {
2167 set_errno (ENOMEM);
2168 goto free_dirname;
2169 }
2170 else
2171 {
2172 cygheap_fdnew cfd;
2173 if (cfd < 0 && fd < 0)
2174 goto free_dirent;
2175
2176 dir->__d_dirent->__d_version = __DIRENT_VERSION;
2177 dir->__d_cookie = __DIRENT_COOKIE;
2178 dir->__handle = INVALID_HANDLE_VALUE;
2179 dir->__d_position = 0;
2180 dir->__flags = (get_name ()[0] == '/' && get_name ()[1] == '\0')
2181 ? dirent_isroot : 0;
2182 dir->__d_internal = 0;
2183
2184 if (pc.iscygdrive ())
2185 {
2186 if (fd < 0 && !open (O_RDONLY, 0))
2187 goto free_mounts;
2188 }
2189 else
2190 {
2191 dir->__d_internal = (uintptr_t) new __DIR_mounts (get_name ());
2192 d_cachepos (dir) = 0;
2193 if (fd < 0)
2194 {
2195 /* opendir() case. Initialize with given directory name and
2196 NULL directory handle. */
2197 OBJECT_ATTRIBUTES attr;
2198 NTSTATUS status;
2199 IO_STATUS_BLOCK io;
2200 /* Tools like ls(1) call dirfd() to fetch the directory
2201 descriptor for calls to facl or fstat. The tight access mask
2202 used so far is not sufficient to reuse the handle for these
2203 calls, instead the facl/fstat calls find the handle to be
2204 unusable and have to re-open the file for reading attributes
2205 and control data. So, what we do here is to try to open the
2206 directory with more relaxed access mask which enables to use
2207 the handle for the aforementioned purpose. This should work
2208 in almost all cases. Only if it doesn't work due to
2209 permission problems, we drop the additional access bits and
2210 try again. */
2211 ACCESS_MASK fstat_mask = READ_CONTROL | FILE_READ_ATTRIBUTES;
2212
2213 do
2214 {
2215 status = NtOpenFile (&get_handle (),
2216 SYNCHRONIZE | FILE_LIST_DIRECTORY
2217 | fstat_mask,
2218 pc.get_object_attr (attr, sec_none_nih),
2219 &io, FILE_SHARE_VALID_FLAGS,
2220 FILE_SYNCHRONOUS_IO_NONALERT
2221 | FILE_OPEN_FOR_BACKUP_INTENT
2222 | FILE_DIRECTORY_FILE);
2223 if (!NT_SUCCESS (status))
2224 {
2225 if (status == STATUS_ACCESS_DENIED && fstat_mask)
2226 fstat_mask = 0;
2227 else
2228 {
2229 __seterrno_from_nt_status (status);
2230 goto free_mounts;
2231 }
2232 }
2233 }
2234 while (!NT_SUCCESS (status));
2235 }
2236
2237 /* FileIdBothDirectoryInformation was unsupported on XP when
2238 accessing UDF. It's not clear if the call isn't also unsupported
2239 on other OS/FS combinations. Instead of testing for yet another
2240 error code, use FileIdBothDirectoryInformation only on FSes
2241 supporting persistent ACLs.
2242
2243 NFS clients hide dangling symlinks from directory queries,
2244 unless you use the FileNamesInformation info class.
2245 FileIdBothDirectoryInformation works fine, but only if the NFS
2246 share is mounted to a drive letter. TODO: We don't test that
2247 here for now, but it might be worth to test if there's a speed
2248 gain in using FileIdBothDirectoryInformation, because it doesn't
2249 require to open the file to read the inode number. */
2250 if (pc.hasgood_inode ())
2251 {
2252 dir->__flags |= dirent_set_d_ino;
2253 if (pc.fs_is_nfs ())
2254 dir->__flags |= dirent_nfs_d_ino;
2255 else if (!pc.has_buggy_fileid_dirinfo ())
2256 dir->__flags |= dirent_get_d_ino;
2257 }
2258 }
2259 if (fd >= 0)
2260 dir->__d_fd = fd;
2261 else
2262 {
2263 /* Filling cfd with `this' (aka storing this in the file
2264 descriptor table should only happen after it's clear that
2265 opendir doesn't fail, otherwise we end up cfree'ing the
2266 fhandler twice, once in opendir() in dir.cc, the second
2267 time on exit. Nasty, nasty... */
2268 cfd = this;
2269 dir->__d_fd = cfd;
2270 }
2271 set_close_on_exec (true);
2272 dir->__fh = this;
2273 res = dir;
2274 }
2275
2276 syscall_printf ("%p = opendir (%s)", res, get_name ());
2277 return res;
2278
2279 free_mounts:
2280 delete d_mounts (dir);
2281 free_dirent:
2282 free (dir->__d_dirent);
2283 free_dirname:
2284 free (dir->__d_dirname);
2285 free_dir:
2286 free (dir);
2287 return res;
2288 }
2289
2290 ino_t
2291 readdir_get_ino (const char *path, bool dot_dot)
2292 {
2293 char *fname;
2294 struct stat st;
2295 HANDLE hdl;
2296 OBJECT_ATTRIBUTES attr;
2297 IO_STATUS_BLOCK io;
2298 ino_t ino = 0;
2299
2300 if (dot_dot)
2301 {
2302 fname = (char *) alloca (strlen (path) + 4);
2303 char *c = stpcpy (fname, path);
2304 if (c[-1] != '/')
2305 *c++ = '/';
2306 strcpy (c, "..");
2307 path = fname;
2308 }
2309 path_conv pc (path, PC_SYM_NOFOLLOW | PC_POSIX | PC_KEEP_HANDLE);
2310 if (pc.isspecial ())
2311 {
2312 if (!stat_worker (pc, &st))
2313 ino = st.st_ino;
2314 }
2315 else if (!pc.hasgood_inode ())
2316 ino = hash_path_name (0, pc.get_nt_native_path ());
2317 else if ((hdl = pc.handle ()) != NULL
2318 || NT_SUCCESS (NtOpenFile (&hdl, READ_CONTROL,
2319 pc.get_object_attr (attr, sec_none_nih),
2320 &io, FILE_SHARE_VALID_FLAGS,
2321 FILE_OPEN_NO_RECALL
2322 | FILE_OPEN_FOR_BACKUP_INTENT
2323 | (pc.is_known_reparse_point ()
2324 ? FILE_OPEN_REPARSE_POINT : 0)))
2325 )
2326 {
2327 ino = pc.get_ino_by_handle (hdl);
2328 if (!ino)
2329 ino = hash_path_name (0, pc.get_nt_native_path ());
2330 }
2331 return ino;
2332 }
2333
2334 int
2335 fhandler_disk_file::readdir_helper (DIR *dir, dirent *de, DWORD w32_err,
2336 DWORD attr, PUNICODE_STRING fname)
2337 {
2338 if (w32_err)
2339 {
2340 switch (d_mounts (dir)->check_missing_mount (fname))
2341 {
2342 case __DIR_mount_none:
2343 fname->Length = 0;
2344 return geterrno_from_win_error (w32_err);
2345 case __DIR_mount_virt_target:
2346 de->d_type = DT_DIR;
2347 break;
2348 default:
2349 break;
2350 }
2351 attr = 0;
2352 dir->__flags &= ~dirent_set_d_ino;
2353 }
2354
2355 /* Set d_type if type can be determined from file attributes. For .lnk
2356 symlinks, d_type will be reset below. Reparse points can be NTFS
2357 symlinks, even if they have the FILE_ATTRIBUTE_DIRECTORY flag set. */
2358 if (attr && !(attr & ~FILE_ATTRIBUTE_VALID_FLAGS))
2359 {
2360 if (attr & FILE_ATTRIBUTE_DIRECTORY)
2361 de->d_type = DT_DIR;
2362 /* FILE_ATTRIBUTE_SYSTEM might denote system-bit type symlinks. */
2363 else if (!(attr & FILE_ATTRIBUTE_SYSTEM))
2364 de->d_type = DT_REG;
2365 }
2366
2367 /* Check for reparse points that can be treated as posix symlinks.
2368 Mountpoints and unknown or unhandled reparse points will be treated
2369 as normal file/directory/unknown. In all cases, returning the INO of
2370 the reparse point (not of the target) matches behavior of posix systems.
2371 Unless the file is OFFLINE. *.
2372 */
2373 if ((attr & FILE_ATTRIBUTE_REPARSE_POINT) && !isoffline (attr))
2374 {
2375 OBJECT_ATTRIBUTES oattr;
2376
2377 InitializeObjectAttributes (&oattr, fname, pc.objcaseinsensitive (),
2378 get_handle (), NULL);
2379 if (readdir_check_reparse_point (&oattr, isremote ()))
2380 de->d_type = DT_LNK;
2381 }
2382
2383 /* Check for Windows shortcut. If it's a Cygwin or U/WIN symlink, drop the
2384 .lnk suffix and set d_type accordingly. */
2385 if ((attr & (FILE_ATTRIBUTE_DIRECTORY
2386 | FILE_ATTRIBUTE_REPARSE_POINT
2387 | FILE_ATTRIBUTE_READONLY)) == FILE_ATTRIBUTE_READONLY
2388 && fname->Length > 4 * sizeof (WCHAR))
2389 {
2390 UNICODE_STRING uname;
2391
2392 RtlInitCountedUnicodeString (&uname,
2393 fname->Buffer
2394 + fname->Length / sizeof (WCHAR) - 4,
2395 4 * sizeof (WCHAR));
2396 if (RtlEqualUnicodeString (&uname, &ro_u_lnk, TRUE))
2397 {
2398 tmp_pathbuf tp;
2399 char *file = tp.c_get ();
2400 char *p = stpcpy (file, pc.get_posix ());
2401 if (p[-1] != '/')
2402 *p++ = '/';
2403 sys_wcstombs (p, NT_MAX_PATH - (p - file),
2404 fname->Buffer, fname->Length / sizeof (WCHAR));
2405 path_conv fpath (file, PC_SYM_NOFOLLOW);
2406 if (fpath.issymlink ())
2407 {
2408 fname->Length -= 4 * sizeof (WCHAR);
2409 de->d_type = DT_LNK;
2410 }
2411 else if (fpath.isfifo ())
2412 {
2413 fname->Length -= 4 * sizeof (WCHAR);
2414 de->d_type = DT_FIFO;
2415 }
2416 else if (fpath.is_fs_special ())
2417 {
2418 fname->Length -= 4 * sizeof (WCHAR);
2419 de->d_type = S_ISCHR (fpath.dev.mode ()) ? DT_CHR : DT_BLK;
2420 }
2421 }
2422 }
2423
2424 sys_wcstombs (de->d_name, NAME_MAX + 1, fname->Buffer,
2425 fname->Length / sizeof (WCHAR));
2426
2427 /* Don't try to optimize relative to dir->__d_position. On several
2428 filesystems it's no safe bet that "." and ".." entries always
2429 come first. */
2430 if (de->d_name[0] == '.')
2431 {
2432 if (de->d_name[1] == '\0')
2433 dir->__flags |= dirent_saw_dot;
2434 else if (de->d_name[1] == '.' && de->d_name[2] == '\0')
2435 dir->__flags |= dirent_saw_dot_dot;
2436 }
2437 return 0;
2438 }
2439
2440 int
2441 fhandler_disk_file::readdir (DIR *dir, dirent *de)
2442 {
2443 int res = 0;
2444 NTSTATUS status = STATUS_SUCCESS;
2445 PFILE_ID_BOTH_DIR_INFORMATION buf = NULL;
2446 PWCHAR FileName;
2447 ULONG FileNameLength;
2448 ULONG FileAttributes;
2449 IO_STATUS_BLOCK io;
2450 UNICODE_STRING fname;
2451
2452 /* d_cachepos always refers to the next cache entry to use. If it's 0
2453 we must reload the cache. */
2454 FileAttributes = 0;
2455 if (d_cachepos (dir) == 0)
2456 {
2457 if ((dir->__flags & dirent_get_d_ino))
2458 {
2459 status = NtQueryDirectoryFile (get_handle (), NULL, NULL, NULL, &io,
2460 d_cache (dir), DIR_BUF_SIZE,
2461 FileIdBothDirectoryInformation,
2462 FALSE, NULL, dir->__d_position == 0);
2463 /* FileIdBothDirectoryInformation isn't supported on some
2464 remote drives, but we don't know every system out there.
2465 Check various status codes indicating this. */
2466 if (!NT_SUCCESS (status)
2467 && (status == STATUS_INVALID_LEVEL
2468 || status == STATUS_NOT_SUPPORTED
2469 || status == STATUS_INVALID_PARAMETER
2470 || status == STATUS_INVALID_NETWORK_RESPONSE
2471 || status == STATUS_INVALID_INFO_CLASS))
2472 dir->__flags &= ~dirent_get_d_ino;
2473 }
2474 /* NFS must use FileNamesInformation! Any other information class
2475 skips all symlinks. */
2476 if (!(dir->__flags & dirent_get_d_ino))
2477 status = NtQueryDirectoryFile (get_handle (), NULL, NULL, NULL, &io,
2478 d_cache (dir), DIR_BUF_SIZE,
2479 (dir->__flags & dirent_nfs_d_ino)
2480 ? FileNamesInformation
2481 : FileBothDirectoryInformation,
2482 FALSE, NULL, dir->__d_position == 0);
2483 }
2484
2485 if (status == STATUS_NO_MORE_FILES)
2486 /*nothing*/;
2487 else if (!NT_SUCCESS (status))
2488 debug_printf ("NtQueryDirectoryFile failed, status %y, win32 error %u",
2489 status, RtlNtStatusToDosError (status));
2490 else
2491 {
2492 buf = (PFILE_ID_BOTH_DIR_INFORMATION) (d_cache (dir) + d_cachepos (dir));
2493 if (buf->NextEntryOffset == 0)
2494 d_cachepos (dir) = 0;
2495 else
2496 d_cachepos (dir) += buf->NextEntryOffset;
2497 if ((dir->__flags & dirent_get_d_ino))
2498 {
2499 FileName = buf->FileName;
2500 FileNameLength = buf->FileNameLength;
2501 FileAttributes = buf->FileAttributes;
2502 if ((dir->__flags & dirent_set_d_ino))
2503 de->d_ino = buf->FileId.QuadPart;
2504 }
2505 else if ((dir->__flags & dirent_nfs_d_ino))
2506 {
2507 FileName = ((PFILE_NAMES_INFORMATION) buf)->FileName;
2508 FileNameLength = ((PFILE_NAMES_INFORMATION) buf)->FileNameLength;
2509 }
2510 else
2511 {
2512 FileName = ((PFILE_BOTH_DIR_INFORMATION) buf)->FileName;
2513 FileNameLength =
2514 ((PFILE_BOTH_DIR_INFORMATION) buf)->FileNameLength;
2515 FileAttributes =
2516 ((PFILE_BOTH_DIR_INFORMATION) buf)->FileAttributes;
2517 }
2518 RtlInitCountedUnicodeString (&fname, FileName, FileNameLength);
2519 d_mounts (dir)->check_mount (&fname);
2520 if (de->d_ino == 0 && (dir->__flags & dirent_set_d_ino))
2521 {
2522 /* Don't try to optimize relative to dir->__d_position. On several
2523 filesystems it's no safe bet that "." and ".." entries always
2524 come first. */
2525 if (FileNameLength == sizeof (WCHAR) && FileName[0] == '.')
2526 de->d_ino = pc.get_ino_by_handle (get_handle ());
2527 else if (FileNameLength == 2 * sizeof (WCHAR)
2528 && FileName[0] == L'.' && FileName[1] == L'.')
2529 {
2530 if (!(dir->__flags & dirent_isroot))
2531 de->d_ino = readdir_get_ino (get_name (), true);
2532 else
2533 de->d_ino = pc.get_ino_by_handle (get_handle ());
2534 }
2535 else
2536 {
2537 OBJECT_ATTRIBUTES attr;
2538 HANDLE hdl;
2539 NTSTATUS f_status;
2540
2541 InitializeObjectAttributes (&attr, &fname,
2542 pc.objcaseinsensitive (),
2543 get_handle (), NULL);
2544 /* FILE_OPEN_REPARSE_POINT on NFS is a no-op, so the normal
2545 NtOpenFile here returns the inode number of the symlink target,
2546 rather than the inode number of the symlink itself.
2547
2548 Worse, trying to open a symlink without setting the special
2549 "ActOnSymlink" EA triggers a bug in Windows 7 which results
2550 in a timeout of up to 20 seconds, followed by two exceptions
2551 in the NT kernel.
2552
2553 Since both results are far from desirable, we open symlinks
2554 on NFS so that we get the right inode and a happy W7.
2555 And, since some filesystems choke on the EAs, we don't
2556 use them unconditionally. */
2557 f_status = (dir->__flags & dirent_nfs_d_ino)
2558 ? NtCreateFile (&hdl,
2559 READ_CONTROL | FILE_READ_ATTRIBUTES,
2560 &attr, &io, NULL, 0,
2561 FILE_SHARE_VALID_FLAGS, FILE_OPEN,
2562 FILE_OPEN_FOR_BACKUP_INTENT,
2563 &nfs_aol_ffei, sizeof nfs_aol_ffei)
2564 : NtOpenFile (&hdl, READ_CONTROL, &attr, &io,
2565 FILE_SHARE_VALID_FLAGS,
2566 FILE_OPEN_NO_RECALL
2567 | FILE_OPEN_FOR_BACKUP_INTENT
2568 | FILE_OPEN_REPARSE_POINT);
2569 if (NT_SUCCESS (f_status))
2570 {
2571 /* We call NtQueryInformationFile here, rather than
2572 pc.get_ino_by_handle(), otherwise we can't short-circuit
2573 dirent_set_d_ino correctly. */
2574 FILE_INTERNAL_INFORMATION fii;
2575 f_status = NtQueryInformationFile (hdl, &io, &fii, sizeof fii,
2576 FileInternalInformation);
2577 /* On NFS fetch the (faked, but useful) DOS attribute.
2578 We need it to recognize shortcut FIFOs. */
2579 if ((dir->__flags & dirent_nfs_d_ino))
2580 {
2581 FILE_BASIC_INFORMATION fbi;
2582
2583 if (NT_SUCCESS (NtQueryInformationFile (hdl, &io, &fbi,
2584 sizeof fbi, FileBasicInformation)))
2585 FileAttributes = fbi.FileAttributes;
2586 }
2587 NtClose (hdl);
2588 if (NT_SUCCESS (f_status))
2589 {
2590 if (pc.isgood_inode (fii.IndexNumber.QuadPart))
2591 de->d_ino = fii.IndexNumber.QuadPart;
2592 else
2593 /* Untrusted file system. Don't try to fetch inode
2594 number again. */
2595 dir->__flags &= ~dirent_set_d_ino;
2596 }
2597 }
2598 }
2599 }
2600 }
2601
2602 if (!(res = readdir_helper (dir, de, RtlNtStatusToDosError (status),
2603 FileAttributes, &fname)))
2604 dir->__d_position++;
2605 else if (!(dir->__flags & dirent_saw_dot))
2606 {
2607 strcpy (de->d_name , ".");
2608 de->d_ino = pc.get_ino_by_handle (get_handle ());
2609 de->d_type = DT_DIR;
2610 dir->__d_position++;
2611 dir->__flags |= dirent_saw_dot;
2612 res = 0;
2613 }
2614 else if (!(dir->__flags & dirent_saw_dot_dot))
2615 {
2616 strcpy (de->d_name , "..");
2617 if (!(dir->__flags & dirent_isroot))
2618 de->d_ino = readdir_get_ino (get_name (), true);
2619 else
2620 de->d_ino = pc.get_ino_by_handle (get_handle ());
2621 de->d_type = DT_DIR;
2622 dir->__d_position++;
2623 dir->__flags |= dirent_saw_dot_dot;
2624 res = 0;
2625 }
2626
2627 syscall_printf ("%d = readdir(%p, %p) (L\"%lS\" > \"%ls\") (attr %y > type %d)",
2628 res, dir, &de, res ? NULL : &fname, res ? "***" : de->d_name,
2629 FileAttributes, de->d_type);
2630 return res;
2631 }
2632
2633 long
2634 fhandler_disk_file::telldir (DIR *dir)
2635 {
2636 return dir->__d_position;
2637 }
2638
2639 void
2640 fhandler_disk_file::seekdir (DIR *dir, long loc)
2641 {
2642 rewinddir (dir);
2643 while (loc > dir->__d_position)
2644 if (!::readdir (dir))
2645 break;
2646 }
2647
2648 void
2649 fhandler_disk_file::rewinddir (DIR *dir)
2650 {
2651 d_cachepos (dir) = 0;
2652 dir->__d_position = 0;
2653 d_mounts (dir)->rewind ();
2654 }
2655
2656 int
2657 fhandler_disk_file::closedir (DIR *dir)
2658 {
2659 int res = 0;
2660
2661 delete d_mounts (dir);
2662 syscall_printf ("%d = closedir(%p, %s)", res, dir, get_name ());
2663 return res;
2664 }
2665
2666 uint64_t
2667 fhandler_disk_file::fs_ioc_getflags ()
2668 {
2669 NTSTATUS status;
2670 IO_STATUS_BLOCK io;
2671 FILE_BASIC_INFORMATION fbi;
2672 FILE_CASE_SENSITIVE_INFORMATION fcsi;
2673 uint64_t flags = 0;
2674
2675 status = NtQueryInformationFile (get_handle (), &io, &fbi, sizeof fbi,
2676 FileBasicInformation);
2677 if (NT_SUCCESS (status))
2678 {
2679 flags = (uint64_t) fbi.FileAttributes & FS_FL_USER_VISIBLE;
2680 pc.file_attributes (fbi.FileAttributes);
2681 }
2682 else
2683 flags = (uint64_t) pc.file_attributes () & FS_FL_USER_VISIBLE;
2684 if (pc.isdir () && wincap.has_case_sensitive_dirs ()
2685 && !pc.isremote () && pc.fs_is_ntfs ())
2686 {
2687 fcsi.Flags = 0;
2688 status = NtQueryInformationFile (get_handle (), &io,
2689 &fcsi, sizeof fcsi,
2690 FileCaseSensitiveInformation);
2691 if (NT_SUCCESS (status)
2692 && (fcsi.Flags & FILE_CS_FLAG_CASE_SENSITIVE_DIR))
2693 flags |= FS_CASESENS_FL;
2694 }
2695 return flags;
2696 }
2697
2698 /* Settable DOS attributes */
2699 #define FS_FL_SETATTRIBS (FS_READONLY_FL \
2700 | FS_HIDDEN_FL \
2701 | FS_SYSTEM_FL \
2702 | FS_ARCHIVE_FL \
2703 | FS_TEMP_FL \
2704 | FS_NOTINDEXED_FL\
2705 | FS_PINNED_FL \
2706 | FS_UNPINNED_FL)
2707
2708 int
2709 fhandler_disk_file::fs_ioc_setflags (uint64_t flags)
2710 {
2711 int ret = -1;
2712 uint64_t old_flags;
2713 HANDLE fh;
2714 NTSTATUS status;
2715 OBJECT_ATTRIBUTES attr;
2716 IO_STATUS_BLOCK io;
2717 FILE_BASIC_INFORMATION fbi;
2718 FILE_SET_SPARSE_BUFFER fssb;
2719 USHORT comp;
2720 FILE_CASE_SENSITIVE_INFORMATION fcsi;
2721
2722 if ((get_access () & (GENERIC_WRITE | FILE_WRITE_ATTRIBUTES)) != 0)
2723 fh = get_handle ();
2724 else
2725 {
2726 status = NtOpenFile (&fh, FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES,
2727 pc.init_reopen_attr (attr, get_handle ()), &io,
2728 FILE_SHARE_VALID_FLAGS,
2729 FILE_OPEN_FOR_BACKUP_INTENT);
2730 if (!NT_SUCCESS (status))
2731 {
2732 fh = get_handle ();
2733 __seterrno_from_nt_status (status);
2734 goto out;
2735 }
2736 }
2737 old_flags = fs_ioc_getflags ();
2738 if ((old_flags & FS_FL_SETATTRIBS) != (flags & FS_FL_SETATTRIBS))
2739 {
2740 fbi.CreationTime.QuadPart
2741 = fbi.LastAccessTime.QuadPart
2742 = fbi.LastWriteTime.QuadPart
2743 = fbi.ChangeTime.QuadPart = 0LL;
2744 fbi.FileAttributes = (ULONG) old_flags;
2745 fbi.FileAttributes &= ~FS_FL_SETATTRIBS;
2746 fbi.FileAttributes |= (flags & FS_FL_SETATTRIBS);
2747 if (fbi.FileAttributes == 0)
2748 fbi.FileAttributes = FILE_ATTRIBUTE_NORMAL;
2749 status = NtSetInformationFile (fh, &io, &fbi, sizeof fbi,
2750 FileBasicInformation);
2751 if (!NT_SUCCESS (status))
2752 {
2753 __seterrno_from_nt_status (status);
2754 goto out;
2755 }
2756 }
2757 if (!pc.isdir() && (flags & FS_SPARSE_FL) != (old_flags & FS_SPARSE_FL))
2758 {
2759 fssb.SetSparse = (flags & FS_SPARSE_FL) ? TRUE : FALSE;
2760 status = NtFsControlFile (fh, NULL, NULL, NULL, &io,
2761 FSCTL_SET_SPARSE, &fssb, sizeof fssb, NULL, 0);
2762 if (!NT_SUCCESS (status))
2763 {
2764 __seterrno_from_nt_status (status);
2765 goto out;
2766 }
2767 }
2768 if (pc.isdir () && (flags & FS_CASESENS_FL) != (old_flags & FS_CASESENS_FL))
2769 {
2770 if (wincap.has_case_sensitive_dirs ()
2771 && !pc.isremote () && pc.fs_is_ntfs ())
2772 {
2773 fcsi.Flags = (flags & FS_CASESENS_FL)
2774 ? FILE_CS_FLAG_CASE_SENSITIVE_DIR : 0;
2775 status = NtSetInformationFile (fh, &io, &fcsi, sizeof fcsi,
2776 FileCaseSensitiveInformation);
2777 if (!NT_SUCCESS (status))
2778 {
2779 __seterrno_from_nt_status (status);
2780 goto out;
2781 }
2782 }
2783 else
2784 {
2785 set_errno (ENOTSUP);
2786 goto out;
2787 }
2788 }
2789 if ((flags & FS_COMPRESSED_FL) != (old_flags & FS_COMPRESSED_FL))
2790 {
2791 if (fh != get_handle ())
2792 NtClose (fh);
2793 fh = NULL;
2794 if ((get_access () & (GENERIC_WRITE | GENERIC_READ))
2795 != (GENERIC_WRITE | GENERIC_READ))
2796 {
2797 status = NtOpenFile (&fh, GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
2798 pc.init_reopen_attr (attr, get_handle ()), &io,
2799 FILE_SHARE_VALID_FLAGS,
2800 FILE_SYNCHRONOUS_IO_NONALERT
2801 | FILE_OPEN_FOR_BACKUP_INTENT);
2802 if (!NT_SUCCESS (status))
2803 {
2804 fh = get_handle ();
2805 __seterrno_from_nt_status (status);
2806 goto out;
2807 }
2808 }
2809 comp = (flags & FS_COMPRESSED_FL)
2810 ? COMPRESSION_FORMAT_DEFAULT : COMPRESSION_FORMAT_NONE;
2811 status = NtFsControlFile (fh, NULL, NULL, NULL, &io,
2812 FSCTL_SET_COMPRESSION, &comp, sizeof comp,
2813 NULL, 0);
2814 if (!NT_SUCCESS (status))
2815 {
2816 __seterrno_from_nt_status (status);
2817 goto out;
2818 }
2819 }
2820 if (!pc.isdir() && (flags & FS_ENCRYPT_FL) != (old_flags & FS_ENCRYPT_FL))
2821 {
2822 tmp_pathbuf tp;
2823 PWCHAR path = tp.w_get ();
2824 BOOL cret;
2825
2826 /* EncryptFileW/DecryptFileW needs exclusive access. */
2827 if (fh != get_handle ())
2828 NtClose (fh);
2829 NtClose (get_handle ());
2830 set_handle (NULL);
2831
2832 pc.get_wide_win32_path (path);
2833 cret = (flags & FS_ENCRYPT_FL)
2834 ? EncryptFileW (path) : DecryptFileW (path, 0);
2835 status = NtOpenFile (&fh, get_access (),
2836 pc.get_object_attr (attr, sec_none_nih), &io,
2837 FILE_SHARE_VALID_FLAGS,
2838 FILE_SYNCHRONOUS_IO_NONALERT
2839 | FILE_OPEN_FOR_BACKUP_INTENT);
2840 if (!NT_SUCCESS (status))
2841 {
2842 __seterrno_from_nt_status (status);
2843 return -1;
2844 }
2845 set_handle (fh);
2846 if (!cret)
2847 {
2848 __seterrno ();
2849 goto out;
2850 }
2851 }
2852 ret = 0;
2853 out:
2854 status = NtQueryInformationFile (fh, &io, &fbi, sizeof fbi,
2855 FileBasicInformation);
2856 if (NT_SUCCESS (status))
2857 pc.file_attributes (fbi.FileAttributes);
2858 if (fh != get_handle ())
2859 NtClose (fh);
2860 return ret;
2861 }
2862
2863 int
2864 fhandler_disk_file::ioctl (unsigned int cmd, void *p)
2865 {
2866 int ret = -1;
2867 uint64_t flags = 0;
2868
2869 switch (cmd)
2870 {
2871 case FS_IOC_GETFLAGS:
2872 __try
2873 {
2874 uint64_t *fp = (uint64_t *) p;
2875 *fp = fs_ioc_getflags ();
2876 ret = 0;
2877 }
2878 __except (EFAULT) {}
2879 __endtry
2880 break;
2881 case FS_IOC_SETFLAGS:
2882 __try
2883 {
2884 flags = *(__uint64_t *) p;
2885 }
2886 __except (EFAULT)
2887 {
2888 break;
2889 }
2890 __endtry
2891 if (flags & ~FS_FL_USER_MODIFIABLE)
2892 {
2893 set_errno (EINVAL);
2894 break;
2895 }
2896 ret = fs_ioc_setflags (flags);
2897 break;
2898 default:
2899 ret = fhandler_base::ioctl (cmd, p);
2900 break;
2901 }
2902 syscall_printf ("%d = ioctl_file(%x, %p)", ret, cmd, p);
2903 return ret;
2904 }
This page took 0.170154 seconds and 6 git commands to generate.