/* Convert an utf-8 file name to NT file name, return converted name length in bytes, no NULL terminator is appended */ static int filename_to_nt_pathname(const char *filename, WCHAR *buf, int buf_size) { if (buf_size < 4) return 0; buf[0] = L'\\'; buf[1] = L'?'; buf[2] = L'?'; buf[3] = L'\\'; buf += 4; buf_size -= 4; int out_size = 4; int len = (DWORD)GetCurrentDirectoryW(buf_size, buf); buf += len; out_size += len; buf_size -= len; if (filename[0] == 0) return out_size; *buf++ = L'\\'; out_size++; buf_size--; int fl = utf8_to_utf16_filename(filename, strlen(filename), buf, buf_size); if (fl == 0) return 0; return out_size + fl; }
/* Convert an utf-8 file name to NT file name, return converted name length in characters, no NULL terminator is appended */ static int filename_to_nt_pathname(const char *filename, WCHAR *buf, int buf_size) { HANDLE basedir_handle = CreateFileW(L".", 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (basedir_handle == INVALID_HANDLE_VALUE) return 0; WCHAR basedir[PATH_MAX]; DWORD basedir_len = GetFinalPathNameByHandleW(basedir_handle, basedir, PATH_MAX, FILE_NAME_NORMALIZED); CloseHandle(basedir_handle); if (basedir_len > PATH_MAX) return 0; basedir[1] = L'?'; if (buf_size < basedir_len) return 0; memcpy(buf, basedir, basedir_len * sizeof(WCHAR)); buf += basedir_len; int out_size = basedir_len; buf_size -= basedir_len; if (filename[0] == 0) return out_size; if (buf_size < 1) return 0; *buf++ = L'\\'; out_size++; buf_size--; int fl = utf8_to_utf16_filename(filename, strlen(filename), buf, buf_size); if (fl == 0) return 0; return out_size + fl; }
static int winfs_rmdir(struct mount_point *mp, const char *pathname) { WCHAR wpathname[PATH_MAX]; if (utf8_to_utf16_filename(pathname, strlen(pathname) + 1, wpathname, PATH_MAX) <= 0) return -L_ENOENT; if (!RemoveDirectoryW(wpathname)) { log_warning("RemoveDirectoryW() failed, error code: %d", GetLastError()); return -L_ENOENT; } return 0; }
static int winfs_mkdir(struct mount_point *mp, const char *pathname, int mode) { WCHAR wpathname[PATH_MAX]; if (utf8_to_utf16_filename(pathname, strlen(pathname) + 1, wpathname, PATH_MAX) <= 0) return -L_ENOENT; if (!CreateDirectoryW(wpathname, NULL)) { DWORD err = GetLastError(); if (err == ERROR_FILE_EXISTS || err == ERROR_ALREADY_EXISTS) { log_warning("File already exists."); return -L_EEXIST; } log_warning("CreateDirectoryW() failed, error code: %d", GetLastError()); return -L_ENOENT; } return 0; }
/* Convert an utf-8 file name to NT file name, return converted name length in characters, no NULL terminator is appended */ static int filename_to_nt_pathname(struct mount_point *mp, const char *filename, WCHAR *buf, int buf_size) { if (buf_size < mp->win_path_len) return 0; memcpy(buf, mp->win_path, mp->win_path_len * sizeof(WCHAR)); buf += mp->win_path_len; int out_size = mp->win_path_len; buf_size -= mp->win_path_len; if (filename[0] == 0) return out_size; if (buf_size < 1) return 0; *buf++ = L'\\'; out_size++; buf_size--; int fl = utf8_to_utf16_filename(filename, strlen(filename), buf, buf_size); if (fl < 0) return 0; return out_size + fl; }
static int winfs_symlink(const char *target, const char *linkpath) { HANDLE handle; WCHAR wlinkpath[PATH_MAX]; if (utf8_to_utf16_filename(linkpath, strlen(linkpath) + 1, wlinkpath, PATH_MAX) <= 0) return -ENOENT; log_info("CreateFileW(): %s\n", linkpath); handle = CreateFileW(wlinkpath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, CREATE_NEW, FILE_ATTRIBUTE_SYSTEM, 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; } log_warning("CreateFileW() failed, error code: %d.\n", GetLastError()); return -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.\n", GetLastError()); CloseHandle(handle); return -EIO; } DWORD targetlen = strlen(target); if (!WriteFile(handle, target, targetlen, &num_written, NULL) || num_written < targetlen) { log_warning("WriteFile() failed, error code: %d.\n", GetLastError()); CloseHandle(handle); return -EIO; } CloseHandle(handle); 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; }