static int winfs_link(struct mount_point *mp, struct file *f, const char *newpath) { AcquireSRWLockShared(&f->rw_lock); struct winfs_file *winfile = (struct winfs_file *) f; NTSTATUS status; int r = 0; char buf[sizeof(FILE_LINK_INFORMATION) + PATH_MAX * 2]; FILE_LINK_INFORMATION *info = (FILE_LINK_INFORMATION *)buf; info->ReplaceIfExists = FALSE; info->RootDirectory = NULL; info->FileNameLength = 2 * filename_to_nt_pathname(mp, newpath, info->FileName, PATH_MAX); if (info->FileNameLength == 0) { r = -L_ENOENT; goto out; } IO_STATUS_BLOCK status_block; status = NtSetInformationFile(winfile->handle, &status_block, info, info->FileNameLength + sizeof(FILE_LINK_INFORMATION), FileLinkInformation); if (!NT_SUCCESS(status)) { log_warning("NtSetInformationFile() failed, status: %x.", status); r = -L_ENOENT; goto out; } out: ReleaseSRWLockShared(&f->rw_lock); return r; }
static int winfs_unlink(struct mount_point *mp, const char *pathname) { WCHAR wpathname[PATH_MAX]; int len = filename_to_nt_pathname(mp, pathname, wpathname, PATH_MAX); if (len <= 0) return -L_ENOENT; UNICODE_STRING object_name; RtlInitCountedUnicodeString(&object_name, wpathname, len * sizeof(WCHAR)); OBJECT_ATTRIBUTES attr; attr.Length = sizeof(OBJECT_ATTRIBUTES); attr.RootDirectory = NULL; attr.ObjectName = &object_name; attr.Attributes = 0; attr.SecurityDescriptor = NULL; attr.SecurityQualityOfService = NULL; IO_STATUS_BLOCK status_block; NTSTATUS status; HANDLE handle; status = NtOpenFile(&handle, DELETE, &attr, &status_block, FILE_SHARE_DELETE, FILE_NON_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT); if (!NT_SUCCESS(status)) { if (status != STATUS_SHARING_VIOLATION) { log_warning("NtOpenFile() failed, status: %x", status); return -L_ENOENT; } /* This file has open handles in some processes, even we set delete disposition flags * The actual deletion of the file will be delayed to the last handle closing * To make the file disappear from its parent directory immediately, we move the file * to Windows recycle bin prior to deletion. */ status = NtOpenFile(&handle, DELETE, &attr, &status_block, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_NON_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT); if (!NT_SUCCESS(status)) { log_warning("NtOpenFile() failed, status: %x", status); return -L_EBUSY; } status = move_to_recycle_bin(handle, wpathname); if (!NT_SUCCESS(status)) return -L_EBUSY; } /* Set disposition flag */ FILE_DISPOSITION_INFORMATION info; info.DeleteFile = TRUE; status = NtSetInformationFile(handle, &status_block, &info, sizeof(info), FileDispositionInformation); if (!NT_SUCCESS(status)) { log_warning("NtSetInformation(FileDispositionInformation) failed, status: %x", status); return -L_EBUSY; } NtClose(handle); return 0; }
static int winfs_symlink(struct mount_point *mp, const char *target, const char *linkpath) { WCHAR wlinkpath[PATH_MAX]; int len = filename_to_nt_pathname(mp, linkpath, wlinkpath, PATH_MAX); if (len <= 0) return -L_ENOENT; UNICODE_STRING pathname; RtlInitCountedUnicodeString(&pathname, wlinkpath, len * sizeof(WCHAR)); IO_STATUS_BLOCK status_block; OBJECT_ATTRIBUTES attr; attr.Length = sizeof(OBJECT_ATTRIBUTES); attr.RootDirectory = NULL; attr.ObjectName = &pathname; attr.Attributes = 0; attr.SecurityDescriptor = NULL; attr.SecurityQualityOfService = NULL; HANDLE handle; NTSTATUS status = NtCreateFile(&handle, SYNCHRONIZE | FILE_WRITE_DATA, &attr, &status_block, NULL, FILE_ATTRIBUTE_SYSTEM, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_CREATE, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0); if (!NT_SUCCESS(status)) { if (status == STATUS_OBJECT_NAME_EXISTS || status == STATUS_OBJECT_NAME_COLLISION) { log_warning("File already exists."); return -L_EEXIST; } log_warning("NtCreateFile() failed, status: %x", status); return -L_ENOENT; } DWORD num_written; if (!WriteFile(handle, WINFS_SYMLINK_HEADER, WINFS_SYMLINK_HEADER_LEN, &num_written, NULL) || num_written < WINFS_SYMLINK_HEADER_LEN) { log_warning("WriteFile() failed, error code: %d.", GetLastError()); NtClose(handle); return -L_EIO; } DWORD targetlen = strlen(target); if (!WriteFile(handle, target, targetlen, &num_written, NULL) || num_written < targetlen) { log_warning("WriteFile() failed, error code: %d.", GetLastError()); NtClose(handle); return -L_EIO; } NtClose(handle); return 0; }
static int winfs_rename(struct mount_point *mp, struct file *f, const char *newpath) { AcquireSRWLockShared(&f->rw_lock); struct winfs_file *winfile = (struct winfs_file *)f; char buf[sizeof(FILE_RENAME_INFORMATION) + PATH_MAX * 2]; NTSTATUS status; int r = 0; int retry_count = 5; retry: if (--retry_count == 0) { r = -L_EPERM; goto out; } FILE_RENAME_INFORMATION *info = (FILE_RENAME_INFORMATION *)buf; info->ReplaceIfExists = TRUE; info->RootDirectory = NULL; info->FileNameLength = 2 * filename_to_nt_pathname(mp, newpath, info->FileName, PATH_MAX); if (info->FileNameLength == 0) { r = -L_ENOENT; goto out; } IO_STATUS_BLOCK status_block; status = NtSetInformationFile(winfile->handle, &status_block, info, info->FileNameLength + sizeof(FILE_RENAME_INFORMATION), FileRenameInformation); if (!NT_SUCCESS(status)) { if (status == STATUS_ACCESS_DENIED) { /* The destination exists and the operation cannot be completed via a native operation. * We remove the destination file first, then move this file again. */ r = winfs_unlink(mp, newpath); if (r) goto out; goto retry; } log_warning("NtSetInformationFile() failed, status: %x", status); r = -L_ENOENT; goto out; } out: ReleaseSRWLockShared(&f->rw_lock); return r; }
static int winfs_rename(struct file *f, const char *newpath) { struct winfs_file *winfile = (struct winfs_file *)f; char buf[sizeof(FILE_RENAME_INFORMATION) + PATH_MAX * 2]; NTSTATUS status; FILE_RENAME_INFORMATION *info = (FILE_RENAME_INFORMATION *)buf; info->ReplaceIfExists = TRUE; /* TODO: This should be worked on to provide true Linux semantics (refer to unlink()) */ info->RootDirectory = NULL; info->FileNameLength = 2 * filename_to_nt_pathname(newpath, info->FileName, PATH_MAX); if (info->FileNameLength == 0) return -ENOENT; IO_STATUS_BLOCK status_block; status = NtSetInformationFile(winfile->handle, &status_block, info, info->FileNameLength + sizeof(FILE_RENAME_INFORMATION), FileRenameInformation); if (!NT_SUCCESS(status)) { log_warning("NtSetInformationFile() failed, status: %x\n", status); return -ENOENT; } return 0; }
static int winfs_link(struct file *f, const char *newpath) { struct winfs_file *winfile = (struct winfs_file *) f; NTSTATUS status; char buf[sizeof(FILE_LINK_INFORMATION) + PATH_MAX * 2]; FILE_LINK_INFORMATION *info = (FILE_LINK_INFORMATION *)buf; info->ReplaceIfExists = FALSE; info->RootDirectory = NULL; info->FileNameLength = 2 * filename_to_nt_pathname(newpath, info->FileName, PATH_MAX); if (info->FileNameLength == 0) return -ENOENT; IO_STATUS_BLOCK status_block; status = NtSetInformationFile(winfile->handle, &status_block, info, info->FileNameLength + sizeof(FILE_LINK_INFORMATION), FileLinkInformation); if (!NT_SUCCESS(status)) { log_warning("NtSetInformationFile() failed, status: %x.\n", status); return -ENOENT; } return 0; }
/* Open a file * Return values: * < 0 => errno * == 0 => Opening file succeeded * > 0 => It is a symlink which needs to be redirected (target written) */ static int open_file(HANDLE *hFile, struct mount_point *mp, const char *pathname, DWORD desired_access, DWORD create_disposition, DWORD attributes, int flags, BOOL bInherit, char *target, int buflen, char *drive_letter) { WCHAR buf[PATH_MAX]; UNICODE_STRING name; name.Buffer = buf; name.MaximumLength = name.Length = 2 * filename_to_nt_pathname(mp, pathname, buf, PATH_MAX); if (name.Length == 0) return -L_ENOENT; *drive_letter = buf[4]; OBJECT_ATTRIBUTES attr; attr.Length = sizeof(OBJECT_ATTRIBUTES); attr.RootDirectory = NULL; attr.ObjectName = &name; attr.Attributes = (bInherit? OBJ_INHERIT: 0); attr.SecurityDescriptor = NULL; attr.SecurityQualityOfService = NULL; NTSTATUS status; IO_STATUS_BLOCK status_block; HANDLE handle; DWORD create_options = FILE_SYNCHRONOUS_IO_NONALERT; /* For synchronous I/O */ if (desired_access & GENERIC_ALL) create_options |= FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REMOTE_INSTANCE; else { if (desired_access & GENERIC_READ) create_options |= FILE_OPEN_FOR_BACKUP_INTENT; if (desired_access & GENERIC_WRITE) create_options |= FILE_OPEN_REMOTE_INSTANCE; } desired_access |= SYNCHRONIZE | FILE_READ_ATTRIBUTES; status = NtCreateFile(&handle, desired_access, &attr, &status_block, NULL, attributes, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, create_disposition, create_options, NULL, 0); if (status == STATUS_OBJECT_NAME_COLLISION) { log_warning("File already exists."); return -L_EEXIST; } else if (!NT_SUCCESS(status)) { log_warning("Unhandled NtCreateFile error, status: %x, returning ENOENT.", status); return -L_ENOENT; } FILE_ATTRIBUTE_TAG_INFORMATION attribute_info; status = NtQueryInformationFile(handle, &status_block, &attribute_info, sizeof(attribute_info), FileAttributeTagInformation); if (!NT_SUCCESS(status)) { log_error("NtQueryInformationFile(FileAttributeTagInformation) failed, status: %x", status); NtClose(handle); return -L_EIO; } /* Test if the file is a symlink */ int is_symlink = 0; if (!(attribute_info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) && (attribute_info.FileAttributes & FILE_ATTRIBUTE_SYSTEM)) { /* The file has system flag set. A potential symbolic link. */ if (!(desired_access & GENERIC_READ)) { /* But the handle does not have READ access, try reopening file */ HANDLE read_handle = ReOpenFile(handle, desired_access | GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_FLAG_BACKUP_SEMANTICS); if (read_handle == INVALID_HANDLE_VALUE) { log_warning("Reopen symlink file failed, error code %d. Assume not symlink.", GetLastError()); *hFile = handle; return 0; } NtClose(handle); handle = read_handle; } if (winfs_read_symlink_unsafe(handle, target, buflen) > 0) { if (!(flags & O_NOFOLLOW)) { NtClose(handle); return 1; } if (!(flags & O_PATH)) { NtClose(handle); log_info("Specified O_NOFOLLOW but not O_PATH, returning ELOOP."); return -L_ELOOP; } } } else if (!(attribute_info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) && (flags & O_DIRECTORY)) { log_warning("Not a directory."); return -L_ENOTDIR; } *hFile = handle; return 0; }