diff options
Diffstat (limited to 'winsup/cygwin/fhandler/base.cc')
-rw-r--r-- | winsup/cygwin/fhandler/base.cc | 1854 |
1 files changed, 1854 insertions, 0 deletions
diff --git a/winsup/cygwin/fhandler/base.cc b/winsup/cygwin/fhandler/base.cc new file mode 100644 index 000000000..b2738cf20 --- /dev/null +++ b/winsup/cygwin/fhandler/base.cc @@ -0,0 +1,1854 @@ +/* base.cc. Base functions, inherited by all fhandlers. + +This file is part of Cygwin. + +This software is a copyrighted work licensed under the terms of the +Cygwin license. Please consult the file "CYGWIN_LICENSE" for +details. */ + +#include "winsup.h" +#include <unistd.h> +#include <stdlib.h> +#include <sys/uio.h> +#include <cygwin/acl.h> +#include <sys/param.h> +#include "cygerrno.h" +#include "perprocess.h" +#include "security.h" +#include "cygwin/version.h" +#include "path.h" +#include "fhandler.h" +#include "dtable.h" +#include "cygheap.h" +#include "pinfo.h" +#include <assert.h> +#include <winioctl.h> +#include "ntdll.h" +#include "cygtls.h" +#include "sigproc.h" +#include "shared_info.h" +#include <asm/socket.h> +#include "cygwait.h" + +static const int CHUNK_SIZE = 1024; /* Used for crlf conversions */ + +struct __cygwin_perfile *perfile_table; + +int +fhandler_base::puts_readahead (const char *s, size_t len) +{ + int success = 1; + while ((len == (size_t) -1 ? *s : len--) + && (success = put_readahead (*s++) > 0)) + continue; + return success; +} + +int +fhandler_base::put_readahead (char value) +{ + char *newrabuf; + if (raixput () < rabuflen ()) + /* Nothing to do */; + else if ((newrabuf = (char *) realloc (rabuf (), rabuflen () += 32))) + rabuf () = newrabuf; + else + return 0; + + rabuf ()[raixput ()++] = value; + ralen ()++; + return 1; +} + +int +fhandler_base::get_readahead () +{ + int chret = -1; + if (raixget () < ralen ()) + chret = ((unsigned char) rabuf ()[raixget ()++]) & 0xff; + /* FIXME - not thread safe */ + if (raixget () >= ralen ()) + raixget () = raixput () = ralen () = 0; + return chret; +} + +int +fhandler_base::peek_readahead (int queryput) +{ + int chret = -1; + if (!queryput && raixget () < ralen ()) + chret = ((unsigned char) rabuf ()[raixget ()]) & 0xff; + else if (queryput && raixput () > 0) + chret = ((unsigned char) rabuf ()[raixput () - 1]) & 0xff; + return chret; +} + +void +fhandler_base::set_readahead_valid (int val, int ch) +{ + if (!val) + ralen () = raixget () = raixput () = 0; + if (ch != -1) + put_readahead (ch); +} + +int +fhandler_base::get_readahead_into_buffer (char *buf, size_t buflen) +{ + int ch; + int copied_chars = 0; + + while (buflen) + if ((ch = get_readahead ()) < 0) + break; + else + { + buf[copied_chars++] = (unsigned char)(ch & 0xff); + buflen--; + } + + return copied_chars; +} + +/* Record the file name. and name hash */ +void +fhandler_base::set_name (path_conv &in_pc) +{ + pc << in_pc; +} + +char *fhandler_base::get_proc_fd_name (char *buf) +{ + IO_STATUS_BLOCK io; + FILE_STANDARD_INFORMATION fsi; + + /* If the file had been opened with O_TMPFILE, don't expose the filename. */ + if ((get_flags () & O_TMPFILE) + || (get_device () == FH_FS + && NT_SUCCESS (NtQueryInformationFile (get_handle (), &io, + &fsi, sizeof fsi, + FileStandardInformation)) + && fsi.DeletePending)) + { + stpcpy (stpcpy (buf, get_name ()), " (deleted)"); + return buf; + } + if (get_name ()) + return strcpy (buf, get_name ()); + if (dev ().name ()) + return strcpy (buf, dev ().name ()); + return strcpy (buf, ""); +} + +/* Detect if we are sitting at EOF for conditions where Windows + returns an error but UNIX doesn't. */ +int +is_at_eof (HANDLE h) +{ + IO_STATUS_BLOCK io; + FILE_POSITION_INFORMATION fpi; + FILE_STANDARD_INFORMATION fsi; + + if (NT_SUCCESS (NtQueryInformationFile (h, &io, &fsi, sizeof fsi, + FileStandardInformation)) + && NT_SUCCESS (NtQueryInformationFile (h, &io, &fpi, sizeof fpi, + FilePositionInformation)) + && fsi.EndOfFile.QuadPart == fpi.CurrentByteOffset.QuadPart) + return 1; + return 0; +} + +void +fhandler_base::set_flags (int flags, int supplied_bin) +{ + int bin; + int fmode; + debug_printf ("flags %y, supplied_bin %y", flags, supplied_bin); + if ((bin = flags & (O_BINARY | O_TEXT))) + debug_printf ("O_TEXT/O_BINARY set in flags %y", bin); + else if (rbinset () && wbinset ()) + bin = rbinary () ? O_BINARY : O_TEXT; // FIXME: Not quite right + else if ((fmode = get_default_fmode (flags)) & O_BINARY) + bin = O_BINARY; + else if (fmode & O_TEXT) + bin = O_TEXT; + else if (supplied_bin) + bin = supplied_bin; + else + bin = wbinary () || rbinary () ? O_BINARY : O_TEXT; + + openflags = flags | bin; + + bin &= O_BINARY; + rbinary (bin ? true : false); + wbinary (bin ? true : false); + syscall_printf ("filemode set to %s", bin ? "binary" : "text"); +} + +/* Normal file i/o handlers. */ + +/* Cover function to ReadFile to achieve (as much as possible) Posix style + semantics and use of errno. */ +void +fhandler_base::raw_read (void *ptr, size_t& len) +{ + NTSTATUS status; + IO_STATUS_BLOCK io; + int try_noreserve = 1; + +retry: + status = NtReadFile (get_handle (), NULL, NULL, NULL, &io, ptr, len, + NULL, NULL); + if (NT_SUCCESS (status)) + len = io.Information; + else + { + /* Some errors are not really errors. Detect such cases here. */ + switch (status) + { + case STATUS_END_OF_FILE: + case STATUS_PIPE_BROKEN: + /* This is really EOF. */ + len = 0; + break; + case STATUS_MORE_ENTRIES: + case STATUS_BUFFER_OVERFLOW: + /* `io.Information' is supposedly valid. */ + len = io.Information; + break; + case STATUS_ACCESS_VIOLATION: + if (is_at_eof (get_handle ())) + { + len = 0; + break; + } + if (try_noreserve) + { + try_noreserve = 0; + switch (mmap_is_attached_or_noreserve (ptr, len)) + { + case MMAP_NORESERVE_COMMITED: + goto retry; + case MMAP_RAISE_SIGBUS: + raise(SIGBUS); + case MMAP_NONE: + break; + } + } + fallthrough; + case STATUS_INVALID_DEVICE_REQUEST: + case STATUS_INVALID_PARAMETER: + case STATUS_INVALID_HANDLE: + if (pc.isdir ()) + { + set_errno (EISDIR); + len = (size_t) -1; + break; + } + fallthrough; + default: + __seterrno_from_nt_status (status); + len = (size_t) -1; + break; + } + } +} + +/* Cover function to WriteFile to provide Posix interface and semantics + (as much as possible). */ +ssize_t +fhandler_base::raw_write (const void *ptr, size_t len) +{ + NTSTATUS status; + IO_STATUS_BLOCK io; + static _RDATA LARGE_INTEGER off_current = + { QuadPart:FILE_USE_FILE_POINTER_POSITION }; + static _RDATA LARGE_INTEGER off_append = + { QuadPart:FILE_WRITE_TO_END_OF_FILE }; + + status = NtWriteFile (get_output_handle (), NULL, NULL, NULL, &io, + (PVOID) ptr, len, + (get_flags () & O_APPEND) ? &off_append : &off_current, + NULL); + if (!NT_SUCCESS (status)) + { + __seterrno_from_nt_status (status); + if (get_errno () == EPIPE) + raise (SIGPIPE); + return -1; + } + return io.Information; +} + +int +fhandler_base::get_default_fmode (int flags) +{ + int fmode = __fmode; + if (perfile_table) + { + size_t nlen = strlen (get_name ()); + unsigned accflags = (flags & O_ACCMODE); + for (__cygwin_perfile *pf = perfile_table; pf->name; pf++) + if (!*pf->name && (pf->flags & O_ACCMODE) == accflags) + { + fmode = pf->flags & ~O_ACCMODE; + break; + } + else + { + size_t pflen = strlen (pf->name); + const char *stem = get_name () + nlen - pflen; + if (pflen > nlen || (stem != get_name () && !isdirsep (stem[-1]))) + continue; + else if ((pf->flags & O_ACCMODE) == accflags + && pathmatch (stem, pf->name, !!pc.objcaseinsensitive ())) + { + fmode = pf->flags & ~O_ACCMODE; + break; + } + } + } + return fmode; +} + +bool +fhandler_base::device_access_denied (int flags) +{ + int mode = 0; + + if (flags & O_PATH) + return false; + + if (flags & O_RDWR) + mode |= R_OK | W_OK; + if (flags & (O_WRONLY | O_APPEND)) + mode |= W_OK; + if (!mode) + mode |= R_OK; + + return fhaccess (mode, true); +} + +int +fhandler_base::fhaccess (int flags, bool effective) +{ + int res = -1; + if (error ()) + { + set_errno (error ()); + goto done; + } + + if (!exists ()) + { + set_errno (ENOENT); + goto done; + } + + if (!(flags & (R_OK | W_OK | X_OK))) + return 0; + + if (is_fs_special ()) + /* short circuit */; + else if (has_attribute (FILE_ATTRIBUTE_READONLY) && (flags & W_OK) + && !pc.isdir ()) + goto eaccess_done; + else if (has_acls ()) + { + res = check_file_access (pc, flags, effective); + goto done; + } + else if (get_device () == FH_REGISTRY && open (O_RDONLY, 0) && get_handle ()) + { + res = check_registry_access (get_handle (), flags, effective); + close (); + return res; + } + + struct stat st; + if (fstat (&st)) + goto done; + + if (flags & R_OK) + { + if (st.st_uid == (effective ? myself->uid : cygheap->user.real_uid)) + { + if (!(st.st_mode & S_IRUSR)) + goto eaccess_done; + } + else if (st.st_gid == (effective ? myself->gid : cygheap->user.real_gid)) + { + if (!(st.st_mode & S_IRGRP)) + goto eaccess_done; + } + else if (!(st.st_mode & S_IROTH)) + goto eaccess_done; + } + + if (flags & W_OK) + { + if (st.st_uid == (effective ? myself->uid : cygheap->user.real_uid)) + { + if (!(st.st_mode & S_IWUSR)) + goto eaccess_done; + } + else if (st.st_gid == (effective ? myself->gid : cygheap->user.real_gid)) + { + if (!(st.st_mode & S_IWGRP)) + goto eaccess_done; + } + else if (!(st.st_mode & S_IWOTH)) + goto eaccess_done; + } + + if (flags & X_OK) + { + if (st.st_uid == (effective ? myself->uid : cygheap->user.real_uid)) + { + if (!(st.st_mode & S_IXUSR)) + goto eaccess_done; + } + else if (st.st_gid == (effective ? myself->gid : cygheap->user.real_gid)) + { + if (!(st.st_mode & S_IXGRP)) + goto eaccess_done; + } + else if (!(st.st_mode & S_IXOTH)) + goto eaccess_done; + } + + res = 0; + goto done; + +eaccess_done: + set_errno (EACCES); +done: + if (!res && (flags & W_OK) && get_device () == FH_FS + && (pc.fs_flags () & FILE_READ_ONLY_VOLUME)) + { + set_errno (EROFS); + res = -1; + } + debug_printf ("returning %d", res); + return res; +} + +int +fhandler_base::open_with_arch (int flags, mode_t mode) +{ + int res; + if (!(res = (archetype && archetype->io_handle) + || open (flags, mode & 07777))) + { + if (archetype && archetype->usecount == 0) + cygheap->fdtab.delete_archetype (archetype); + } + else if (archetype) + { + if (!archetype->get_handle ()) + { + archetype->copy_from (this); + archetype_usecount (1); + archetype->archetype = NULL; + usecount = 0; + } + else + { + char *name; + /* Preserve any name (like /dev/tty) derived from build_fh_pc. */ + if (!get_name ()) + name = NULL; + else + { + name = (char *) alloca (strlen (get_name ()) + 1); + strcpy (name, get_name ()); + } + fhandler_base *arch = archetype; + copy_from (archetype); + if (name) + set_name (name); + archetype = arch; + archetype_usecount (1); + usecount = 0; + } + if (!open_setup (flags)) + api_fatal ("open_setup failed, %E"); + } + + close_on_exec (flags & O_CLOEXEC); + /* A unique ID is necessary to recognize fhandler entries which are + duplicated by dup(2) or fork(2). This is used in BSD flock calls + to identify the descriptor. Skip nohandle fhandlers since advisory + locking is unusable for those anyway. */ + if (!nohandle ()) + set_unique_id (); + return res; +} + +/* Open a fake handle to \\Device\\Null. This is a helper function for + fhandlers which just need some handle to keep track of BSD flock locks. */ +int +fhandler_base::open_null (int flags) +{ + int res = 0; + HANDLE fh; + OBJECT_ATTRIBUTES attr; + IO_STATUS_BLOCK io; + NTSTATUS status; + + InitializeObjectAttributes (&attr, &ro_u_null, OBJ_CASE_INSENSITIVE | + ((flags & O_CLOEXEC) ? 0 : OBJ_INHERIT), + NULL, NULL); + status = NtCreateFile (&fh, GENERIC_READ | SYNCHRONIZE, &attr, &io, NULL, 0, + FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN, + FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0); + if (!NT_SUCCESS (status)) + { + __seterrno_from_nt_status (status); + goto done; + } + set_handle (fh); + set_flags (flags, pc.binmode ()); + res = 1; + set_open_status (); +done: + debug_printf ("%y = NtCreateFile (%p, ... %S ...)", status, fh, &ro_u_null); + syscall_printf ("%d = fhandler_base::open_null (%y)", res, flags); + return res; +} + +/* Open system call handler function. */ +int +fhandler_base::open (int flags, mode_t mode) +{ + int res = 0; + HANDLE fh; + ULONG file_attributes = 0; + ULONG shared = (get_major () == DEV_TAPE_MAJOR ? 0 : FILE_SHARE_VALID_FLAGS); + ULONG create_disposition; + OBJECT_ATTRIBUTES attr; + IO_STATUS_BLOCK io; + NTSTATUS status; + PFILE_FULL_EA_INFORMATION p = NULL; + ULONG plen = 0; + + syscall_printf ("(%S, %y)%s", pc.get_nt_native_path (), flags, + get_handle () ? " by handle" : ""); + + if (flags & O_PATH) + query_open (query_read_attributes); + + /* Allow to reopen from handle. This is utilized by + open ("/proc/PID/fd/DESCRIPTOR", ...); */ + if (get_handle ()) + { + pc.init_reopen_attr (attr, get_handle ()); + if (!(flags & O_CLOEXEC)) + attr.Attributes |= OBJ_INHERIT; + if (pc.has_buggy_reopen ()) + debug_printf ("Reopen by handle requested but FS doesn't support it"); + } + else + pc.get_object_attr (attr, *sec_none_cloexec (flags)); + + options = FILE_OPEN_FOR_BACKUP_INTENT; + switch (query_open ()) + { + case query_read_control: + access = READ_CONTROL; + break; + case query_read_attributes: + access = READ_CONTROL | FILE_READ_ATTRIBUTES; + break; + case query_write_control: + access = READ_CONTROL | WRITE_OWNER | WRITE_DAC | FILE_WRITE_ATTRIBUTES; + break; + case query_write_dac: + access = READ_CONTROL | WRITE_DAC | FILE_WRITE_ATTRIBUTES; + break; + case query_write_attributes: + access = READ_CONTROL | FILE_WRITE_ATTRIBUTES; + break; + default: + switch (flags & O_ACCMODE) + { + case O_RDONLY: + access = GENERIC_READ; + break; + case O_WRONLY: + access = GENERIC_WRITE | READ_CONTROL | FILE_READ_ATTRIBUTES; + break; + default: + access = GENERIC_READ | GENERIC_WRITE; + break; + } + if (flags & O_SYNC) + options |= FILE_WRITE_THROUGH; + if (flags & O_DIRECT) + options |= FILE_NO_INTERMEDIATE_BUFFERING; + if (get_major () != DEV_SERIAL_MAJOR && get_major () != DEV_TAPE_MAJOR) + { + options |= FILE_SYNCHRONOUS_IO_NONALERT; + access |= SYNCHRONIZE; + } + break; + } + + /* Don't use the FILE_OVERWRITE{_IF} flags here. See below for an + explanation, why that's not such a good idea. */ + if (((flags & O_EXCL) && (flags & O_CREAT)) || (flags & O_TMPFILE)) + create_disposition = FILE_CREATE; + else + create_disposition = (flags & O_CREAT) ? FILE_OPEN_IF : FILE_OPEN; + + if (get_device () == FH_FS +#ifdef __WITH_AF_UNIX + || get_device () == FH_UNIX +#endif + ) + { + /* Add the reparse point flag to known reparse points, otherwise we + open the target, not the reparse point. This would break lstat. */ + if (pc.is_known_reparse_point ()) + options |= FILE_OPEN_REPARSE_POINT; + } + + if (get_device () == FH_FS) + { + /* O_TMPFILE files are created with delete-on-close semantics, as well + as with FILE_ATTRIBUTE_TEMPORARY. The latter speeds up file access, + because the OS tries to keep the file in memory as much as possible. + In conjunction with FILE_DELETE_ON_CLOSE, ideally the OS never has + to write to the disk at all. + Note that O_TMPFILE_FILE_ATTRS also sets the DOS HIDDEN attribute + to help telling Cygwin O_TMPFILE files apart from other files + accidentally setting FILE_ATTRIBUTE_TEMPORARY. */ + if (flags & O_TMPFILE) + { + access |= DELETE; + file_attributes |= O_TMPFILE_FILE_ATTRS; + options |= FILE_DELETE_ON_CLOSE; + } + + if (pc.fs_is_nfs ()) + { + /* Make sure we can read EAs of files on an NFS share. Also make + sure that we're going to act on the file itself, even if it's a + a symlink. */ + access |= FILE_READ_EA; + if (query_open ()) + { + if (query_open () >= query_write_control) + access |= FILE_WRITE_EA; + plen = sizeof nfs_aol_ffei; + p = (PFILE_FULL_EA_INFORMATION) &nfs_aol_ffei; + } + } + + if (flags & (O_CREAT | O_TMPFILE)) + { + file_attributes |= FILE_ATTRIBUTE_NORMAL; + + if (pc.fs_is_nfs ()) + { + /* When creating a file on an NFS share, we have to set the + file mode by writing a NFS fattr3 structure with the + correct mode bits set. */ + access |= FILE_WRITE_EA; + plen = sizeof (FILE_FULL_EA_INFORMATION) + sizeof (NFS_V3_ATTR) + + sizeof (fattr3); + p = (PFILE_FULL_EA_INFORMATION) alloca (plen); + p->NextEntryOffset = 0; + p->Flags = 0; + p->EaNameLength = sizeof (NFS_V3_ATTR) - 1; + p->EaValueLength = sizeof (fattr3); + strcpy (p->EaName, NFS_V3_ATTR); + fattr3 *nfs_attr = (fattr3 *) (p->EaName + + p->EaNameLength + 1); + memset (nfs_attr, 0, sizeof (fattr3)); + nfs_attr->type = NF3REG; + nfs_attr->mode = (mode & 07777) & ~cygheap->umask; + } + else if (!has_acls () + && !(mode & ~cygheap->umask & (S_IWUSR | S_IWGRP | S_IWOTH))) + /* If mode has no write bits set, and ACLs are not used, we set + the DOS R/O attribute. */ + file_attributes |= FILE_ATTRIBUTE_READONLY; + /* Never set the WRITE_DAC flag here. Calls to fstat may return + wrong st_ctime information after calls to fchmod, fchown, etc + because Windows only guarantees the update of metadata when + the handle is closed or flushed. However, flushing the file + on every fstat to enforce POSIXy stat behaviour is excessivly + slow, compared to an extra open/close to change the file's + security descriptor. */ + } + } + + status = NtCreateFile (&fh, access, &attr, &io, NULL, file_attributes, shared, + create_disposition, options, p, plen); + /* Pre-W10, we can't reopen a file by handle with delete disposition + set, so we have to lie our ass off. */ + if (get_handle () && status == STATUS_DELETE_PENDING) + { + BOOL ret = DuplicateHandle (GetCurrentProcess (), get_handle (), + GetCurrentProcess (), &fh, + access, !(flags & O_CLOEXEC), 0); + if (!ret) + ret = DuplicateHandle (GetCurrentProcess (), get_handle (), + GetCurrentProcess (), &fh, + 0, !(flags & O_CLOEXEC), + DUPLICATE_SAME_ACCESS); + if (!ret) + debug_printf ("DuplicateHandle after STATUS_DELETE_PENDING, %E"); + else + status = STATUS_SUCCESS; + } + if (!NT_SUCCESS (status)) + { + /* Trying to create a directory should return EISDIR, not ENOENT. */ + PUNICODE_STRING upath = pc.get_nt_native_path (); + if (status == STATUS_OBJECT_NAME_INVALID && (flags & O_CREAT) + && upath->Buffer[upath->Length / sizeof (WCHAR) - 1] == '\\') + set_errno (EISDIR); + else + __seterrno_from_nt_status (status); + if (!nohandle ()) + goto done; + } + + if (io.Information == FILE_CREATED) + { + /* Correct file attributes are needed for later use in, e.g. fchmod. */ + FILE_BASIC_INFORMATION fbi; + + if (!NT_SUCCESS (NtQueryInformationFile (fh, &io, &fbi, sizeof fbi, + FileBasicInformation))) + fbi.FileAttributes = file_attributes | FILE_ATTRIBUTE_ARCHIVE; + pc.file_attributes (fbi.FileAttributes); + + /* Always create files using a NULL SD. Create correct permission bits + afterwards, maintaining the owner and group information just like + chmod. This is done for two reasons. + + On Windows filesystems we need to create the file with default + permissions to allow inheriting ACEs. When providing an explicit DACL + in calls to [Nt]CreateFile, the created file will not inherit default + permissions from the parent object. This breaks not only Windows + inheritance, but also POSIX ACL inheritance. + + Another reason to do this are remote shares. Files on a remote share + are created as the user used for authentication. In a domain that's + usually the user you're logged in as. Outside of a domain you're + authenticating using a local user account on the sharing machine. + If the SIDs of the client machine are used, that's entirely unexpected + behaviour. Doing it like we do here creates the expected SD in a + domain as well as on standalone servers. This is the result of a + discussion on the samba-technical list, starting at + http://lists.samba.org/archive/samba-technical/2008-July/060247.html */ + if (has_acls ()) + set_created_file_access (fh, pc, mode); + } + + /* If you O_TRUNC a file on Linux, the data is truncated, but the EAs are + preserved. If you open a file on Windows with FILE_OVERWRITE{_IF} or + FILE_SUPERSEDE, all streams are truncated, including the EAs. So we don't + use the FILE_OVERWRITE{_IF} flags, but instead just open the file and set + the size of the data stream explicitely to 0. Apart from being more Linux + compatible, this implementation has the pleasant side-effect to be more + than 5% faster than using FILE_OVERWRITE{_IF} (tested on W7 32 bit). */ + if ((flags & O_TRUNC) + && (flags & O_ACCMODE) != O_RDONLY + && io.Information != FILE_CREATED + && get_device () == FH_FS) + { + FILE_END_OF_FILE_INFORMATION feofi = { EndOfFile:{ QuadPart:0 } }; + status = NtSetInformationFile (fh, &io, &feofi, sizeof feofi, + FileEndOfFileInformation); + /* In theory, truncating the file should never fail, since the opened + handle has FILE_WRITE_DATA permissions, which is all you need to + be allowed to truncate a file. Better safe than sorry. */ + if (!NT_SUCCESS (status)) + { + __seterrno_from_nt_status (status); + NtClose (fh); + goto done; + } + } + + set_handle (fh); + set_flags (flags, pc.binmode ()); + + res = 1; + set_open_status (); +done: + debug_printf ("%y = NtCreateFile " + "(%p, %y, %S, io, NULL, %y, %y, %y, %y, NULL, 0)", + status, fh, access, pc.get_nt_native_path (), file_attributes, + shared, create_disposition, options); + + syscall_printf ("%d = fhandler_base::open(%S, %y)", + res, pc.get_nt_native_path (), flags); + return res; +} + +fhandler_base * +fhandler_base::fd_reopen (int, mode_t) +{ + /* This is implemented in fhandler_process only. */ + return NULL; +} + +bool +fhandler_base::open_setup (int) +{ + return true; +} + +/* states: + open buffer in binary mode? Just do the read. + + open buffer in text mode? Scan buffer for control zs and handle + the first one found. Then scan buffer, converting every \r\n into + an \n. If last char is an \r, look ahead one more char, if \n then + modify \r, if not, remember char. +*/ +void +fhandler_base::read (void *in_ptr, size_t& len) +{ + char *ptr = (char *) in_ptr; + ssize_t copied_chars = get_readahead_into_buffer (ptr, len); + + if (copied_chars || !len) + { + len = (size_t) copied_chars; + goto out; + } + + raw_read (ptr, len); + + if (rbinary () || (ssize_t) len <= 0) + goto out; + + /* Scan buffer and turn \r\n into \n */ + char *src, *dst, *end; + src = (char *) ptr; + dst = (char *) ptr; + end = src + len - 1; + + /* Read up to the last but one char - the last char needs special handling */ + while (src < end) + { + if (*src == '\r' && src[1] == '\n') + src++; + *dst++ = *src++; + } + + /* If not beyond end and last char is a '\r' then read one more + to see if we should translate this one too */ + if (src > end) + /* nothing */; + else if (*src != '\r') + *dst++ = *src; + else + { + char c1; + size_t c1len = 1; + raw_read (&c1, c1len); + if (c1len <= 0) + /* nothing */; + else if (c1 == '\n') + *dst++ = '\n'; + else + { + set_readahead_valid (1, c1); + *dst++ = *src; + } + } + + len = dst - (char *) ptr; + +out: + debug_printf ("returning %d, %s mode", len, rbinary () ? "binary" : "text"); +} + +ssize_t +fhandler_base::write (const void *ptr, size_t len) +{ + ssize_t res; + + if (did_lseek ()) + { + IO_STATUS_BLOCK io; + FILE_POSITION_INFORMATION fpi; + FILE_STANDARD_INFORMATION fsi; + + did_lseek (false); /* don't do it again */ + + if (!(get_flags () & O_APPEND) + && !has_attribute (FILE_ATTRIBUTE_SPARSE_FILE) + && NT_SUCCESS (NtQueryInformationFile (get_output_handle (), + &io, &fsi, sizeof fsi, + FileStandardInformation)) + && NT_SUCCESS (NtQueryInformationFile (get_output_handle (), + &io, &fpi, sizeof fpi, + FilePositionInformation)) + && fpi.CurrentByteOffset.QuadPart + >= fsi.EndOfFile.QuadPart + (128 * 1024)) + { + /* If the file system supports sparse files and the application + is writing after a long seek beyond EOF, convert the file to + a sparse file. */ + NTSTATUS status; + status = NtFsControlFile (get_output_handle (), NULL, NULL, NULL, + &io, FSCTL_SET_SPARSE, NULL, 0, NULL, 0); + if (NT_SUCCESS (status)) + pc.file_attributes (pc.file_attributes () + | FILE_ATTRIBUTE_SPARSE_FILE); + debug_printf ("%y = NtFsControlFile(%S, FSCTL_SET_SPARSE)", + status, pc.get_nt_native_path ()); + } + } + + if (wbinary ()) + res = raw_write (ptr, len); + else + { + debug_printf ("text write"); + /* This is the Microsoft/DJGPP way. Still not ideal, but it's + compatible. + Modified slightly by CGF 2000-10-07 */ + + int left_in_data = len; + char *data = (char *)ptr; + res = 0; + + while (left_in_data > 0) + { + char buf[CHUNK_SIZE + 1], *buf_ptr = buf; + int left_in_buf = CHUNK_SIZE; + + while (left_in_buf > 0 && left_in_data > 0) + { + char ch = *data++; + if (ch == '\n') + { + *buf_ptr++ = '\r'; + left_in_buf--; + } + *buf_ptr++ = ch; + left_in_buf--; + left_in_data--; + if (left_in_data > 0 && ch == '\r' && *data == '\n') + { + *buf_ptr++ = *data++; + left_in_buf--; + left_in_data--; + } + } + + /* We've got a buffer-full, or we're out of data. Write it out */ + ssize_t nbytes; + ptrdiff_t want = buf_ptr - buf; + if ((nbytes = raw_write (buf, (size_t) want)) == want) + { + /* Keep track of how much written not counting additional \r's */ + res = data - (char *)ptr; + continue; + } + + if (nbytes == -1) + res = -1; /* Error */ + else + res += nbytes; /* Partial write. Return total bytes written. */ + break; /* All done */ + } + } + + return res; +} + +ssize_t +fhandler_base::readv (const struct iovec *const iov, const int iovcnt, + ssize_t tot) +{ + assert (iov); + assert (iovcnt >= 1); + + size_t len = tot; + if (iovcnt == 1) + { + len = iov->iov_len; + read (iov->iov_base, len); + return len; + } + + if (tot == -1) // i.e. if not pre-calculated by the caller. + { + len = 0; + const struct iovec *iovptr = iov + iovcnt; + do + { + iovptr -= 1; + len += iovptr->iov_len; + } + while (iovptr != iov); + } + + if (!len) + return 0; + + char *buf = (char *) malloc (len); + + if (!buf) + { + set_errno (ENOMEM); + return -1; + } + + read (buf, len); + ssize_t nbytes = (ssize_t) len; + + const struct iovec *iovptr = iov; + + char *p = buf; + while (nbytes > 0) + { + const int frag = MIN (nbytes, (ssize_t) iovptr->iov_len); + memcpy (iovptr->iov_base, p, frag); + p += frag; + iovptr += 1; + nbytes -= frag; + } + + free (buf); + return len; +} + +ssize_t +fhandler_base::writev (const struct iovec *const iov, const int iovcnt, + ssize_t tot) +{ + assert (iov); + assert (iovcnt >= 1); + + if (iovcnt == 1) + return write (iov->iov_base, iov->iov_len); + + if (tot == -1) // i.e. if not pre-calculated by the caller. + { + tot = 0; + const struct iovec *iovptr = iov + iovcnt; + do + { + iovptr -= 1; + tot += iovptr->iov_len; + } + while (iovptr != iov); + } + + assert (tot >= 0); + + if (tot == 0) + return 0; + + char *const buf = (char *) malloc (tot); + + if (!buf) + { + set_errno (ENOMEM); + return -1; + } + + char *bufptr = buf; + const struct iovec *iovptr = iov; + int nbytes = tot; + + while (nbytes != 0) + { + const int frag = MIN (nbytes, (ssize_t) iovptr->iov_len); + memcpy (bufptr, iovptr->iov_base, frag); + bufptr += frag; + iovptr += 1; + nbytes -= frag; + } + ssize_t ret = write (buf, tot); + free (buf); + return ret; +} + +off_t +fhandler_base::lseek (off_t offset, int whence) +{ + NTSTATUS status; + IO_STATUS_BLOCK io; + FILE_POSITION_INFORMATION fpi; + FILE_STANDARD_INFORMATION fsi; + + /* Seeks on text files is tough, we rewind and read till we get to the + right place. */ + + if (whence != SEEK_CUR || offset != 0) + { + if (whence == SEEK_CUR) + offset -= ralen () - raixget (); + set_readahead_valid (0); + } + + switch (whence) + { + case SEEK_SET: + fpi.CurrentByteOffset.QuadPart = offset; + break; + case SEEK_CUR: + status = NtQueryInformationFile (get_handle (), &io, &fpi, sizeof fpi, + FilePositionInformation); + if (!NT_SUCCESS (status)) + { + __seterrno_from_nt_status (status); + return -1; + } + fpi.CurrentByteOffset.QuadPart += offset; + break; + default: /* SEEK_END */ + status = NtQueryInformationFile (get_handle (), &io, &fsi, sizeof fsi, + FileStandardInformation); + if (!NT_SUCCESS (status)) + { + __seterrno_from_nt_status (status); + return -1; + } + fpi.CurrentByteOffset.QuadPart = fsi.EndOfFile.QuadPart + offset; + break; + } + + debug_printf ("setting file pointer to %U", fpi.CurrentByteOffset.QuadPart); + status = NtSetInformationFile (get_handle (), &io, &fpi, sizeof fpi, + FilePositionInformation); + if (!NT_SUCCESS (status)) + { + __seterrno_from_nt_status (status); + return -1; + } + off_t res = fpi.CurrentByteOffset.QuadPart; + + /* When next we write(), we will check to see if *this* seek went beyond + the end of the file and if so, potentially sparsify the file. */ + if (pc.support_sparse ()) + did_lseek (true); + + /* If this was a SEEK_CUR with offset 0, we still might have + readahead that we have to take into account when calculating + the actual position for the application. */ + if (whence == SEEK_CUR) + res -= ralen () - raixget (); + + return res; +} + +ssize_t +fhandler_base::pread (void *, size_t, off_t, void *) +{ + set_errno (ESPIPE); + return -1; +} + +ssize_t +fhandler_base::pwrite (void *, size_t, off_t, void *) +{ + set_errno (ESPIPE); + return -1; +} + +int +fhandler_base::close_with_arch () +{ + int res; + fhandler_base *fh; + if (usecount) + { + /* This was the archetype itself. */ + if (--usecount) + { + debug_printf ("not closing passed in archetype %p, usecount %d", archetype, usecount); + return 0; + } + debug_printf ("closing passed in archetype %p, usecount %d", archetype, usecount); + /* Set archetype temporarily so that it will eventually be deleted. */ + archetype = fh = this; + } + else if (!archetype) + fh = this; + else if (archetype_usecount (-1) == 0) + { + debug_printf ("closing archetype"); + fh = archetype; + } + else + { + debug_printf ("not closing archetype"); + return 0; + } + + cleanup (); + res = fh->close (); + if (archetype) + { + cygheap->fdtab.delete_archetype (archetype); + archetype = NULL; + } + return res; +} + +void +fhandler_base::cleanup () +{ + /* Delete all POSIX locks on the file. Delete all flock locks on the + file if this is the last reference to this file. */ + if (unique_id) + del_my_locks (on_close); +} + +int +fhandler_base::close () +{ + int res = -1; + + syscall_printf ("closing '%s' handle %p", get_name (), get_handle ()); + if (nohandle () || CloseHandle (get_handle ())) + res = 0; + else + { + paranoid_printf ("CloseHandle failed, %E"); + __seterrno (); + } + return res; +} + +int +fhandler_base::ioctl (unsigned int cmd, void *buf) +{ + int res; + + switch (cmd) + { + case FIONBIO: + set_nonblocking (*(int *) buf); + res = 0; + break; + case FIONREAD: + case TIOCSCTTY: + set_errno (ENOTTY); + res = -1; + break; + default: + set_errno (EINVAL); + res = -1; + break; + } + + syscall_printf ("%d = ioctl(%x, %p)", res, cmd, buf); + return res; +} + +int +fhandler_base::fstat (struct stat *buf) +{ + if (is_fs_special ()) + return fstat_fs (buf); + + switch (get_device ()) + { + case FH_PIPE: + buf->st_mode = S_IFIFO | S_IRUSR | S_IWUSR; + break; + case FH_PIPEW: + buf->st_mode = S_IFIFO | S_IWUSR; + break; + case FH_PIPER: + buf->st_mode = S_IFIFO | S_IRUSR; + break; + default: + buf->st_mode = S_IFCHR | STD_RBITS | STD_WBITS | S_IWGRP | S_IWOTH; + break; + } + + buf->st_uid = geteuid (); + buf->st_gid = getegid (); + buf->st_nlink = 1; + buf->st_blksize = PREFERRED_IO_BLKSIZE; + + buf->st_ctim.tv_sec = 1164931200L; /* Arbitrary value: 2006-12-01 */ + buf->st_ctim.tv_nsec = 0L; + buf->st_birthtim = buf->st_ctim; + buf->st_mtim.tv_sec = time (NULL); /* Arbitrary value: current time, + like Linux */ + buf->st_mtim.tv_nsec = 0L; + buf->st_atim = buf->st_mtim; + + return 0; +} + +int +fhandler_base::fstatvfs (struct statvfs *sfs) +{ + /* If we hit this base implementation, it's some device in /dev. + Just call statvfs on /dev for simplicity. */ + path_conv pc ("/dev", PC_KEEP_HANDLE); + fhandler_disk_file fh (pc); + return fh.fstatvfs (sfs); +} + +int +fhandler_base::init (HANDLE f, DWORD a, mode_t bin) +{ + set_handle (f); + access = a; + a &= GENERIC_READ | GENERIC_WRITE; + int flags = 0; + if (a == GENERIC_READ) + flags = O_RDONLY; + else if (a == GENERIC_WRITE) + flags = O_WRONLY; + else if (a == (GENERIC_READ | GENERIC_WRITE)) + flags = O_RDWR; + set_flags (flags | bin); + set_open_status (); + debug_printf ("created new fhandler_base for handle %p, bin %d", f, rbinary ()); + return 1; +} + +int +fhandler_base::dup (fhandler_base *child, int flags) +{ + debug_printf ("in fhandler_base dup"); + + HANDLE nh; + if (!nohandle () && !archetype) + { + if (!DuplicateHandle (GetCurrentProcess (), get_handle (), + GetCurrentProcess (), &nh, + 0, !(flags & O_CLOEXEC), DUPLICATE_SAME_ACCESS)) + { + debug_printf ("dup(%s) failed, handle %p, %E", + get_name (), get_handle ()); + __seterrno (); + return -1; + } + + VerifyHandle (nh); + child->set_handle (nh); + } + return 0; +} + +int fhandler_base::fcntl (int cmd, intptr_t arg) +{ + int res; + + switch (cmd) + { + case F_GETFD: + res = close_on_exec () ? FD_CLOEXEC : 0; + break; + case F_SETFD: + set_close_on_exec ((arg & FD_CLOEXEC) ? 1 : 0); + res = 0; + break; + case F_GETFL: + res = get_flags (); + debug_printf ("GETFL: %y", res); + break; + case F_SETFL: + { + /* Only O_APPEND, O_ASYNC and O_NONBLOCK are allowed. + Each other flag will be ignored. + Since O_ASYNC isn't defined in fcntl.h it's currently + ignored as well. */ + const int allowed_flags = O_APPEND | O_NONBLOCK; + int new_flags = arg & allowed_flags; + set_flags ((get_flags () & ~allowed_flags) | new_flags); + } + res = 0; + break; + case F_GETLK: + case F_SETLK: + case F_SETLKW: + { + struct flock *fl = (struct flock *) arg; + fl->l_type &= F_RDLCK | F_WRLCK | F_UNLCK; + res = mandatory_locking () ? mand_lock (cmd, fl) : lock (cmd, fl); + } + break; + default: + set_errno (EINVAL); + res = -1; + break; + } + return res; +} + +/* Base terminal handlers. These just return errors. */ + +int +fhandler_base::tcflush (int) +{ + set_errno (ENOTTY); + return -1; +} + +int +fhandler_base::tcsendbreak (int) +{ + set_errno (ENOTTY); + return -1; +} + +int +fhandler_base::tcdrain () +{ + set_errno (ENOTTY); + return -1; +} + +int +fhandler_base::tcflow (int) +{ + set_errno (ENOTTY); + return -1; +} + +int +fhandler_base::tcsetattr (int, const struct termios *) +{ + set_errno (ENOTTY); + return -1; +} + +int +fhandler_base::tcgetattr (struct termios *) +{ + set_errno (ENOTTY); + return -1; +} + +int +fhandler_base::tcsetpgrp (const pid_t) +{ + set_errno (ENOTTY); + return -1; +} + +int +fhandler_base::tcgetpgrp () +{ + set_errno (ENOTTY); + return -1; +} + +pid_t +fhandler_base::tcgetsid () +{ + set_errno (ENOTTY); + return -1; +} + +int +fhandler_base::ptsname_r (char *, size_t) +{ + set_errno (ENOTTY); + return ENOTTY; +} + +/* Normal I/O constructor */ +fhandler_base::fhandler_base () : + status (), + open_status (), + access (0), + io_handle (NULL), + ino (0), + _refcnt (0), + openflags (0), + unique_id (0), + select_sem (NULL), + archetype (NULL), + usecount (0) +{ + ra.rabuf = NULL; + ra.ralen = 0; + ra.raixget = 0; + ra.raixput = 0; + ra.rabuflen = 0; + isclosed (false); +} + +/* Normal I/O destructor */ +fhandler_base::~fhandler_base () +{ + if (ra.rabuf) + free (ra.rabuf); +} + +void +fhandler_base::set_no_inheritance (HANDLE &h, bool not_inheriting) +{ + if (!SetHandleInformation (h, HANDLE_FLAG_INHERIT, + not_inheriting ? 0 : HANDLE_FLAG_INHERIT)) + debug_printf ("SetHandleInformation failed, %E"); +#ifdef DEBUGGING_AND_FDS_PROTECTED + if (h) + setclexec (oh, h, not_inheriting); +#endif +} + +bool +fhandler_base::fork_fixup (HANDLE parent, HANDLE &h, const char *name) +{ + HANDLE oh = h; + bool res = false; + if (!close_on_exec ()) + debug_printf ("handle %p already opened", h); + else if (!DuplicateHandle (parent, h, GetCurrentProcess (), &h, + 0, !close_on_exec (), DUPLICATE_SAME_ACCESS)) + system_printf ("%s - %E, handle %s<%p>", get_name (), name, h); + else + { + if (oh != h) + VerifyHandle (h); + res = true; + } + return res; +} + +void +fhandler_base::set_close_on_exec (bool val) +{ + if (!nohandle ()) + set_no_inheritance (io_handle, val); + close_on_exec (val); + debug_printf ("set close_on_exec for %s to %d", get_name (), val); +} + +void +fhandler_base::fixup_after_fork (HANDLE parent) +{ + debug_printf ("inheriting '%s' from parent", get_name ()); + if (!nohandle ()) + fork_fixup (parent, io_handle, "io_handle"); + /* POSIX locks are not inherited across fork. */ + if (unique_id) + del_my_locks (after_fork); +} + +void +fhandler_base::fixup_after_exec () +{ + debug_printf ("here for '%s'", get_name ()); + if (unique_id && close_on_exec ()) + del_my_locks (after_exec); + mandatory_locking (false); +} + +bool +fhandler_base::is_nonblocking () +{ + return (openflags & O_NONBLOCK) != 0; +} + +void +fhandler_base::set_nonblocking (int yes) +{ + int current = openflags & O_NONBLOCK; + int new_flags = yes ? (!current ? O_NONBLOCK : current) : 0; + openflags = (openflags & ~O_NONBLOCK) | new_flags; +} + +int +fhandler_base::mkdir (mode_t) +{ + if (exists ()) + set_errno (EEXIST); + else + set_errno (EROFS); + return -1; +} + +int +fhandler_base::rmdir () +{ + if (!exists ()) + set_errno (ENOENT); + else if (!pc.isdir ()) + set_errno (ENOTDIR); + else + set_errno (EROFS); + return -1; +} + +DIR * +fhandler_base::opendir (int fd) +{ + set_errno (ENOTDIR); + return NULL; +} + +int +fhandler_base::readdir (DIR *, dirent *) +{ + return ENOTDIR; +} + +long +fhandler_base::telldir (DIR *) +{ + set_errno (ENOTDIR); + return -1; +} + +void +fhandler_base::seekdir (DIR *, long) +{ + set_errno (ENOTDIR); +} + +void +fhandler_base::rewinddir (DIR *) +{ + set_errno (ENOTDIR); +} + +int +fhandler_base::closedir (DIR *) +{ + set_errno (ENOTDIR); + return -1; +} + +int +fhandler_base::fchmod (mode_t mode) +{ + if (pc.is_fs_special ()) + return chmod_device (pc, mode); + /* By default, just succeeds. */ + return 0; +} + +int +fhandler_base::fchown (uid_t uid, gid_t gid) +{ + if (pc.is_fs_special ()) + return ((fhandler_disk_file *) this)->fhandler_disk_file::fchown (uid, gid); + /* By default, just succeeds. */ + return 0; +} + +int +fhandler_base::facl (int cmd, int nentries, aclent_t *aclbufp) +{ + int res = -1; + switch (cmd) + { + case SETACL: + /* By default, just succeeds. */ + res = 0; + break; + case GETACL: + if (!aclbufp) + set_errno(EFAULT); + else if (nentries < MIN_ACL_ENTRIES) + set_errno (ENOSPC); + else + { + aclbufp[0].a_type = USER_OBJ; + aclbufp[0].a_id = myself->uid; + aclbufp[0].a_perm = (S_IRUSR | S_IWUSR) >> 6; + aclbufp[1].a_type = GROUP_OBJ; + aclbufp[1].a_id = myself->gid; + aclbufp[1].a_perm = (S_IRGRP | S_IWGRP) >> 3; + aclbufp[2].a_type = OTHER_OBJ; + aclbufp[2].a_id = ILLEGAL_GID; + aclbufp[2].a_perm = S_IROTH | S_IWOTH; + res = MIN_ACL_ENTRIES; + } + break; + case GETACLCNT: + res = MIN_ACL_ENTRIES; + break; + default: + set_errno (EINVAL); + break; + } + return res; +} + +ssize_t +fhandler_base::fgetxattr (const char *name, void *value, size_t size) +{ + set_errno (ENOTSUP); + return -1; +} + +int +fhandler_base::fsetxattr (const char *name, const void *value, size_t size, + int flags) +{ + set_errno (ENOTSUP); + return -1; +} + +int +fhandler_base::fadvise (off_t offset, off_t length, int advice) +{ + set_errno (EINVAL); + return -1; +} + +int +fhandler_base::ftruncate (off_t length, bool allow_truncate) +{ + return EINVAL; +} + +int +fhandler_base::link (const char *newpath) +{ + set_errno (EPERM); + return -1; +} + +int +fhandler_base::utimens (const struct timespec *tvp) +{ + if (is_fs_special ()) + return utimens_fs (tvp); + + set_errno (EINVAL); + return -1; +} + +int +fhandler_base::fsync () +{ + if (!get_handle () || nohandle () || pc.isspecial ()) + { + set_errno (EINVAL); + return -1; + } + if (pc.isdir ()) /* Just succeed. */ + return 0; + if (FlushFileBuffers (get_handle ())) + return 0; + + /* Ignore ERROR_INVALID_FUNCTION because FlushFileBuffers() always fails + with this code on raw devices which are unbuffered by default. */ + DWORD errcode = GetLastError(); + if (errcode == ERROR_INVALID_FUNCTION) + return 0; + + __seterrno_from_win_error (errcode); + return -1; +} + +int +fhandler_base::fpathconf (int v) +{ + int ret; + + switch (v) + { + case _PC_LINK_MAX: + return pc.fs_is_ntfs () || pc.fs_is_samba () || pc.fs_is_nfs () + ? LINK_MAX : 1; + case _PC_MAX_CANON: + if (is_tty ()) + return MAX_CANON; + set_errno (EINVAL); + break; + case _PC_MAX_INPUT: + if (is_tty ()) + return MAX_INPUT; + set_errno (EINVAL); + break; + case _PC_NAME_MAX: + /* NAME_MAX is without trailing \0 */ + if (!pc.isdir ()) + return NAME_MAX; + ret = NT_MAX_PATH - strlen (get_name ()) - 2; + return ret < 0 ? 0 : ret > NAME_MAX ? NAME_MAX : ret; + case _PC_PATH_MAX: + /* PATH_MAX is with trailing \0 */ + if (!pc.isdir ()) + return PATH_MAX; + ret = NT_MAX_PATH - strlen (get_name ()) - 1; + return ret < 0 ? 0 : ret > PATH_MAX ? PATH_MAX : ret; + case _PC_PIPE_BUF: + if (pc.isdir () + || get_device () == FH_FIFO || get_device () == FH_PIPE + || get_device () == FH_PIPER || get_device () == FH_PIPEW) + return PIPE_BUF; + set_errno (EINVAL); + break; + case _PC_CHOWN_RESTRICTED: + return 1; + case _PC_NO_TRUNC: + return 1; + case _PC_VDISABLE: + if (is_tty ()) + return _POSIX_VDISABLE; + set_errno (EINVAL); + break; + case _PC_ASYNC_IO: + return 1; + case _PC_PRIO_IO: + break; + case _PC_SYNC_IO: + return 1; + case _PC_FILESIZEBITS: + return FILESIZEBITS; + case _PC_2_SYMLINKS: + return 1; + case _PC_SYMLINK_MAX: + return SYMLINK_MAX; + case _PC_POSIX_PERMISSIONS: + case _PC_POSIX_SECURITY: + if (get_device () == FH_FS) + return pc.has_acls () || pc.fs_is_nfs (); + set_errno (EINVAL); + break; + case _PC_CASE_INSENSITIVE: + return !!pc.objcaseinsensitive (); + default: + set_errno (EINVAL); + break; + } + return -1; +} + +NTSTATUS +fhandler_base::npfs_handle (HANDLE &nph) +{ + static NO_COPY SRWLOCK npfs_lock; + static NO_COPY HANDLE npfs_dirh; + + NTSTATUS status = STATUS_SUCCESS; + OBJECT_ATTRIBUTES attr; + IO_STATUS_BLOCK io; + + /* Lockless after first call. */ + if (npfs_dirh) + { + nph = npfs_dirh; + return STATUS_SUCCESS; + } + AcquireSRWLockExclusive (&npfs_lock); + if (!npfs_dirh) + { + InitializeObjectAttributes (&attr, &ro_u_npfs, 0, NULL, NULL); + status = NtOpenFile (&npfs_dirh, FILE_READ_ATTRIBUTES | SYNCHRONIZE, + &attr, &io, FILE_SHARE_READ | FILE_SHARE_WRITE, + 0); + } + ReleaseSRWLockExclusive (&npfs_lock); + if (NT_SUCCESS (status)) + nph = npfs_dirh; + return status; +} |