Welcome to mirror list, hosted at ThFree Co, Russian Federation.

cygwin.com/git/newlib-cygwin.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'winsup/cygwin/fhandler/base.cc')
-rw-r--r--winsup/cygwin/fhandler/base.cc1854
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;
+}