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