static size_t winfs_readlink(struct file *f, char *target, size_t buflen) { struct winfs_file *winfile = (struct winfs_file *) f; int r = winfs_read_symlink(winfile->handle, target, (int)buflen); if (r == 0) return -EINVAL; return r; }
static int winfs_stat(struct file *f, struct newstat *buf) { struct winfs_file *winfile = (struct winfs_file *) f; BY_HANDLE_FILE_INFORMATION info; if (!GetFileInformationByHandle(winfile->handle, &info)) { log_warning("GetFileInformationByHandle() failed.\n"); return -1; /* TODO */ } /* 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(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); return 0; }
static int winfs_open(const char *pathname, int flags, int mode, struct file **fp, char *target, int buflen) { /* TODO: mode */ DWORD desiredAccess, shareMode, creationDisposition; HANDLE handle; FILE_ATTRIBUTE_TAG_INFO attributeInfo; WCHAR wpathname[PATH_MAX]; struct winfs_file *file; int pathlen = strlen(pathname); if (utf8_to_utf16_filename(pathname, pathlen + 1, wpathname, PATH_MAX) <= 0) return -ENOENT; if (wpathname[0] == 0) { /* CreateFile() does not accept empty filename. */ wpathname[0] = '.'; wpathname[1] = 0; } if (flags & O_PATH) desiredAccess = 0; else if (flags & O_RDWR) desiredAccess = GENERIC_READ | GENERIC_WRITE; else if (flags & O_WRONLY) desiredAccess = GENERIC_WRITE; else desiredAccess = GENERIC_READ; if (flags & __O_DELETE) desiredAccess |= DELETE; shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; creationDisposition; if (flags & O_EXCL) creationDisposition = CREATE_NEW; else if (flags & O_CREAT) creationDisposition = OPEN_ALWAYS; else creationDisposition = OPEN_EXISTING; //log_debug("CreateFileW(): %s\n", pathname); SECURITY_ATTRIBUTES attr; attr.nLength = sizeof(SECURITY_ATTRIBUTES); attr.lpSecurityDescriptor = NULL; attr.bInheritHandle = (fp != NULL); handle = CreateFileW(wpathname, desiredAccess, shareMode, &attr, creationDisposition, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, NULL); if (handle == INVALID_HANDLE_VALUE) { DWORD err = GetLastError(); if (err == ERROR_FILE_EXISTS || err == ERROR_ALREADY_EXISTS) { log_warning("File already exists.\n"); return -EEXIST; } else { log_warning("Unhandled CreateFileW() failure, error code: %d, returning ENOENT.\n", GetLastError()); return -ENOENT; } } if (!GetFileInformationByHandleEx(handle, FileAttributeTagInfo, &attributeInfo, sizeof(attributeInfo))) { CloseHandle(handle); return -EIO; } /* Test if the file is a symlink */ int is_symlink = 0; if (attributeInfo.FileAttributes != INVALID_FILE_ATTRIBUTES && (attributeInfo.FileAttributes & FILE_ATTRIBUTE_SYSTEM)) { log_info("The file has system flag set.\n"); if (!(desiredAccess & GENERIC_READ)) { /* We need to get a readable handle */ log_info("But the handle does not have READ access, try reopening file...\n"); HANDLE read_handle = ReOpenFile(handle, desiredAccess | GENERIC_READ, shareMode, FILE_FLAG_BACKUP_SEMANTICS); if (read_handle == INVALID_HANDLE_VALUE) { log_warning("Reopen file failed, error code %d. Assume not symlink.\n", GetLastError()); goto after_symlink_test; } CloseHandle(handle); log_info("Reopen succeeded.\n"); handle = read_handle; } if (winfs_read_symlink(handle, target, buflen) > 0) { if (!(flags & O_NOFOLLOW)) { CloseHandle(handle); return 1; } if (!(flags & O_PATH)) { CloseHandle(handle); log_info("Specified O_NOFOLLOW but not O_PATH, returning ELOOP.\n"); return -ELOOP; } is_symlink = 1; } log_info("Opening file directly.\n"); } else if (attributeInfo.FileAttributes != INVALID_FILE_ATTRIBUTES && !(attributeInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) && (flags & O_DIRECTORY)) { log_warning("Not a directory.\n"); return -ENOTDIR; } after_symlink_test: if (!is_symlink && (flags & O_TRUNC) && ((flags & O_WRONLY) || (flags & O_RDWR))) { /* Truncate the file */ FILE_END_OF_FILE_INFORMATION info; info.EndOfFile.QuadPart = 0; IO_STATUS_BLOCK status_block; NTSTATUS status = NtSetInformationFile(handle, &status_block, &info, sizeof(info), FileEndOfFileInformation); if (!NT_SUCCESS(status)) log_error("NtSetInformationFile() failed, status: %x\n", status); } if (fp) { file = (struct winfs_file *)kmalloc(sizeof(struct winfs_file) + pathlen); file->base_file.op_vtable = &winfs_ops; file->base_file.ref = 1; file->base_file.flags = flags; file->handle = handle; file->restart_scan = 1; file->pathlen = pathlen; memcpy(file->pathname, pathname, pathlen); *fp = (struct file *)file; } else CloseHandle(handle); 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, const char *pathname, DWORD desired_access, DWORD create_disposition, int flags, BOOL bInherit, char *target, int buflen) { WCHAR buf[PATH_MAX]; UNICODE_STRING name; name.Buffer = buf; name.MaximumLength = name.Length = 2 * filename_to_nt_pathname(pathname, buf, PATH_MAX); if (name.Length == 0) return -ENOENT; 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, FILE_ATTRIBUTE_NORMAL, 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.\n"); return -EEXIST; } else if (!NT_SUCCESS(status)) { log_warning("Unhandled NtCreateFile error, status: %x, returning ENOENT.\n", status); return -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\n", status); NtClose(handle); return -EIO; } /* Test if the file is a symlink */ int is_symlink = 0; if (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.\n", GetLastError()); return 0; } CloseHandle(handle); handle = read_handle; } if (winfs_read_symlink(handle, target, buflen) > 0) { if (!(flags & O_NOFOLLOW)) { CloseHandle(handle); return 1; } if (!(flags & O_PATH)) { CloseHandle(handle); log_info("Specified O_NOFOLLOW but not O_PATH, returning ELOOP.\n"); return -ELOOP; } } } else if (!(attribute_info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) && (flags & O_DIRECTORY)) { log_warning("Not a directory.\n"); return -ENOTDIR; } *hFile = handle; return 0; }