static int winfs_stat(struct file *f, struct newstat *buf) { AcquireSRWLockShared(&f->rw_lock); struct winfs_file *winfile = (struct winfs_file *) f; BY_HANDLE_FILE_INFORMATION info; GetFileInformationByHandle(winfile->handle, &info); /* Programs (ld.so) may use st_dev and st_ino to identity files so these must be unique for each file. */ INIT_STRUCT_NEWSTAT_PADDING(buf); buf->st_dev = mkdev(8, 0); // (8, 0): /dev/sda //buf->st_ino = ((uint64_t)info.nFileIndexHigh << 32ULL) + info.nFileIndexLow; /* Hash 64 bit inode to 32 bit to fix legacy applications * We may later add an option for changing this behaviour */ buf->st_ino = info.nFileIndexHigh ^ info.nFileIndexLow; if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) buf->st_mode = 0555; else buf->st_mode = 0755; if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { buf->st_mode |= S_IFDIR; buf->st_size = 0; } else { int r; if ((info.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) && (r = winfs_read_symlink_unsafe(winfile->handle, NULL, 0)) > 0) { buf->st_mode |= S_IFLNK; buf->st_size = r; } else { buf->st_mode |= S_IFREG; buf->st_size = ((uint64_t)info.nFileSizeHigh << 32ULL) + info.nFileSizeLow; } } buf->st_nlink = info.nNumberOfLinks; buf->st_uid = 0; buf->st_gid = 0; buf->st_rdev = 0; buf->st_blksize = PAGE_SIZE; buf->st_blocks = (buf->st_size + buf->st_blksize - 1) / buf->st_blksize; buf->st_atime = filetime_to_unix_sec(&info.ftLastAccessTime); buf->st_atime_nsec = filetime_to_unix_nsec(&info.ftLastAccessTime); buf->st_mtime = filetime_to_unix_sec(&info.ftLastWriteTime); buf->st_mtime_nsec = filetime_to_unix_nsec(&info.ftLastWriteTime); buf->st_ctime = filetime_to_unix_sec(&info.ftCreationTime); buf->st_ctime_nsec = filetime_to_unix_nsec(&info.ftCreationTime); ReleaseSRWLockShared(&f->rw_lock); return 0; }
static size_t winfs_readlink(struct file *f, char *target, size_t buflen) { /* This file is a symlink, so read(), write() should not be called on this file * Thus we don't need to lock the file pointer */ /* TODO: Store the file type in winfile structure so we can be sure this file is really a symlink */ AcquireSRWLockShared(&f->rw_lock); struct winfs_file *winfile = (struct winfs_file *) f; int r = winfs_read_symlink_unsafe(winfile->handle, target, (int)buflen); ReleaseSRWLockShared(&f->rw_lock); if (r == 0) return -L_EINVAL; return r; }
/* 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; }