Import the WinBtrfs 0.2 driver from https://github.com/maharmstone/btrfs.

Based on the initial work from Peter Hater, with various modification and patches sent upstream (yay, yet another collaboration :-)).

This driver is in its earlies, so expect crashes, issues, and so on. We'll keep it updated to get rid of these issues.
For now, it reads really well from a btrfs volume!

CORE-10892

svn path=/trunk/; revision=71037
This commit is contained in:
Pierre Schweitzer 2016-03-23 20:35:05 +00:00
parent bd24dc4921
commit d6b45221ca
22 changed files with 23580 additions and 0 deletions

View file

@ -1,4 +1,5 @@
add_subdirectory(btrfs)
add_subdirectory(cdfs)
add_subdirectory(ext2)
add_subdirectory(fastfat)

View file

@ -0,0 +1,37 @@
include_directories(${REACTOS_SOURCE_DIR}/include/reactos/drivers
inc)
list(APPEND SOURCE
btrfs.c
cache.c
crc32c.c
create.c
dirctrl.c
fastio.c
fileinfo.c
flushthread.c
fsctl.c
read.c
reparse.c
search.c
security.c
treefuncs.c
write.c
btrfs_drv.h)
add_library(btrfs SHARED ${SOURCE} btrfs.rc)
if(NOT MSVC)
replace_compile_flags("-Werror" " ")
else()
replace_compile_flags("/we\"4189\"" " ")
endif()
add_definitions(-D__KERNEL__)
set_module_type(btrfs kernelmodedriver)
target_link_libraries(btrfs ntoskrnl_vista)
add_importlibs(btrfs ntoskrnl hal)
add_pch(btrfs btrfs_drv.h SOURCE)
add_cd_file(TARGET btrfs DESTINATION reactos/system32/drivers NO_CAB FOR all)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,403 @@
/* btrfs.h
* Generic btrfs header file. Thanks to whoever it was who wrote
* https://btrfs.wiki.kernel.org/index.php/On-disk_Format - you saved me a lot of time!
*
* I release this file, and this file only, into the public domain - do whatever
* you want with it. You don't have to, but I'd appreciate if you let me know if you
* use it anything cool - mark@harmstone.com. */
#ifndef BTRFS_H_DEFINED
#define BTRFS_H_DEFINED
static const UINT64 superblock_addrs[] = { 0x10000, 0x4000000, 0x4000000000, 0x4000000000000, 0 };
#define BTRFS_MAGIC 0x4d5f53665248425f
#define MAX_LABEL_SIZE 0x100
#define SUBVOL_ROOT_INODE 0x100
#define TYPE_INODE_ITEM 0x01
#define TYPE_INODE_REF 0x0C
#define TYPE_INODE_EXTREF 0x0D
#define TYPE_XATTR_ITEM 0x18
#define TYPE_DIR_ITEM 0x54
#define TYPE_DIR_INDEX 0x60
#define TYPE_EXTENT_DATA 0x6C
#define TYPE_EXTENT_CSUM 0x80
#define TYPE_ROOT_ITEM 0x84
#define TYPE_ROOT_BACKREF 0x90
#define TYPE_ROOT_REF 0x9C
#define TYPE_EXTENT_ITEM 0xA8
#define TYPE_METADATA_ITEM 0xA9
#define TYPE_TREE_BLOCK_REF 0xB0
#define TYPE_EXTENT_DATA_REF 0xB2
#define TYPE_EXTENT_REF_V0 0xB4
#define TYPE_SHARED_BLOCK_REF 0xB6
#define TYPE_SHARED_DATA_REF 0xB8
#define TYPE_BLOCK_GROUP_ITEM 0xC0
#define TYPE_DEV_EXTENT 0xCC
#define TYPE_DEV_ITEM 0xD8
#define TYPE_CHUNK_ITEM 0xE4
#define BTRFS_ROOT_ROOT 1
#define BTRFS_ROOT_EXTENT 2
#define BTRFS_ROOT_CHUNK 3
#define BTRFS_ROOT_DEVTREE 4
#define BTRFS_ROOT_FSTREE 5
#define BTRFS_ROOT_CHECKSUM 7
#define BTRFS_COMPRESSION_NONE 0
#define BTRFS_COMPRESSION_ZLIB 1
#define BTRFS_COMPRESSION_LZO 2
#define BTRFS_ENCRYPTION_NONE 0
#define BTRFS_ENCODING_NONE 0
#define EXTENT_TYPE_INLINE 0
#define EXTENT_TYPE_REGULAR 1
#define EXTENT_TYPE_PREALLOC 2
#define BLOCK_FLAG_DATA 0x001
#define BLOCK_FLAG_SYSTEM 0x002
#define BLOCK_FLAG_METADATA 0x004
#define BLOCK_FLAG_RAID0 0x008
#define BLOCK_FLAG_RAID1 0x010
#define BLOCK_FLAG_DUPLICATE 0x020
#define BLOCK_FLAG_RAID10 0x040
#define BLOCK_FLAG_RAID5 0x080
#define BLOCK_FLAG_RAID6 0x100
#define FREE_SPACE_CACHE_ID 0xFFFFFFFFFFFFFFF5
#define EXTENT_CSUM_ID 0xFFFFFFFFFFFFFFF6
#define BTRFS_INODE_NODATASUM 0x001
#define BTRFS_INODE_NODATACOW 0x002
#define BTRFS_INODE_READONLY 0x004
#define BTRFS_INODE_NOCOMPRESS 0x008
#define BTRFS_INODE_PREALLOC 0x010
#define BTRFS_INODE_SYNC 0x020
#define BTRFS_INODE_IMMUTABLE 0x040
#define BTRFS_INODE_APPEND 0x080
#define BTRFS_INODE_NODUMP 0x100
#define BTRFS_INODE_NOATIME 0x200
#define BTRFS_INODE_DIRSYNC 0x400
#define BTRFS_INODE_COMPRESS 0x800
#define BTRFS_SUBVOL_READONLY 0x1
#define BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE 0x1
#define BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF 0x0001
#define BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL 0x0002
#define BTRFS_INCOMPAT_FLAGS_MIXED_GROUPS 0x0004
#define BTRFS_INCOMPAT_FLAGS_COMPRESS_LZO 0x0008
#define BTRFS_INCOMPAT_FLAGS_COMPRESS_LZOV2 0x0010
#define BTRFS_INCOMPAT_FLAGS_BIG_METADATA 0x0020
#define BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF 0x0040
#define BTRFS_INCOMPAT_FLAGS_RAID56 0x0080
#define BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA 0x0100
#define BTRFS_INCOMPAT_FLAGS_NO_HOLES 0x0200
#pragma pack(push, 1)
typedef struct {
UINT8 uuid[16];
} BTRFS_UUID;
typedef struct {
UINT64 obj_id;
UINT8 obj_type;
UINT64 offset;
} KEY;
#define HEADER_FLAG_MIXED_BACKREF 0x100000000000000
#define HEADER_FLAG_SHARED_BACKREF 0x000000000000002
typedef struct {
UINT8 csum[32];
BTRFS_UUID fs_uuid;
UINT64 address;
UINT64 flags;
BTRFS_UUID chunk_tree_uuid;
UINT64 generation;
UINT64 tree_id;
UINT32 num_items;
UINT8 level;
} tree_header;
typedef struct {
KEY key;
UINT32 offset;
UINT32 size;
} leaf_node;
typedef struct {
KEY key;
UINT64 address;
UINT64 generation;
} internal_node;
typedef struct {
UINT64 dev_id;
UINT64 num_bytes;
UINT64 bytes_used;
UINT32 optimal_io_align;
UINT32 optimal_io_width;
UINT32 minimal_io_size;
UINT64 type;
UINT64 generation;
UINT64 start_offset;
UINT32 dev_group;
UINT8 seek_speed;
UINT8 bandwidth;
BTRFS_UUID device_uuid;
BTRFS_UUID fs_uuid;
} DEV_ITEM;
#define SYS_CHUNK_ARRAY_SIZE 0x800
typedef struct {
UINT8 checksum[32];
BTRFS_UUID uuid;
UINT64 sb_phys_addr;
UINT64 flags;
UINT64 magic;
UINT64 generation;
UINT64 root_tree_addr;
UINT64 chunk_tree_addr;
UINT64 log_tree_addr;
UINT64 log_root_transid;
UINT64 total_bytes;
UINT64 bytes_used;
UINT64 root_dir_objectid;
UINT64 num_devices;
UINT32 sector_size;
UINT32 node_size;
UINT32 leaf_size;
UINT32 stripe_size;
UINT32 n;
UINT64 chunk_root_generation;
UINT64 compat_flags;
UINT64 compat_ro_flags;
UINT64 incompat_flags;
UINT16 csum_type;
UINT8 root_level;
UINT8 chunk_root_level;
UINT8 log_root_level;
DEV_ITEM dev_item;
char label[MAX_LABEL_SIZE];
UINT64 cache_generation;
UINT64 uuid_tree_generation;
UINT64 reserved[30];
UINT8 sys_chunk_array[SYS_CHUNK_ARRAY_SIZE];
// struct btrfs_root_backup super_roots[BTRFS_NUM_BACKUP_ROOTS];
UINT8 reserved2[1237];
} superblock;
#define BTRFS_TYPE_UNKNOWN 0
#define BTRFS_TYPE_FILE 1
#define BTRFS_TYPE_DIRECTORY 2
#define BTRFS_TYPE_CHARDEV 3
#define BTRFS_TYPE_BLOCKDEV 4
#define BTRFS_TYPE_FIFO 5
#define BTRFS_TYPE_SOCKET 6
#define BTRFS_TYPE_SYMLINK 7
#define BTRFS_TYPE_EA 8
typedef struct {
KEY key;
UINT64 transid;
UINT16 m;
UINT16 n;
UINT8 type;
char name[1];
} DIR_ITEM;
typedef struct {
UINT64 seconds;
UINT32 nanoseconds;
} BTRFS_TIME;
typedef struct {
UINT64 generation;
UINT64 transid;
UINT64 st_size;
UINT64 st_blocks;
UINT64 block_group;
UINT32 st_nlink;
UINT32 st_uid;
UINT32 st_gid;
UINT32 st_mode;
UINT64 st_rdev;
UINT64 flags;
UINT64 sequence;
UINT8 reserved[32];
BTRFS_TIME st_atime;
BTRFS_TIME st_ctime;
BTRFS_TIME st_mtime;
BTRFS_TIME otime;
} INODE_ITEM;
typedef struct {
INODE_ITEM inode;
UINT64 generation;
UINT64 objid;
UINT64 block_number;
UINT64 byte_limit;
UINT64 bytes_used;
UINT64 last_snapshot_generation;
UINT64 flags;
UINT32 num_references;
KEY drop_progress;
UINT8 drop_level;
UINT8 root_level;
UINT64 generation2;
BTRFS_UUID uuid;
BTRFS_UUID parent_uuid;
BTRFS_UUID received_uuid;
UINT64 ctransid;
UINT64 otransid;
UINT64 stransid;
UINT64 rtransid;
BTRFS_TIME ctime;
BTRFS_TIME otime;
BTRFS_TIME stime;
BTRFS_TIME rtime;
UINT64 reserved[8];
} ROOT_ITEM;
typedef struct {
UINT64 size;
UINT64 root_id;
UINT64 stripe_length;
UINT64 type;
UINT32 opt_io_alignment;
UINT32 opt_io_width;
UINT32 sector_size;
UINT16 num_stripes;
UINT16 sub_stripes;
} CHUNK_ITEM;
typedef struct {
UINT64 dev_id;
UINT64 offset;
BTRFS_UUID dev_uuid;
} CHUNK_ITEM_STRIPE;
typedef struct {
UINT64 generation;
UINT64 decoded_size;
UINT8 compression;
UINT8 encryption;
UINT16 encoding;
UINT8 type;
UINT8 data[1];
} EXTENT_DATA;
typedef struct {
UINT64 address;
UINT64 size;
UINT64 offset;
UINT64 num_bytes;
} EXTENT_DATA2;
typedef struct {
UINT64 index;
UINT16 n;
char name[1];
} INODE_REF;
typedef struct {
UINT64 dir;
UINT64 index;
UINT16 n;
char name[1];
} INODE_EXTREF;
#define EXTENT_ITEM_DATA 0x001
#define EXTENT_ITEM_TREE_BLOCK 0x002
#define EXTENT_ITEM_SHARED_BACKREFS 0x100
typedef struct {
UINT64 refcount;
UINT64 generation;
UINT64 flags;
} EXTENT_ITEM;
typedef struct {
UINT32 refcount;
} EXTENT_ITEM_V0;
typedef struct {
EXTENT_ITEM extent_item;
KEY firstitem;
UINT8 level;
} EXTENT_ITEM_TREE;
typedef struct {
UINT64 offset;
} TREE_BLOCK_REF;
typedef struct {
UINT64 root;
UINT64 objid;
UINT64 offset;
UINT32 count;
} EXTENT_DATA_REF;
typedef struct {
UINT64 used;
UINT64 chunk_tree;
UINT64 flags;
} BLOCK_GROUP_ITEM;
typedef struct {
UINT64 root;
UINT64 gen;
UINT64 objid;
UINT64 count;
} EXTENT_REF_V0;
typedef struct {
UINT64 offset;
} SHARED_BLOCK_REF;
typedef struct {
UINT64 offset;
UINT32 count;
} SHARED_DATA_REF;
#define FREE_SPACE_EXTENT 1
#define FREE_SPACE_BITMAP 2
typedef struct {
UINT64 offset;
UINT64 size;
UINT8 type;
} FREE_SPACE_ENTRY;
typedef struct {
KEY key;
UINT64 generation;
UINT64 num_entries;
UINT64 num_bitmaps;
} FREE_SPACE_ITEM;
typedef struct {
UINT64 dir;
UINT64 index;
UINT16 n;
char name[1];
} ROOT_REF;
typedef struct {
UINT64 chunktree;
UINT64 objid;
UINT64 address;
UINT64 length;
BTRFS_UUID chunktree_uuid;
} DEV_EXTENT;
#pragma pack(pop)
#endif

View file

@ -0,0 +1,101 @@
// Microsoft Visual C++ generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// English (U.K.) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG)
#ifdef _WIN32
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK
#pragma code_page(1252)
#endif //_WIN32
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE
BEGIN
"#include ""afxres.h""\r\n"
"\0"
END
3 TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,0,0,1
PRODUCTVERSION 1,0,0,1
FILEFLAGSMASK 0x17L
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x4L
FILETYPE 0x1L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "080904b0"
BEGIN
VALUE "FileDescription", "WinBtrfs"
VALUE "FileVersion", "0.2"
VALUE "InternalName", "btrfs"
VALUE "LegalCopyright", "Copyright (c) Mark Harmstone 2016"
VALUE "OriginalFilename", "btrfs.sys"
VALUE "ProductName", "WinBtrfs"
VALUE "ProductVersion", "0.2"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x809, 1200
END
END
#endif // English (U.K.) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED

View file

@ -0,0 +1,644 @@
/* Copyright (c) Mark Harmstone 2016
*
* This file is part of WinBtrfs.
*
* WinBtrfs is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public Licence as published by
* the Free Software Foundation, either version 3 of the Licence, or
* (at your option) any later version.
*
* WinBtrfs is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public Licence for more details.
*
* You should have received a copy of the GNU Lesser General Public Licence
* along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */
#ifndef BTRFS_DRV_H_DEFINED
#define BTRFS_DRV_H_DEFINED
#ifndef __REACTOS__
#undef _WIN32_WINNT
#undef NTDDI_VERSION
#define _WIN32_WINNT 0x0600
#define NTDDI_VERSION 0x06010000 // Win 7
#define _CRT_SECURE_NO_WARNINGS
#endif /* __REACTOS__ */
#include <ntifs.h>
#include <ntddk.h>
#ifdef __REACTOS__
#include <ntdddisk.h>
#endif /* __REACTOS__ */
#include <mountmgr.h>
#ifdef __REACTOS__
#include <rtlfuncs.h>
#include <iotypes.h>
#endif /* __REACTOS__ */
//#include <windows.h>
#include <windef.h>
#include <wdm.h>
#include <stdio.h>
#include <stdarg.h>
#include <stddef.h>
#include "btrfs.h"
#ifdef _DEBUG
// #define DEBUG_TREE_REFCOUNTS
// #define DEBUG_FCB_REFCOUNTS
// #define DEBUG_LONG_MESSAGES
#define DEBUG_PARANOID
#endif
#define BTRFS_NODE_TYPE_CCB 0x2295
#define BTRFS_NODE_TYPE_FCB 0x2296
#define ALLOC_TAG 0x7442484D //'MHBt'
#define STDCALL __stdcall
#define UID_NOBODY 65534
#define GID_NOBODY 65534
#define EA_NTACL "security.NTACL"
#define EA_NTACL_HASH 0x45922146
#define EA_DOSATTRIB "user.DOSATTRIB"
#define EA_DOSATTRIB_HASH 0x914f9939
#define READ_AHEAD_GRANULARITY 0x10000 // 64 KB
#ifdef _MSC_VER
#define try __try
#define except __except
#define finally __finally
#else
#define try if (1)
#define except(x) if (0 && (x))
#define finally if (1)
#endif
// #pragma pack(push, 1)
struct device_extension;
typedef struct {
PDEVICE_OBJECT devobj;
BTRFS_UUID fsuuid;
BTRFS_UUID devuuid;
UINT64 devnum;
UNICODE_STRING devpath;
BOOL processed;
LIST_ENTRY list_entry;
} volume;
typedef struct _fcb_nonpaged {
FAST_MUTEX HeaderMutex;
SECTION_OBJECT_POINTERS segment_object;
ERESOURCE resource;
ERESOURCE paging_resource;
} fcb_nonpaged;
struct _root;
typedef struct _fcb {
FSRTL_ADVANCED_FCB_HEADER Header;
struct _fcb_nonpaged* nonpaged;
LONG refcount;
LONG open_count;
UNICODE_STRING filepart;
ANSI_STRING utf8;
struct _device_extension* Vcb;
struct _fcb* par;
struct _fcb* prev;
struct _fcb* next;
struct _root* subvol;
LIST_ENTRY children;
UINT64 inode;
UINT8 type;
BOOL delete_on_close;
INODE_ITEM inode_item;
UNICODE_STRING full_filename;
ULONG name_offset;
SECURITY_DESCRIPTOR* sd;
FILE_LOCK lock;
BOOL deleted;
PKTHREAD lazy_writer_thread;
ULONG atts;
SHARE_ACCESS share_access;
BOOL ads;
UINT32 adssize;
UINT32 adshash;
ANSI_STRING adsxattr;
LIST_ENTRY list_entry;
} fcb;
typedef struct _ccb {
USHORT NodeType;
CSHORT NodeSize;
ULONG disposition;
ULONG options;
UINT64 query_dir_offset;
// char* query_string;
UNICODE_STRING query_string;
BOOL has_wildcard;
BOOL specific_file;
} ccb;
// typedef struct _log_to_phys {
// UINT64 address;
// UINT64 size;
// UINT64 physaddr;
// UINT32 sector_size;
// struct _log_to_phys* next;
// } log_to_phys;
struct _device_extension;
// enum tree_holder_status {
// tree_holder_unloaded,
// tree_holder_loading,
// tree_holder_loaded,
// tree_holder_unloading
// };
// typedef struct {
// enum tree_holder_status status;
// KSPIN_LOCK spin_lock;
// ERESOURCE lock;
// } tree_holder_nonpaged;
typedef struct {
UINT64 address;
UINT64 generation;
struct _tree* tree;
// tree_holder_nonpaged* nonpaged;
} tree_holder;
typedef struct _tree_data {
KEY key;
LIST_ENTRY list_entry;
BOOL ignore;
BOOL inserted;
union {
tree_holder treeholder;
struct {
UINT32 size;
UINT8* data;
};
};
} tree_data;
// typedef struct _tree_nonpaged {
// ERESOURCE load_tree_lock;
// } tree_nonpaged;
typedef struct _tree {
// UINT64 address;
// UINT8 level;
tree_header header;
LONG refcount;
UINT32 size;
struct _device_extension* Vcb;
struct _tree* parent;
tree_data* paritem;
struct _root* root;
// tree_nonpaged* nonpaged;
LIST_ENTRY itemlist;
LIST_ENTRY list_entry;
UINT64 new_address;
UINT64 flags;
} tree;
typedef struct {
// KSPIN_LOCK load_tree_lock;
ERESOURCE load_tree_lock;
} root_nonpaged;
typedef struct _root {
UINT64 id;
tree_holder treeholder;
root_nonpaged* nonpaged;
UINT64 lastinode;
ROOT_ITEM root_item;
struct _root* prev;
struct _root* next;
} root;
typedef struct {
tree* tree;
tree_data* item;
} traverse_ptr;
typedef struct _tree_cache {
tree* tree;
BOOL write;
LIST_ENTRY list_entry;
} tree_cache;
typedef struct _root_cache {
root* root;
struct _root_cache* next;
} root_cache;
#define SPACE_TYPE_FREE 0
#define SPACE_TYPE_USED 1
#define SPACE_TYPE_DELETING 2
#define SPACE_TYPE_WRITING 3
typedef struct {
UINT64 offset;
UINT64 size;
UINT8 type;
LIST_ENTRY list_entry;
} space;
typedef struct {
UINT64 address;
UINT64 size;
BOOL provisional;
LIST_ENTRY listentry;
} disk_hole;
typedef struct {
PDEVICE_OBJECT devobj;
DEV_ITEM devitem;
LIST_ENTRY disk_holes;
} device;
typedef struct {
CHUNK_ITEM* chunk_item;
UINT32 size;
UINT64 offset;
UINT64 used;
UINT32 oldused;
BOOL space_changed;
device** devices;
LIST_ENTRY space;
LIST_ENTRY list_entry;
} chunk;
typedef struct {
KEY key;
void* data;
USHORT size;
LIST_ENTRY list_entry;
} sys_chunk;
typedef struct _device_extension {
device* devices;
// DISK_GEOMETRY geometry;
UINT64 length;
superblock superblock;
// WCHAR label[MAX_LABEL_SIZE];
BOOL readonly;
fcb* fcbs;
fcb* volume_fcb;
fcb* root_fcb;
ERESOURCE DirResource;
KSPIN_LOCK FcbListLock;
ERESOURCE fcb_lock;
ERESOURCE load_lock;
ERESOURCE tree_lock;
PNOTIFY_SYNC NotifySync;
LIST_ENTRY DirNotifyList;
LONG tree_lock_counter;
LONG open_trees;
ULONG write_trees;
// ERESOURCE LogToPhysLock;
// UINT64 chunk_root_phys_addr;
UINT64 root_tree_phys_addr;
// log_to_phys* log_to_phys;
root* roots;
root* chunk_root;
root* root_root;
root* extent_root;
root* checksum_root;
root* dev_root;
BOOL log_to_phys_loaded;
UINT32 max_inline;
LIST_ENTRY sys_chunks;
LIST_ENTRY chunks;
LIST_ENTRY trees;
LIST_ENTRY tree_cache;
HANDLE flush_thread_handle;
KTIMER flush_thread_timer;
LIST_ENTRY list_entry;
} device_extension;
typedef struct {
LIST_ENTRY listentry;
PSID sid;
UINT32 uid;
} uid_map;
// #pragma pack(pop)
static __inline void init_tree_holder(tree_holder* th) {
// th->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(tree_holder_nonpaged), ALLOC_TAG);
// KeInitializeSpinLock(&th->nonpaged->spin_lock);
// ExInitializeResourceLite(&th->nonpaged->lock); // FIXME - delete this later
}
static __inline void* map_user_buffer(PIRP Irp) {
if (!Irp->MdlAddress) {
return Irp->UserBuffer;
} else {
return MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
}
}
static __inline UINT64 unix_time_to_win(BTRFS_TIME* t) {
return (t->seconds * 10000000) + (t->nanoseconds / 100) + 116444736000000000;
}
static __inline void win_time_to_unix(LARGE_INTEGER t, BTRFS_TIME* out) {
ULONGLONG l = t.QuadPart - 116444736000000000;
out->seconds = l / 10000000;
out->nanoseconds = (l % 10000000) * 100;
}
// in btrfs.c
device* find_device_from_uuid(device_extension* Vcb, BTRFS_UUID* uuid);
ULONG sector_align( ULONG NumberToBeAligned, ULONG Alignment );
int keycmp(const KEY* key1, const KEY* key2);
ULONG STDCALL get_file_attributes(device_extension* Vcb, INODE_ITEM* ii, root* r, UINT64 inode, UINT8 type, BOOL dotfile, BOOL ignore_xa);
BOOL STDCALL get_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, UINT8** data, UINT16* datalen);
NTSTATUS STDCALL set_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, UINT8* data, UINT16 datalen, LIST_ENTRY* rollback);
BOOL STDCALL delete_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, LIST_ENTRY* rollback);
void _free_fcb(fcb* fcb, const char* func, const char* file, unsigned int line);
BOOL STDCALL get_last_inode(device_extension* Vcb, root* r);
NTSTATUS add_dir_item(device_extension* Vcb, root* subvol, UINT64 inode, UINT32 crc32, DIR_ITEM* di, ULONG disize, LIST_ENTRY* rollback);
NTSTATUS delete_dir_item(device_extension* Vcb, root* subvol, UINT64 parinode, UINT32 crc32, PANSI_STRING utf8, LIST_ENTRY* rollback);
UINT64 find_next_dir_index(device_extension* Vcb, root* subvol, UINT64 inode);
NTSTATUS delete_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, UINT64* index, LIST_ENTRY* rollback);
NTSTATUS delete_fcb(fcb* fcb, PFILE_OBJECT FileObject, LIST_ENTRY* rollback);
fcb* create_fcb();
void protect_superblocks(device_extension* Vcb, chunk* c);
BOOL is_top_level(PIRP Irp);
#ifdef _MSC_VER
#define funcname __FUNCTION__
#else
#define funcname __func__
#endif
// FIXME - we probably shouldn't be moving funcname etc. around if we're not printing debug messages
#define free_fcb(fcb) _free_fcb(fcb, funcname, __FILE__, __LINE__)
#ifdef _DEBUG
#ifdef DEBUG_LONG_MESSAGES
#define TRACE(s, ...) _debug_message(funcname, 3, __FILE__, __LINE__, s, ##__VA_ARGS__)
#define WARN(s, ...) _debug_message(funcname, 2, __FILE__, __LINE__, s, ##__VA_ARGS__)
#define FIXME(s, ...) _debug_message(funcname, 1, __FILE__, __LINE__, s, ##__VA_ARGS__)
#define ERR(s, ...) _debug_message(funcname, 1, __FILE__, __LINE__, s, ##__VA_ARGS__)
void STDCALL _debug_message(const char* func, UINT8 priority, const char* file, unsigned int line, char* s, ...);
#else
#define TRACE(s, ...) _debug_message(funcname, 3, s, ##__VA_ARGS__)
#define WARN(s, ...) _debug_message(funcname, 2, s, ##__VA_ARGS__)
#define FIXME(s, ...) _debug_message(funcname, 1, s, ##__VA_ARGS__)
#define ERR(s, ...) _debug_message(funcname, 1, s, ##__VA_ARGS__)
void STDCALL _debug_message(const char* func, UINT8 priority, char* s, ...);
#endif
#else
#define TRACE(s, ...)
#define WARN(s, ...)
#ifndef __REACTOS__
#define FIXME(s, ...) DbgPrint("Btrfs FIXME : " funcname " : " s, ##__VA_ARGS__)
#define ERR(s, ...) DbgPrint("Btrfs ERR : " funcname " : " s, ##__VA_ARGS__)
#else
#define FIXME(s, ...) DbgPrint("Btrfs FIXME : %s : " s, funcname, ##__VA_ARGS__)
#define ERR(s, ...) DbgPrint("Btrfs ERR : %s : " s, funcname, ##__VA_ARGS__)
#endif
#endif
// in fastio.c
void STDCALL init_fast_io_dispatch(FAST_IO_DISPATCH** fiod);
// in crc32c.c
UINT32 STDCALL calc_crc32c(UINT32 seed, UINT8* msg, ULONG msglen);
// in treefuncs.c
NTSTATUS STDCALL _find_item(device_extension* Vcb, root* r, traverse_ptr* tp, const KEY* searchkey, BOOL ignore, const char* func, const char* file, unsigned int line);
BOOL STDCALL _find_next_item(device_extension* Vcb, const traverse_ptr* tp, traverse_ptr* next_tp, BOOL ignore, const char* func, const char* file, unsigned int line);
BOOL STDCALL _find_prev_item(device_extension* Vcb, const traverse_ptr* tp, traverse_ptr* prev_tp, BOOL ignore, const char* func, const char* file, unsigned int line);
void STDCALL _free_traverse_ptr(traverse_ptr* tp, const char* func, const char* file, unsigned int line);
void STDCALL free_tree_cache(LIST_ENTRY* tc);
BOOL STDCALL insert_tree_item(device_extension* Vcb, root* r, UINT64 obj_id, UINT8 obj_type, UINT64 offset, void* data, UINT32 size, traverse_ptr* ptp, LIST_ENTRY* rollback);
void STDCALL delete_tree_item(device_extension* Vcb, traverse_ptr* tp, LIST_ENTRY* rollback);
void STDCALL add_to_tree_cache(device_extension* Vcb, tree* t, BOOL write);
tree* STDCALL _free_tree(tree* t, const char* func, const char* file, unsigned int line);
NTSTATUS STDCALL _load_tree(device_extension* Vcb, UINT64 addr, root* r, tree** pt, const char* func, const char* file, unsigned int line);
NTSTATUS STDCALL _do_load_tree(device_extension* Vcb, tree_holder* th, root* r, tree* t, tree_data* td, BOOL* loaded, const char* func, const char* file, unsigned int line);
void clear_rollback(LIST_ENTRY* rollback);
void do_rollback(device_extension* Vcb, LIST_ENTRY* rollback);
#define find_item(Vcb, r, tp, searchkey, ignore) _find_item(Vcb, r, tp, searchkey, ignore, funcname, __FILE__, __LINE__)
#define find_next_item(Vcb, tp, next_tp, ignore) _find_next_item(Vcb, tp, next_tp, ignore, funcname, __FILE__, __LINE__)
#define find_prev_item(Vcb, tp, prev_tp, ignore) _find_prev_item(Vcb, tp, prev_tp, ignore, funcname, __FILE__, __LINE__)
#define free_tree(t) _free_tree(t, funcname, __FILE__, __LINE__)
#define load_tree(t, addr, r, pt) _load_tree(t, addr, r, pt, funcname, __FILE__, __LINE__)
#define free_traverse_ptr(tp) _free_traverse_ptr(tp, funcname, __FILE__, __LINE__)
#define do_load_tree(Vcb, th, r, t, td, loaded) _do_load_tree(Vcb, th, r, t, td, loaded, funcname, __FILE__, __LINE__)
// in search.c
void STDCALL look_for_vols(LIST_ENTRY* volumes);
// in cache.c
NTSTATUS STDCALL init_cache();
void STDCALL free_cache();
extern CACHE_MANAGER_CALLBACKS* cache_callbacks;
// in write.c
NTSTATUS STDCALL do_write(device_extension* Vcb, LIST_ENTRY* rollback);
NTSTATUS write_file(PDEVICE_OBJECT DeviceObject, PIRP Irp);
NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void* buf, ULONG* length, BOOL paging_io, BOOL no_cache, LIST_ENTRY* rollback);
NTSTATUS truncate_file(fcb* fcb, UINT64 end, LIST_ENTRY* rollback);
NTSTATUS extend_file(fcb* fcb, UINT64 end, LIST_ENTRY* rollback);
NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, LIST_ENTRY* changed_sector_list, LIST_ENTRY* rollback);
void update_checksum_tree(device_extension* Vcb, LIST_ENTRY* changed_sector_list, LIST_ENTRY* rollback);
NTSTATUS insert_sparse_extent(device_extension* Vcb, root* r, UINT64 inode, UINT64 start, UINT64 length, LIST_ENTRY* rollback);
NTSTATUS STDCALL add_extent_ref(device_extension* Vcb, UINT64 address, UINT64 size, root* subvol, UINT64 inode, UINT64 offset, LIST_ENTRY* rollback);
NTSTATUS STDCALL remove_extent_ref(device_extension* Vcb, UINT64 address, UINT64 size, root* subvol, UINT64 inode, UINT64 offset, LIST_ENTRY* changed_sector_list, LIST_ENTRY* rollback);
void print_trees(LIST_ENTRY* tc);
chunk* get_chunk_from_address(device_extension* Vcb, UINT64 address);
void add_to_space_list(chunk* c, UINT64 offset, UINT64 size, UINT8 type);
NTSTATUS consider_write(device_extension* Vcb);
// in dirctrl.c
NTSTATUS STDCALL drv_directory_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
// in security.c
NTSTATUS STDCALL drv_query_security(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
NTSTATUS STDCALL drv_set_security(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
void fcb_get_sd(fcb* fcb);
// UINT32 STDCALL get_uid();
void add_user_mapping(WCHAR* sidstring, ULONG sidstringlength, UINT32 uid);
NTSTATUS fcb_get_new_sd(fcb* fcb, ACCESS_STATE* as);
// in fileinfo.c
NTSTATUS STDCALL drv_set_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
NTSTATUS STDCALL drv_query_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
NTSTATUS add_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, UINT64 index, PANSI_STRING utf8, LIST_ENTRY* rollback);
// in reparse.c
BOOL follow_symlink(fcb* fcb, PFILE_OBJECT FileObject);
NTSTATUS get_reparse_point(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, void* buffer, DWORD buflen, DWORD* retlen);
NTSTATUS set_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp);
// in create.c
NTSTATUS STDCALL drv_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
NTSTATUS get_fcb(device_extension* Vcb, fcb** pfcb, PUNICODE_STRING fnus, fcb* relatedfcb, BOOL parent);
BOOL STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STRING filename, UINT32 crc32, root* r, UINT64 parinode, root** subvol,
UINT64* inode, UINT8* type, PANSI_STRING utf8);
// in fsctl.c
NTSTATUS fsctl_request(PDEVICE_OBJECT DeviceObject, PIRP Irp, UINT32 type, BOOL user);
// in flushthread.c
void STDCALL flush_thread(void* context);
// in read.c
NTSTATUS STDCALL drv_read(PDEVICE_OBJECT DeviceObject, PIRP Irp);
NTSTATUS STDCALL read_file(device_extension* Vcb, root* subvol, UINT64 inode, UINT8* data, UINT64 start, UINT64 length, ULONG* pbr);
static __inline void print_open_trees(device_extension* Vcb) {
LIST_ENTRY* le = Vcb->trees.Flink;
while (le != &Vcb->trees) {
tree* t = CONTAINING_RECORD(le, tree, list_entry);
tree_data* td = CONTAINING_RECORD(t->itemlist.Flink, tree_data, list_entry);
ERR("tree %p: root %llx, level %u, refcount %u, first key (%llx,%x,%llx)\n",
t, t->root->id, t->header.level, t->refcount, td->key.obj_id, td->key.obj_type, td->key.offset);
le = le->Flink;
}
}
static __inline void InsertAfter(LIST_ENTRY* head, LIST_ENTRY* item, LIST_ENTRY* before) {
item->Flink = before->Flink;
before->Flink = item;
item->Blink = before;
if (item->Flink != head)
item->Flink->Blink = item;
else
head->Blink = item;
}
#ifdef _MSC_VER
// #define int3 __asm { int 3 }
#define int3 __debugbreak()
#else
#define int3 asm("int3;")
#endif
#define acquire_tree_lock(Vcb, exclusive) {\
LONG ref = InterlockedIncrement(&Vcb->tree_lock_counter); \
ref = ref; \
if (exclusive) { \
TRACE("getting tree_lock (exclusive) %u->%u\n", ref-1, ref); \
ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE); \
TRACE("open tree count = %i\n", Vcb->open_trees); \
} else { \
TRACE("getting tree_lock %u->%u\n", ref-1, ref); \
ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE); \
} \
}
// if (Vcb->open_trees > 0) { ERR("open tree count = %i\n", Vcb->open_trees); print_open_trees(Vcb); int3; }
// else TRACE("open tree count = %i\n", Vcb->open_trees);
// FIXME - find a way to catch unfreed trees again
#define release_tree_lock(Vcb, exclusive) {\
LONG ref = InterlockedDecrement(&Vcb->tree_lock_counter); \
ref = ref; \
TRACE("releasing tree_lock %u->%u\n", ref+1, ref); \
if (exclusive) {\
TRACE("open tree count = %i\n", Vcb->open_trees); \
} \
ExReleaseResourceLite(&Vcb->tree_lock); \
}
#ifdef DEBUG_TREE_REFCOUNTS
#ifdef DEBUG_LONG_MESSAGES
#define _increase_tree_rc(t, func, file, line) { \
LONG rc = InterlockedIncrement(&t->refcount); \
_debug_message(func, file, line, "tree %p: refcount increased to %i (increase_tree_rc)\n", t, rc); \
}
#else
#define _increase_tree_rc(t, func, file, line) { \
LONG rc = InterlockedIncrement(&t->refcount); \
_debug_message(func, "tree %p: refcount increased to %i (increase_tree_rc)\n", t, rc); \
}
#endif
#define increase_tree_rc(t) _increase_tree_rc(t, funcname, __FILE__, __LINE__)
#else
#define increase_tree_rc(t) InterlockedIncrement(&t->refcount);
#define _increase_tree_rc(t, func, file, line) increase_tree_rc(t)
#endif
// from sys/stat.h
#define __S_IFMT 0170000 /* These bits determine file type. */
#define __S_IFDIR 0040000 /* Directory. */
#define __S_IFCHR 0020000 /* Character device. */
#define __S_IFBLK 0060000 /* Block device. */
#define __S_IFREG 0100000 /* Regular file. */
#define __S_IFIFO 0010000 /* FIFO. */
#define __S_IFLNK 0120000 /* Symbolic link. */
#define __S_IFSOCK 0140000 /* Socket. */
#define __S_ISTYPE(mode, mask) (((mode) & __S_IFMT) == (mask))
#ifndef S_ISDIR
#define S_ISDIR(mode) __S_ISTYPE((mode), __S_IFDIR)
#endif
#ifndef S_IXUSR
#define S_IXUSR 0000100
#endif
#ifdef __REACTOS__
#define S_IFDIR __S_IFDIR
#define S_IFREG __S_IFREG
#endif /* __REACTOS__ */
#ifndef S_IXGRP
#define S_IXGRP (S_IXUSR >> 3)
#endif
#ifndef S_IXOTH
#define S_IXOTH (S_IXGRP >> 3)
#endif
#if defined(__REACTOS__) && (NTDDI_VERSION < NTDDI_WIN7)
NTSTATUS WINAPI RtlUnicodeToUTF8N(CHAR *utf8_dest, ULONG utf8_bytes_max,
ULONG *utf8_bytes_written,
const WCHAR *uni_src, ULONG uni_bytes);
NTSTATUS WINAPI RtlUTF8ToUnicodeN(WCHAR *uni_dest, ULONG uni_bytes_max,
ULONG *uni_bytes_written,
const CHAR *utf8_src, ULONG utf8_bytes);
#endif /* defined(__REACTOS__) && (NTDDI_VERSION < NTDDI_WIN7) */
#if defined(__REACTOS__) && (NTDDI_VERSION < NTDDI_VISTA)
NTSTATUS NTAPI FsRtlRemoveDotsFromPath(PWSTR OriginalString,
USHORT PathLength, USHORT *NewLength);
#endif /* defined(__REACTOS__) && (NTDDI_VERSION < NTDDI_WIN7) */
#endif

View file

@ -0,0 +1,76 @@
/* Copyright (c) Mark Harmstone 2016
*
* This file is part of WinBtrfs.
*
* WinBtrfs is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public Licence as published by
* the Free Software Foundation, either version 3 of the Licence, or
* (at your option) any later version.
*
* WinBtrfs is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public Licence for more details.
*
* You should have received a copy of the GNU Lesser General Public Licence
* along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */
#include "btrfs_drv.h"
#include <wdm.h>
CACHE_MANAGER_CALLBACKS* cache_callbacks;
static BOOLEAN STDCALL acquire_for_lazy_write(PVOID Context, BOOLEAN Wait) {
PFILE_OBJECT FileObject = Context;
fcb* fcb = FileObject->FsContext;
TRACE("(%p, %u)\n", Context, Wait);
if (!fcb || FileObject->Flags & FO_CLEANUP_COMPLETE)
return FALSE;
fcb->lazy_writer_thread = KeGetCurrentThread();
return TRUE;
}
static void STDCALL release_from_lazy_write(PVOID Context) {
PFILE_OBJECT FileObject = Context;
fcb* fcb = FileObject->FsContext;
TRACE("(%p)\n", Context);
if (!fcb || FileObject->Flags & FO_CLEANUP_COMPLETE)
return;
fcb->lazy_writer_thread = NULL;
}
static BOOLEAN STDCALL acquire_for_read_ahead(PVOID Context, BOOLEAN Wait) {
TRACE("(%p, %u)\n", Context, Wait);
return TRUE;
}
static void STDCALL release_from_read_ahead(PVOID Context) {
TRACE("(%p)\n", Context);
}
NTSTATUS STDCALL init_cache() {
cache_callbacks = ExAllocatePoolWithTag(NonPagedPool, sizeof(CACHE_MANAGER_CALLBACKS), ALLOC_TAG);
if (!cache_callbacks) {
ERR("out of memory\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
cache_callbacks->AcquireForLazyWrite = acquire_for_lazy_write;
cache_callbacks->ReleaseFromLazyWrite = release_from_lazy_write;
cache_callbacks->AcquireForReadAhead = acquire_for_read_ahead;
cache_callbacks->ReleaseFromReadAhead = release_from_read_ahead;
return STATUS_SUCCESS;
}
void STDCALL free_cache() {
ExFreePool(cache_callbacks);
}

View file

@ -0,0 +1,108 @@
/* Copyright (c) Mark Harmstone 2016
*
* This file is part of WinBtrfs.
*
* WinBtrfs is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public Licence as published by
* the Free Software Foundation, either version 3 of the Licence, or
* (at your option) any later version.
*
* WinBtrfs is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public Licence for more details.
*
* You should have received a copy of the GNU Lesser General Public Licence
* along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */
#include <windef.h>
#ifndef __REACTOS__
#include <smmintrin.h>
extern BOOL have_sse42;
#endif /* __REACTOS__ */
static const UINT32 crctable[] = {
0x00000000, 0xf26b8303, 0xe13b70f7, 0x1350f3f4, 0xc79a971f, 0x35f1141c, 0x26a1e7e8, 0xd4ca64eb,
0x8ad958cf, 0x78b2dbcc, 0x6be22838, 0x9989ab3b, 0x4d43cfd0, 0xbf284cd3, 0xac78bf27, 0x5e133c24,
0x105ec76f, 0xe235446c, 0xf165b798, 0x030e349b, 0xd7c45070, 0x25afd373, 0x36ff2087, 0xc494a384,
0x9a879fa0, 0x68ec1ca3, 0x7bbcef57, 0x89d76c54, 0x5d1d08bf, 0xaf768bbc, 0xbc267848, 0x4e4dfb4b,
0x20bd8ede, 0xd2d60ddd, 0xc186fe29, 0x33ed7d2a, 0xe72719c1, 0x154c9ac2, 0x061c6936, 0xf477ea35,
0xaa64d611, 0x580f5512, 0x4b5fa6e6, 0xb93425e5, 0x6dfe410e, 0x9f95c20d, 0x8cc531f9, 0x7eaeb2fa,
0x30e349b1, 0xc288cab2, 0xd1d83946, 0x23b3ba45, 0xf779deae, 0x05125dad, 0x1642ae59, 0xe4292d5a,
0xba3a117e, 0x4851927d, 0x5b016189, 0xa96ae28a, 0x7da08661, 0x8fcb0562, 0x9c9bf696, 0x6ef07595,
0x417b1dbc, 0xb3109ebf, 0xa0406d4b, 0x522bee48, 0x86e18aa3, 0x748a09a0, 0x67dafa54, 0x95b17957,
0xcba24573, 0x39c9c670, 0x2a993584, 0xd8f2b687, 0x0c38d26c, 0xfe53516f, 0xed03a29b, 0x1f682198,
0x5125dad3, 0xa34e59d0, 0xb01eaa24, 0x42752927, 0x96bf4dcc, 0x64d4cecf, 0x77843d3b, 0x85efbe38,
0xdbfc821c, 0x2997011f, 0x3ac7f2eb, 0xc8ac71e8, 0x1c661503, 0xee0d9600, 0xfd5d65f4, 0x0f36e6f7,
0x61c69362, 0x93ad1061, 0x80fde395, 0x72966096, 0xa65c047d, 0x5437877e, 0x4767748a, 0xb50cf789,
0xeb1fcbad, 0x197448ae, 0x0a24bb5a, 0xf84f3859, 0x2c855cb2, 0xdeeedfb1, 0xcdbe2c45, 0x3fd5af46,
0x7198540d, 0x83f3d70e, 0x90a324fa, 0x62c8a7f9, 0xb602c312, 0x44694011, 0x5739b3e5, 0xa55230e6,
0xfb410cc2, 0x092a8fc1, 0x1a7a7c35, 0xe811ff36, 0x3cdb9bdd, 0xceb018de, 0xdde0eb2a, 0x2f8b6829,
0x82f63b78, 0x709db87b, 0x63cd4b8f, 0x91a6c88c, 0x456cac67, 0xb7072f64, 0xa457dc90, 0x563c5f93,
0x082f63b7, 0xfa44e0b4, 0xe9141340, 0x1b7f9043, 0xcfb5f4a8, 0x3dde77ab, 0x2e8e845f, 0xdce5075c,
0x92a8fc17, 0x60c37f14, 0x73938ce0, 0x81f80fe3, 0x55326b08, 0xa759e80b, 0xb4091bff, 0x466298fc,
0x1871a4d8, 0xea1a27db, 0xf94ad42f, 0x0b21572c, 0xdfeb33c7, 0x2d80b0c4, 0x3ed04330, 0xccbbc033,
0xa24bb5a6, 0x502036a5, 0x4370c551, 0xb11b4652, 0x65d122b9, 0x97baa1ba, 0x84ea524e, 0x7681d14d,
0x2892ed69, 0xdaf96e6a, 0xc9a99d9e, 0x3bc21e9d, 0xef087a76, 0x1d63f975, 0x0e330a81, 0xfc588982,
0xb21572c9, 0x407ef1ca, 0x532e023e, 0xa145813d, 0x758fe5d6, 0x87e466d5, 0x94b49521, 0x66df1622,
0x38cc2a06, 0xcaa7a905, 0xd9f75af1, 0x2b9cd9f2, 0xff56bd19, 0x0d3d3e1a, 0x1e6dcdee, 0xec064eed,
0xc38d26c4, 0x31e6a5c7, 0x22b65633, 0xd0ddd530, 0x0417b1db, 0xf67c32d8, 0xe52cc12c, 0x1747422f,
0x49547e0b, 0xbb3ffd08, 0xa86f0efc, 0x5a048dff, 0x8ecee914, 0x7ca56a17, 0x6ff599e3, 0x9d9e1ae0,
0xd3d3e1ab, 0x21b862a8, 0x32e8915c, 0xc083125f, 0x144976b4, 0xe622f5b7, 0xf5720643, 0x07198540,
0x590ab964, 0xab613a67, 0xb831c993, 0x4a5a4a90, 0x9e902e7b, 0x6cfbad78, 0x7fab5e8c, 0x8dc0dd8f,
0xe330a81a, 0x115b2b19, 0x020bd8ed, 0xf0605bee, 0x24aa3f05, 0xd6c1bc06, 0xc5914ff2, 0x37faccf1,
0x69e9f0d5, 0x9b8273d6, 0x88d28022, 0x7ab90321, 0xae7367ca, 0x5c18e4c9, 0x4f48173d, 0xbd23943e,
0xf36e6f75, 0x0105ec76, 0x12551f82, 0xe03e9c81, 0x34f4f86a, 0xc69f7b69, 0xd5cf889d, 0x27a40b9e,
0x79b737ba, 0x8bdcb4b9, 0x988c474d, 0x6ae7c44e, 0xbe2da0a5, 0x4c4623a6, 0x5f16d052, 0xad7d5351,
};
#ifndef __REACTOS__
// HW code taken from https://github.com/rurban/smhasher/blob/master/crc32_hw.c
#define ALIGN_SIZE 0x08UL
#define ALIGN_MASK (ALIGN_SIZE - 1)
#define CALC_CRC(op, crc, type, buf, len) \
do { \
for (; (len) >= sizeof (type); (len) -= sizeof(type), buf += sizeof (type)) { \
(crc) = op((crc), *(type *) (buf)); \
} \
} while(0)
static UINT32 crc32c_hw(const void *input, int len, UINT32 crc) {
const char* buf = (const char*)input;
for (; (len > 0) && ((size_t)buf & ALIGN_MASK); len--, buf++) {
crc = _mm_crc32_u8(crc, *buf);
}
#ifdef __x86_64__
CALC_CRC(_mm_crc32_u64, crc, UINT64, buf, len);
#endif
CALC_CRC(_mm_crc32_u32, crc, UINT32, buf, len);
CALC_CRC(_mm_crc32_u16, crc, UINT16, buf, len);
CALC_CRC(_mm_crc32_u8, crc, UINT8, buf, len);
return crc;
}
#endif
UINT32 __stdcall calc_crc32c(UINT32 seed, UINT8* msg, ULONG msglen) {
UINT32 rem;
ULONG i;
#ifndef __REACTOS__
if (have_sse42) {
return crc32c_hw(msg, msglen, seed);
} else {
#endif
rem = seed;
for (i = 0; i < msglen; i++) {
rem = crctable[(rem ^ msg[i]) & 0xff] ^ (rem >> 8);
}
#ifndef __REACTOS__
}
#endif
return rem;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,879 @@
/* Copyright (c) Mark Harmstone 2016
*
* This file is part of WinBtrfs.
*
* WinBtrfs is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public Licence as published by
* the Free Software Foundation, either version 3 of the Licence, or
* (at your option) any later version.
*
* WinBtrfs is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public Licence for more details.
*
* You should have received a copy of the GNU Lesser General Public Licence
* along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */
#include "btrfs_drv.h"
enum DirEntryType {
DirEntryType_File,
DirEntryType_Self,
DirEntryType_Parent
};
typedef struct {
KEY key;
char* name;
ULONG namelen;
UINT8 type;
enum DirEntryType dir_entry_type;
} dir_entry;
static NTSTATUS STDCALL query_dir_item(fcb* fcb, void* buf, LONG* len, PIRP Irp, dir_entry* de, root* r) {
PIO_STACK_LOCATION IrpSp;
UINT32 needed;
UINT64 inode;
INODE_ITEM ii;
NTSTATUS Status;
ULONG stringlen;
BOOL dotfile;
IrpSp = IoGetCurrentIrpStackLocation(Irp);
if (de->key.obj_type == TYPE_ROOT_ITEM) { // subvol
r = fcb->Vcb->roots;
while (r && r->id != de->key.obj_id)
r = r->next;
if (!r) {
ERR("could not find root %llx\n", de->key.obj_id);
return STATUS_OBJECT_NAME_NOT_FOUND;
}
inode = SUBVOL_ROOT_INODE;
} else {
inode = de->key.obj_id;
}
if (IrpSp->Parameters.QueryDirectory.FileInformationClass != FileNamesInformation) { // FIXME - object ID and reparse point classes too?
switch (de->dir_entry_type) {
case DirEntryType_File:
{
LIST_ENTRY* le;
BOOL found = FALSE;
le = fcb->children.Flink;
while (le != &fcb->children) {
struct _fcb* c = CONTAINING_RECORD(le, struct _fcb, list_entry);
if (c->subvol == r && c->inode == inode) {
ii = c->inode_item;
found = TRUE;
break;
}
le = le->Flink;
}
if (!found) {
KEY searchkey;
traverse_ptr tp;
searchkey.obj_id = inode;
searchkey.obj_type = TYPE_INODE_ITEM;
searchkey.offset = 0xffffffffffffffff;
Status = find_item(fcb->Vcb, r, &tp, &searchkey, FALSE);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
}
if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
ERR("could not find inode item for inode %llx in root %llx\n", inode, r->id);
free_traverse_ptr(&tp);
return STATUS_INTERNAL_ERROR;
}
RtlZeroMemory(&ii, sizeof(INODE_ITEM));
if (tp.item->size > 0)
RtlCopyMemory(&ii, tp.item->data, min(sizeof(INODE_ITEM), tp.item->size));
free_traverse_ptr(&tp);
}
break;
}
case DirEntryType_Self:
ii = fcb->inode_item;
r = fcb->subvol;
inode = fcb->inode;
break;
case DirEntryType_Parent:
ii = fcb->par->inode_item;
r = fcb->par->subvol;
inode = fcb->par->inode;
break;
}
}
// FICs which return the filename
if (IrpSp->Parameters.QueryDirectory.FileInformationClass == FileBothDirectoryInformation ||
IrpSp->Parameters.QueryDirectory.FileInformationClass == FileDirectoryInformation ||
IrpSp->Parameters.QueryDirectory.FileInformationClass == FileFullDirectoryInformation ||
IrpSp->Parameters.QueryDirectory.FileInformationClass == FileIdBothDirectoryInformation ||
IrpSp->Parameters.QueryDirectory.FileInformationClass == FileNamesInformation) {
Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, de->name, de->namelen);
if (!NT_SUCCESS(Status)) {
ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
return Status;
}
}
dotfile = de->name[0] == '.' && (de->name[1] != '.' || de->name[2] != 0) && (de->name[1] != 0);
switch (IrpSp->Parameters.QueryDirectory.FileInformationClass) {
case FileBothDirectoryInformation:
{
FILE_BOTH_DIR_INFORMATION* fbdi = buf;
TRACE("FileBothDirectoryInformation\n");
needed = sizeof(FILE_BOTH_DIR_INFORMATION) - sizeof(WCHAR) + stringlen;
if (needed > *len) {
WARN("buffer overflow - %u > %u\n", needed, *len);
return STATUS_BUFFER_OVERFLOW;
}
fbdi->NextEntryOffset = 0;
fbdi->FileIndex = 0;
fbdi->CreationTime.QuadPart = unix_time_to_win(&ii.otime);
fbdi->LastAccessTime.QuadPart = unix_time_to_win(&ii.st_atime);
fbdi->LastWriteTime.QuadPart = unix_time_to_win(&ii.st_mtime);
fbdi->ChangeTime.QuadPart = 0;
fbdi->EndOfFile.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_size;
fbdi->AllocationSize.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_blocks;
fbdi->FileAttributes = get_file_attributes(fcb->Vcb, &ii, r, inode, de->type, dotfile, FALSE);
fbdi->FileNameLength = stringlen;
fbdi->EaSize = de->type == BTRFS_TYPE_SYMLINK ? IO_REPARSE_TAG_SYMLINK : 0;
fbdi->ShortNameLength = 0;
// fibdi->ShortName[12];
Status = RtlUTF8ToUnicodeN(fbdi->FileName, stringlen, &stringlen, de->name, de->namelen);
if (!NT_SUCCESS(Status)) {
ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
return Status;
}
*len -= needed;
return STATUS_SUCCESS;
}
case FileDirectoryInformation:
{
FILE_DIRECTORY_INFORMATION* fdi = buf;
TRACE("FileDirectoryInformation\n");
needed = sizeof(FILE_DIRECTORY_INFORMATION) - sizeof(WCHAR) + stringlen;
if (needed > *len) {
WARN("buffer overflow - %u > %u\n", needed, *len);
return STATUS_BUFFER_OVERFLOW;
}
fdi->NextEntryOffset = 0;
fdi->FileIndex = 0;
fdi->CreationTime.QuadPart = unix_time_to_win(&ii.otime);
fdi->LastAccessTime.QuadPart = unix_time_to_win(&ii.st_atime);
fdi->LastWriteTime.QuadPart = unix_time_to_win(&ii.st_mtime);
fdi->ChangeTime.QuadPart = 0;
fdi->EndOfFile.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_size;
fdi->AllocationSize.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_blocks;
fdi->FileAttributes = get_file_attributes(fcb->Vcb, &ii, r, inode, de->type, dotfile, FALSE);
fdi->FileNameLength = stringlen;
Status = RtlUTF8ToUnicodeN(fdi->FileName, stringlen, &stringlen, de->name, de->namelen);
if (!NT_SUCCESS(Status)) {
ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
return Status;
}
*len -= needed;
return STATUS_SUCCESS;
}
case FileFullDirectoryInformation:
{
FILE_FULL_DIR_INFORMATION* ffdi = buf;
TRACE("FileFullDirectoryInformation\n");
needed = sizeof(FILE_FULL_DIR_INFORMATION) - sizeof(WCHAR) + stringlen;
if (needed > *len) {
WARN("buffer overflow - %u > %u\n", needed, *len);
return STATUS_BUFFER_OVERFLOW;
}
ffdi->NextEntryOffset = 0;
ffdi->FileIndex = 0;
ffdi->CreationTime.QuadPart = unix_time_to_win(&ii.otime);
ffdi->LastAccessTime.QuadPart = unix_time_to_win(&ii.st_atime);
ffdi->LastWriteTime.QuadPart = unix_time_to_win(&ii.st_mtime);
ffdi->ChangeTime.QuadPart = 0;
ffdi->EndOfFile.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_size;
ffdi->AllocationSize.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_blocks;
ffdi->FileAttributes = get_file_attributes(fcb->Vcb, &ii, r, inode, de->type, dotfile, FALSE);
ffdi->FileNameLength = stringlen;
ffdi->EaSize = de->type == BTRFS_TYPE_SYMLINK ? IO_REPARSE_TAG_SYMLINK : 0;
Status = RtlUTF8ToUnicodeN(ffdi->FileName, stringlen, &stringlen, de->name, de->namelen);
if (!NT_SUCCESS(Status)) {
ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
return Status;
}
*len -= needed;
return STATUS_SUCCESS;
}
case FileIdBothDirectoryInformation:
{
FILE_ID_BOTH_DIR_INFORMATION* fibdi = buf;
TRACE("FileIdBothDirectoryInformation\n");
needed = sizeof(FILE_ID_BOTH_DIR_INFORMATION) - sizeof(WCHAR) + stringlen;
if (needed > *len) {
WARN("buffer overflow - %u > %u\n", needed, *len);
return STATUS_BUFFER_OVERFLOW;
}
// if (!buf)
// return STATUS_INVALID_POINTER;
fibdi->NextEntryOffset = 0;
fibdi->FileIndex = 0;
fibdi->CreationTime.QuadPart = unix_time_to_win(&ii.otime);
fibdi->LastAccessTime.QuadPart = unix_time_to_win(&ii.st_atime);
fibdi->LastWriteTime.QuadPart = unix_time_to_win(&ii.st_mtime);
fibdi->ChangeTime.QuadPart = 0;
fibdi->EndOfFile.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_size;
fibdi->AllocationSize.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_blocks;
fibdi->FileAttributes = get_file_attributes(fcb->Vcb, &ii, r, inode, de->type, dotfile, FALSE);
fibdi->FileNameLength = stringlen;
fibdi->EaSize = de->type == BTRFS_TYPE_SYMLINK ? IO_REPARSE_TAG_SYMLINK : 0;
fibdi->ShortNameLength = 0;
// fibdi->ShortName[12];
fibdi->FileId.QuadPart = inode;
Status = RtlUTF8ToUnicodeN(fibdi->FileName, stringlen, &stringlen, de->name, de->namelen);
if (!NT_SUCCESS(Status)) {
ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
return Status;
}
*len -= needed;
return STATUS_SUCCESS;
}
case FileIdFullDirectoryInformation:
FIXME("STUB: FileIdFullDirectoryInformation\n");
break;
case FileNamesInformation:
{
FILE_NAMES_INFORMATION* fni = buf;
TRACE("FileNamesInformation\n");
needed = sizeof(FILE_NAMES_INFORMATION) - sizeof(WCHAR) + stringlen;
if (needed > *len) {
WARN("buffer overflow - %u > %u\n", needed, *len);
return STATUS_BUFFER_OVERFLOW;
}
fni->NextEntryOffset = 0;
fni->FileIndex = 0;
fni->FileNameLength = stringlen;
Status = RtlUTF8ToUnicodeN(fni->FileName, stringlen, &stringlen, de->name, de->namelen);
if (!NT_SUCCESS(Status)) {
ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
return Status;
}
*len -= needed;
return STATUS_SUCCESS;
}
case FileObjectIdInformation:
FIXME("STUB: FileObjectIdInformation\n");
return STATUS_NOT_IMPLEMENTED;
case FileQuotaInformation:
FIXME("STUB: FileQuotaInformation\n");
return STATUS_NOT_IMPLEMENTED;
case FileReparsePointInformation:
FIXME("STUB: FileReparsePointInformation\n");
return STATUS_NOT_IMPLEMENTED;
default:
WARN("Unknown FileInformationClass %u\n", IrpSp->Parameters.QueryDirectory.FileInformationClass);
return STATUS_NOT_IMPLEMENTED;
}
return STATUS_NO_MORE_FILES;
}
static NTSTATUS STDCALL next_dir_entry(fcb* fcb, UINT64* offset, dir_entry* de, traverse_ptr* tp) {
KEY searchkey;
traverse_ptr next_tp;
DIR_ITEM* di;
NTSTATUS Status;
if (fcb->par) { // don't return . and .. if root directory
if (*offset == 0) {
de->key.obj_id = fcb->inode;
de->key.obj_type = TYPE_INODE_ITEM;
de->key.offset = 0;
de->dir_entry_type = DirEntryType_Self;
de->name = ".";
de->namelen = 1;
de->type = BTRFS_TYPE_DIRECTORY;
*offset = 1;
return STATUS_SUCCESS;
} else if (*offset == 1) {
de->key.obj_id = fcb->par->inode;
de->key.obj_type = TYPE_INODE_ITEM;
de->key.offset = 0;
de->dir_entry_type = DirEntryType_Parent;
de->name = "..";
de->namelen = 2;
de->type = BTRFS_TYPE_DIRECTORY;
*offset = 2;
return STATUS_SUCCESS;
}
}
if (!tp->tree) {
searchkey.obj_id = fcb->inode;
searchkey.obj_type = TYPE_DIR_INDEX;
searchkey.offset = *offset;
Status = find_item(fcb->Vcb, fcb->subvol, tp, &searchkey, FALSE);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
free_traverse_ptr(tp);
tp->tree = NULL;
return Status;
}
TRACE("found item %llx,%x,%llx\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset);
if (keycmp(&tp->item->key, &searchkey) == -1) {
if (find_next_item(fcb->Vcb, tp, &next_tp, FALSE)) {
free_traverse_ptr(tp);
*tp = next_tp;
TRACE("moving on to %llx,%x,%llx\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset);
}
}
if (tp->item->key.obj_id != searchkey.obj_id || tp->item->key.obj_type != searchkey.obj_type || tp->item->key.offset < *offset) {
free_traverse_ptr(tp);
tp->tree = NULL;
return STATUS_NO_MORE_FILES;
}
*offset = tp->item->key.offset + 1;
di = (DIR_ITEM*)tp->item->data;
if (tp->item->size < sizeof(DIR_ITEM) || tp->item->size < sizeof(DIR_ITEM) - 1 + di->m + di->n) {
ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset, tp->item->size, sizeof(DIR_ITEM));
free_traverse_ptr(tp);
tp->tree = NULL;
return STATUS_INTERNAL_ERROR;
}
de->key = di->key;
de->name = di->name;
de->namelen = di->n;
de->type = di->type;
de->dir_entry_type = DirEntryType_File;
return STATUS_SUCCESS;
} else {
if (find_next_item(fcb->Vcb, tp, &next_tp, FALSE)) {
if (next_tp.item->key.obj_type == TYPE_DIR_INDEX && next_tp.item->key.obj_id == tp->item->key.obj_id) {
free_traverse_ptr(tp);
*tp = next_tp;
*offset = tp->item->key.offset + 1;
di = (DIR_ITEM*)tp->item->data;
if (tp->item->size < sizeof(DIR_ITEM) || tp->item->size < sizeof(DIR_ITEM) - 1 + di->m + di->n) {
ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset, tp->item->size, sizeof(DIR_ITEM));
free_traverse_ptr(&next_tp);
return STATUS_INTERNAL_ERROR;
}
de->key = di->key;
de->name = di->name;
de->namelen = di->n;
de->type = di->type;
de->dir_entry_type = DirEntryType_File;
return STATUS_SUCCESS;
} else {
free_traverse_ptr(&next_tp);
return STATUS_NO_MORE_FILES;
}
} else
return STATUS_NO_MORE_FILES;
}
}
static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
PIO_STACK_LOCATION IrpSp;
NTSTATUS Status, status2;
fcb* fcb;
ccb* ccb;
void* buf;
UINT8 *curitem, *lastitem;
LONG length;
ULONG count;
BOOL has_wildcard = FALSE, specific_file = FALSE;
// UINT64 num_reads_orig;
traverse_ptr tp;
dir_entry de;
TRACE("query directory\n");
// get_uid(); // TESTING
// num_reads_orig = num_reads;
IrpSp = IoGetCurrentIrpStackLocation(Irp);
fcb = IrpSp->FileObject->FsContext;
ccb = IrpSp->FileObject->FsContext2;
acquire_tree_lock(fcb->Vcb, FALSE);
TRACE("%.*S\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
if (IrpSp->Flags == 0) {
TRACE("QD flags: (none)\n");
} else {
ULONG flags = IrpSp->Flags;
TRACE("QD flags:\n");
if (flags & SL_INDEX_SPECIFIED) {
TRACE(" SL_INDEX_SPECIFIED\n");
flags &= ~SL_INDEX_SPECIFIED;
}
if (flags & SL_RESTART_SCAN) {
TRACE(" SL_RESTART_SCAN\n");
flags &= ~SL_RESTART_SCAN;
}
if (flags & SL_RETURN_SINGLE_ENTRY) {
TRACE(" SL_RETURN_SINGLE_ENTRY\n");
flags &= ~SL_RETURN_SINGLE_ENTRY;
}
if (flags != 0)
TRACE(" unknown flags: %u\n", flags);
}
if (IrpSp->Flags & SL_RESTART_SCAN) {
ccb->query_dir_offset = 0;
if (ccb->query_string.Buffer) {
RtlFreeUnicodeString(&ccb->query_string);
ccb->query_string.Buffer = NULL;
}
}
if (IrpSp->Parameters.QueryDirectory.FileName) {
// int i;
// WCHAR* us;
TRACE("QD filename: %.*S\n", IrpSp->Parameters.QueryDirectory.FileName->Length / sizeof(WCHAR), IrpSp->Parameters.QueryDirectory.FileName->Buffer);
// if (IrpSp->Parameters.QueryDirectory.FileName->Length > 1 || IrpSp->Parameters.QueryDirectory.FileName->Buffer[0] != '*') {
// specific_file = TRUE;
// for (i = 0; i < IrpSp->Parameters.QueryDirectory.FileName->Length; i++) {
// if (IrpSp->Parameters.QueryDirectory.FileName->Buffer[i] == '?' || IrpSp->Parameters.QueryDirectory.FileName->Buffer[i] == '*') {
// has_wildcard = TRUE;
// specific_file = FALSE;
// }
// }
// }
has_wildcard = TRUE;
if (ccb->query_string.Buffer)
RtlFreeUnicodeString(&ccb->query_string);
// us = ExAllocatePoolWithTag(PagedPool, IrpSp->Parameters.QueryDirectory.FileName->Length + sizeof(WCHAR), ALLOC_TAG);
// RtlCopyMemory(us, IrpSp->Parameters.QueryDirectory.FileName->Buffer, IrpSp->Parameters.QueryDirectory.FileName->Length);
// us[IrpSp->Parameters.QueryDirectory.FileName->Length / sizeof(WCHAR)] = 0;
// ccb->query_string = ExAllocatePoolWithTag(NonPagedPool, utf16_to_utf8_len(us), ALLOC_TAG);
// utf16_to_utf8(us, ccb->query_string);
// ccb->query_string.Buffer = ExAllocatePoolWithTag(PagedPool, IrpSp->Parameters.QueryDirectory.FileName->Length, ALLOC_TAG);
// RtlCopyMemory(ccb->query_string.Buffer, IrpSp->Parameters.QueryDirectory.FileName->Buffer,
// IrpSp->Parameters.QueryDirectory.FileName->Length);
// ccb->query_string.Length = IrpSp->Parameters.QueryDirectory.FileName->Length;
// ccb->query_string.MaximumLength = IrpSp->Parameters.QueryDirectory.FileName->Length;
RtlUpcaseUnicodeString(&ccb->query_string, IrpSp->Parameters.QueryDirectory.FileName, TRUE);
ccb->has_wildcard = has_wildcard;
ccb->specific_file = specific_file;
// ExFreePool(us);
} else {
has_wildcard = ccb->has_wildcard;
specific_file = ccb->specific_file;
}
if (ccb->query_string.Buffer) {
TRACE("query string = %.*S\n", ccb->query_string.Length / sizeof(WCHAR), ccb->query_string.Buffer);
}
tp.tree = NULL;
Status = next_dir_entry(fcb, &ccb->query_dir_offset, &de, &tp);
if (!NT_SUCCESS(Status)) {
if (Status == STATUS_NO_MORE_FILES && IrpSp->Flags & SL_RETURN_SINGLE_ENTRY)
Status = STATUS_NO_SUCH_FILE;
goto end;
}
// FIXME - make this work
// if (specific_file) {
// UINT64 filesubvol, fileinode;
// WCHAR* us;
//
// us = ExAllocatePoolWithTag(NonPagedPool, IrpSp->Parameters.QueryDirectory.FileName->Length + sizeof(WCHAR), ALLOC_TAG);
// RtlCopyMemory(us, IrpSp->Parameters.QueryDirectory.FileName->Buffer, IrpSp->Parameters.QueryDirectory.FileName->Length);
// us[IrpSp->Parameters.QueryDirectory.FileName->Length / sizeof(WCHAR)] = 0;
//
// if (!find_file_in_dir(fcb->Vcb, us, fcb->subvolume, fcb->inode, &filesubvol, &fileinode)) {
// ExFreePool(us);
// return STATUS_NO_MORE_FILES;
// }
//
// ExFreePool(us);
// }
buf = map_user_buffer(Irp);
if (Irp->MdlAddress && !buf) {
ERR("MmGetSystemAddressForMdlSafe returned NULL\n");
Status = STATUS_INSUFFICIENT_RESOURCES;
if (tp.tree) free_traverse_ptr(&tp);
goto end;
}
length = IrpSp->Parameters.QueryDirectory.Length;
// if (specific_file) {
if (has_wildcard) {
WCHAR* uni_fn;
ULONG stringlen;
UNICODE_STRING di_uni_fn;
Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, de.name, de.namelen);
if (!NT_SUCCESS(Status)) {
ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
if (tp.tree) free_traverse_ptr(&tp);
goto end;
}
uni_fn = ExAllocatePoolWithTag(PagedPool, stringlen, ALLOC_TAG);
if (!uni_fn) {
ERR("out of memory\n");
if (tp.tree) free_traverse_ptr(&tp);
Status = STATUS_INSUFFICIENT_RESOURCES;
goto end;
}
Status = RtlUTF8ToUnicodeN(uni_fn, stringlen, &stringlen, de.name, de.namelen);
if (!NT_SUCCESS(Status)) {
ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
if (tp.tree) free_traverse_ptr(&tp);
goto end;
}
di_uni_fn.Length = di_uni_fn.MaximumLength = stringlen;
di_uni_fn.Buffer = uni_fn;
while (!FsRtlIsNameInExpression(&ccb->query_string, &di_uni_fn, TRUE, NULL)) {
Status = next_dir_entry(fcb, &ccb->query_dir_offset, &de, &tp);
ExFreePool(uni_fn);
if (NT_SUCCESS(Status)) {
Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, de.name, de.namelen);
if (!NT_SUCCESS(Status)) {
ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
if (tp.tree) free_traverse_ptr(&tp);
goto end;
}
uni_fn = ExAllocatePoolWithTag(PagedPool, stringlen, ALLOC_TAG);
if (!uni_fn) {
ERR("out of memory\n");
if (tp.tree) free_traverse_ptr(&tp);
Status = STATUS_INSUFFICIENT_RESOURCES;
goto end;
}
Status = RtlUTF8ToUnicodeN(uni_fn, stringlen, &stringlen, de.name, de.namelen);
if (!NT_SUCCESS(Status)) {
ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
ExFreePool(uni_fn);
if (tp.tree) free_traverse_ptr(&tp);
goto end;
}
di_uni_fn.Length = di_uni_fn.MaximumLength = stringlen;
di_uni_fn.Buffer = uni_fn;
} else {
if (tp.tree) free_traverse_ptr(&tp);
if (Status == STATUS_NO_MORE_FILES && IrpSp->Flags & SL_RETURN_SINGLE_ENTRY)
Status = STATUS_NO_SUCH_FILE;
goto end;
}
}
ExFreePool(uni_fn);
}
TRACE("file(0) = %.*s\n", de.namelen, de.name);
TRACE("offset = %u\n", ccb->query_dir_offset - 1);
Status = query_dir_item(fcb, buf, &length, Irp, &de, fcb->subvol);
count = 0;
if (NT_SUCCESS(Status) && !(IrpSp->Flags & SL_RETURN_SINGLE_ENTRY) && !specific_file) {
lastitem = (UINT8*)buf;
while (length > 0) {
switch (IrpSp->Parameters.QueryDirectory.FileInformationClass) {
case FileBothDirectoryInformation:
case FileDirectoryInformation:
case FileIdBothDirectoryInformation:
case FileFullDirectoryInformation:
length -= length % 8;
break;
case FileNamesInformation:
length -= length % 4;
break;
default:
WARN("unhandled file information class %u\n", IrpSp->Parameters.QueryDirectory.FileInformationClass);
break;
}
if (length > 0) {
WCHAR* uni_fn = NULL;
UNICODE_STRING di_uni_fn;
Status = next_dir_entry(fcb, &ccb->query_dir_offset, &de, &tp);
if (NT_SUCCESS(Status)) {
if (has_wildcard) {
ULONG stringlen;
Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, de.name, de.namelen);
if (!NT_SUCCESS(Status)) {
ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
if (tp.tree) free_traverse_ptr(&tp);
goto end;
}
uni_fn = ExAllocatePoolWithTag(PagedPool, stringlen, ALLOC_TAG);
if (!uni_fn) {
ERR("out of memory\n");
if (tp.tree) free_traverse_ptr(&tp);
Status = STATUS_INSUFFICIENT_RESOURCES;
goto end;
}
Status = RtlUTF8ToUnicodeN(uni_fn, stringlen, &stringlen, de.name, de.namelen);
if (!NT_SUCCESS(Status)) {
ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
ExFreePool(uni_fn);
if (tp.tree) free_traverse_ptr(&tp);
goto end;
}
di_uni_fn.Length = di_uni_fn.MaximumLength = stringlen;
di_uni_fn.Buffer = uni_fn;
}
if (!has_wildcard || FsRtlIsNameInExpression(&ccb->query_string, &di_uni_fn, TRUE, NULL)) {
curitem = (UINT8*)buf + IrpSp->Parameters.QueryDirectory.Length - length;
count++;
TRACE("file(%u) %u = %.*s\n", count, curitem - (UINT8*)buf, de.namelen, de.name);
TRACE("offset = %u\n", ccb->query_dir_offset - 1);
status2 = query_dir_item(fcb, curitem, &length, Irp, &de, fcb->subvol);
if (NT_SUCCESS(status2)) {
ULONG* lastoffset = (ULONG*)lastitem;
*lastoffset = (ULONG)(curitem - lastitem);
lastitem = curitem;
} else {
if (uni_fn) ExFreePool(uni_fn);
break;
}
}
if (uni_fn) {
ExFreePool(uni_fn);
uni_fn = NULL;
}
} else {
if (Status == STATUS_NO_MORE_FILES)
Status = STATUS_SUCCESS;
break;
}
} else
break;
}
}
Irp->IoStatus.Information = IrpSp->Parameters.QueryDirectory.Length - length;
if (tp.tree) free_traverse_ptr(&tp);
end:
release_tree_lock(fcb->Vcb, FALSE);
// TRACE("query directory performed %u reads\n", (UINT32)(num_reads-num_reads_orig));
TRACE("returning %08x\n", Status);
return Status;
}
static NTSTATUS STDCALL notify_change_directory(device_extension* Vcb, PIRP Irp) {
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
PFILE_OBJECT FileObject = IrpSp->FileObject;
fcb* fcb = FileObject->FsContext;
NTSTATUS Status;
// WCHAR fn[MAX_PATH];
TRACE("IRP_MN_NOTIFY_CHANGE_DIRECTORY\n");
acquire_tree_lock(fcb->Vcb, FALSE);
if (fcb->type != BTRFS_TYPE_DIRECTORY) {
Status = STATUS_INVALID_PARAMETER;
goto end;
}
// FIXME - raise exception if FCB marked for deletion?
TRACE("%.*S\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
FsRtlNotifyFullChangeDirectory(Vcb->NotifySync, &Vcb->DirNotifyList, FileObject->FsContext2, (PSTRING)&fcb->full_filename,
IrpSp->Flags & SL_WATCH_TREE, FALSE, IrpSp->Parameters.NotifyDirectory.CompletionFilter, Irp, NULL, NULL);
Status = STATUS_PENDING;
end:
release_tree_lock(fcb->Vcb, FALSE);
return Status;
}
NTSTATUS STDCALL drv_directory_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
PIO_STACK_LOCATION IrpSp;
NTSTATUS Status;
ULONG func;
BOOL top_level;
TRACE("directory control\n");
FsRtlEnterFileSystem();
top_level = is_top_level(Irp);
IrpSp = IoGetCurrentIrpStackLocation(Irp);
Irp->IoStatus.Information = 0;
func = IrpSp->MinorFunction;
switch (func) {
case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
Status = notify_change_directory(DeviceObject->DeviceExtension, Irp);
break;
case IRP_MN_QUERY_DIRECTORY:
Status = query_directory(DeviceObject, Irp);
break;
default:
WARN("unknown minor %u\n", func);
Status = STATUS_NOT_IMPLEMENTED;
Irp->IoStatus.Status = Status;
break;
}
if (func != IRP_MN_NOTIFY_CHANGE_DIRECTORY || Status != STATUS_PENDING) {
Irp->IoStatus.Status = Status;
IoCompleteRequest( Irp, IO_DISK_INCREMENT );
}
if (top_level)
IoSetTopLevelIrp(NULL);
FsRtlExitFileSystem();
return Status;
}

View file

@ -0,0 +1,182 @@
/* Copyright (c) Mark Harmstone 2016
*
* This file is part of WinBtrfs.
*
* WinBtrfs is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public Licence as published by
* the Free Software Foundation, either version 3 of the Licence, or
* (at your option) any later version.
*
* WinBtrfs is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public Licence for more details.
*
* You should have received a copy of the GNU Lesser General Public Licence
* along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */
#include <ntifs.h>
#include "btrfs_drv.h"
FAST_IO_DISPATCH FastIoDispatch;
static void STDCALL acquire_file_for_create_section(PFILE_OBJECT FileObject) {
TRACE("STUB: acquire_file_for_create_section\n");
}
static void STDCALL release_file_for_create_section(PFILE_OBJECT FileObject) {
TRACE("STUB: release_file_for_create_section\n");
}
static BOOLEAN STDCALL fast_query_basic_info(PFILE_OBJECT FileObject, BOOLEAN wait, PFILE_BASIC_INFORMATION buf,
PIO_STATUS_BLOCK IoStatus, PDEVICE_OBJECT DeviceObject) {
TRACE("STUB: fast_query_basic_info\n");
return FALSE;
}
static BOOLEAN STDCALL fast_query_standard_info(PFILE_OBJECT FileObject, BOOLEAN wait, PFILE_STANDARD_INFORMATION buf,
PIO_STATUS_BLOCK IoStatus, PDEVICE_OBJECT DeviceObject) {
TRACE("STUB: fast_query_standard_info\n");
return FALSE;
}
static BOOLEAN STDCALL fast_io_query_open(PIRP Irp, PFILE_NETWORK_OPEN_INFORMATION NetworkInformation, PDEVICE_OBJECT DeviceObject) {
TRACE("STUB: fast_io_query_open\n");
return FALSE;
}
static BOOLEAN STDCALL fast_io_check_if_possible(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, ULONG Length, BOOLEAN Wait,
ULONG LockKey, BOOLEAN CheckForReadOperation, PIO_STATUS_BLOCK IoStatus,
PDEVICE_OBJECT DeviceObject) {
fcb* fcb = FileObject->FsContext;
LARGE_INTEGER len2;
TRACE("(%p, %llx, %x, %x, %x, %x, %p, %p)\n", FileObject, FileOffset->QuadPart, Length, Wait, LockKey, CheckForReadOperation, IoStatus, DeviceObject);
len2.QuadPart = Length;
if (CheckForReadOperation) {
if (FsRtlFastCheckLockForRead(&fcb->lock, FileOffset, &len2, LockKey, FileObject, PsGetCurrentProcess()))
return TRUE;
} else {
if (!fcb->Vcb->readonly && !(fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY) && FsRtlFastCheckLockForWrite(&fcb->lock, FileOffset, &len2, LockKey, FileObject, PsGetCurrentProcess()))
return TRUE;
}
return FALSE;
}
static BOOLEAN STDCALL fast_io_lock(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, PLARGE_INTEGER Length, PEPROCESS ProcessId, ULONG Key, BOOLEAN FailImmediately, BOOLEAN ExclusiveLock, PIO_STATUS_BLOCK IoStatus, PDEVICE_OBJECT DeviceObject){
TRACE("STUB: fast_io_lock\n");
return FALSE;
}
static BOOLEAN STDCALL fast_io_unlock_single(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, PLARGE_INTEGER Length, PEPROCESS ProcessId, ULONG Key, PIO_STATUS_BLOCK IoStatus, PDEVICE_OBJECT DeviceObject){
TRACE("STUB: fast_io_unlock_single\n");
return FALSE;
}
static BOOLEAN STDCALL fast_io_unlock_all(PFILE_OBJECT FileObject, PEPROCESS ProcessId, PIO_STATUS_BLOCK IoStatus, PDEVICE_OBJECT DeviceObject){
TRACE("STUB: fast_io_unlock_all\n");
return FALSE;
}
static BOOLEAN STDCALL fast_io_unlock_all_by_key(PFILE_OBJECT FileObject, PVOID ProcessId, ULONG Key, PIO_STATUS_BLOCK IoStatus, PDEVICE_OBJECT DeviceObject){
TRACE("STUB: fast_io_unlock_all_by_key\n");
return FALSE;
}
static BOOLEAN STDCALL fast_io_device_control(PFILE_OBJECT FileObject, BOOLEAN Wait, PVOID InputBuffer OPTIONAL, ULONG InputBufferLength, PVOID OutputBuffer OPTIONAL, ULONG OutputBufferLength, ULONG IoControlCode, PIO_STATUS_BLOCK IoStatus, PDEVICE_OBJECT DeviceObject){
TRACE("STUB: fast_io_device_control\n");
return FALSE;
}
static VOID STDCALL fast_io_detach_device(PDEVICE_OBJECT SourceDevice, PDEVICE_OBJECT TargetDevice){
TRACE("STUB: fast_io_detach_device\n");
}
static BOOLEAN STDCALL fast_io_query_network_open_info(PFILE_OBJECT FileObject, BOOLEAN Wait, struct _FILE_NETWORK_OPEN_INFORMATION *Buffer, struct _IO_STATUS_BLOCK *IoStatus, PDEVICE_OBJECT DeviceObject){
TRACE("STUB: fast_io_query_network_open_info\n");
return FALSE;
}
static NTSTATUS STDCALL fast_io_acquire_for_mod_write(PFILE_OBJECT FileObject, PLARGE_INTEGER EndingOffset, struct _ERESOURCE **ResourceToRelease, PDEVICE_OBJECT DeviceObject){
TRACE("STUB: fast_io_acquire_for_mod_write\n");
return STATUS_NOT_IMPLEMENTED;
}
static BOOLEAN STDCALL fast_io_read_compressed(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, ULONG Length, ULONG LockKey, PVOID Buffer, PMDL *MdlChain, PIO_STATUS_BLOCK IoStatus, struct _COMPRESSED_DATA_INFO *CompressedDataInfo, ULONG CompressedDataInfoLength, PDEVICE_OBJECT DeviceObject){
TRACE("STUB: fast_io_read_compressed\n");
return FALSE;
}
static BOOLEAN STDCALL fast_io_write_compressed(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, ULONG Length, ULONG LockKey, PVOID Buffer, PMDL *MdlChain, PIO_STATUS_BLOCK IoStatus, struct _COMPRESSED_DATA_INFO *CompressedDataInfo, ULONG CompressedDataInfoLength, PDEVICE_OBJECT DeviceObject){
TRACE("STUB: fast_io_write_compressed\n");
return FALSE;
}
static BOOLEAN STDCALL fast_io_mdl_read_complete_compressed(PFILE_OBJECT FileObject, PMDL MdlChain, PDEVICE_OBJECT DeviceObject){
TRACE("STUB: fast_io_mdl_read_complete_compressed\n");
return FALSE;
}
static BOOLEAN STDCALL fast_io_mdl_write_complete_compressed(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, PMDL MdlChain, PDEVICE_OBJECT DeviceObject){
TRACE("STUB: fast_io_mdl_write_complete_compressed\n");
return FALSE;
}
static NTSTATUS STDCALL fast_io_release_for_mod_write(PFILE_OBJECT FileObject, struct _ERESOURCE *ResourceToRelease, PDEVICE_OBJECT DeviceObject){
TRACE("STUB: fast_io_release_for_mod_write\n");
return STATUS_NOT_IMPLEMENTED;
}
static NTSTATUS STDCALL fast_io_acquire_for_ccflush(PFILE_OBJECT FileObject, PDEVICE_OBJECT DeviceObject){
TRACE("STUB: fast_io_acquire_for_ccflush\n");
return STATUS_NOT_IMPLEMENTED;
}
static NTSTATUS STDCALL fast_io_release_for_ccflush(PFILE_OBJECT FileObject, PDEVICE_OBJECT DeviceObject){
TRACE("STUB: fast_io_release_for_ccflush\n");
return STATUS_NOT_IMPLEMENTED;
}
void __stdcall init_fast_io_dispatch(FAST_IO_DISPATCH** fiod) {
RtlZeroMemory(&FastIoDispatch, sizeof(FastIoDispatch));
FastIoDispatch.SizeOfFastIoDispatch = sizeof(FAST_IO_DISPATCH);
FastIoDispatch.FastIoCheckIfPossible = fast_io_check_if_possible;
FastIoDispatch.FastIoRead = FsRtlCopyRead;
FastIoDispatch.FastIoWrite = FsRtlCopyWrite;
FastIoDispatch.FastIoQueryBasicInfo = fast_query_basic_info;
FastIoDispatch.FastIoQueryStandardInfo = fast_query_standard_info;
FastIoDispatch.FastIoLock = fast_io_lock;
FastIoDispatch.FastIoUnlockSingle = fast_io_unlock_single;
FastIoDispatch.FastIoUnlockAll = fast_io_unlock_all;
FastIoDispatch.FastIoUnlockAllByKey = fast_io_unlock_all_by_key;
FastIoDispatch.FastIoDeviceControl = fast_io_device_control;
FastIoDispatch.AcquireFileForNtCreateSection = acquire_file_for_create_section;
FastIoDispatch.ReleaseFileForNtCreateSection = release_file_for_create_section;
FastIoDispatch.FastIoDetachDevice = fast_io_detach_device;
FastIoDispatch.FastIoQueryNetworkOpenInfo = fast_io_query_network_open_info;
FastIoDispatch.AcquireForModWrite = fast_io_acquire_for_mod_write;
FastIoDispatch.MdlRead = FsRtlMdlReadDev;
FastIoDispatch.MdlReadComplete = FsRtlMdlReadCompleteDev;
FastIoDispatch.PrepareMdlWrite = FsRtlPrepareMdlWriteDev;
FastIoDispatch.MdlWriteComplete = FsRtlMdlWriteCompleteDev;
FastIoDispatch.FastIoReadCompressed = fast_io_read_compressed;
FastIoDispatch.FastIoWriteCompressed = fast_io_write_compressed;
FastIoDispatch.MdlReadCompleteCompressed = fast_io_mdl_read_complete_compressed;
FastIoDispatch.MdlWriteCompleteCompressed = fast_io_mdl_write_complete_compressed;
FastIoDispatch.FastIoQueryOpen = fast_io_query_open;
FastIoDispatch.ReleaseForModWrite = fast_io_release_for_mod_write;
FastIoDispatch.AcquireForCcFlush = fast_io_acquire_for_ccflush;
FastIoDispatch.ReleaseForCcFlush = fast_io_release_for_ccflush;
*fiod = &FastIoDispatch;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,63 @@
/* Copyright (c) Mark Harmstone 2016
*
* This file is part of WinBtrfs.
*
* WinBtrfs is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public Licence as published by
* the Free Software Foundation, either version 3 of the Licence, or
* (at your option) any later version.
*
* WinBtrfs is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public Licence for more details.
*
* You should have received a copy of the GNU Lesser General Public Licence
* along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */
#include "btrfs_drv.h"
#define INTERVAL 15000 // in milliseconds
static void do_flush(device_extension* Vcb) {
LIST_ENTRY rollback;
InitializeListHead(&rollback);
FsRtlEnterFileSystem();
acquire_tree_lock(Vcb, TRUE);
if (Vcb->write_trees > 0)
do_write(Vcb, &rollback);
free_tree_cache(&Vcb->tree_cache);
clear_rollback(&rollback);
release_tree_lock(Vcb, TRUE);
FsRtlExitFileSystem();
}
void STDCALL flush_thread(void* context) {
device_extension* Vcb = context;
LARGE_INTEGER due_time;
KeInitializeTimer(&Vcb->flush_thread_timer);
due_time.QuadPart = -INTERVAL * 10000;
KeSetTimer(&Vcb->flush_thread_timer, due_time, NULL);
while (TRUE) {
KeWaitForSingleObject(&Vcb->flush_thread_timer, Executive, KernelMode, FALSE, NULL);
do_flush(Vcb);
KeSetTimer(&Vcb->flush_thread_timer, due_time, NULL);
}
KeCancelTimer(&Vcb->flush_thread_timer);
PsTerminateSystemThread(STATUS_SUCCESS);
}

View file

@ -0,0 +1,510 @@
/* Copyright (c) Mark Harmstone 2016
*
* This file is part of WinBtrfs.
*
* WinBtrfs is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public Licence as published by
* the Free Software Foundation, either version 3 of the Licence, or
* (at your option) any later version.
*
* WinBtrfs is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public Licence for more details.
*
* You should have received a copy of the GNU Lesser General Public Licence
* along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */
#include "btrfs_drv.h"
#ifndef FSCTL_CSV_CONTROL
#define FSCTL_CSV_CONTROL CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 181, METHOD_BUFFERED, FILE_ANY_ACCESS)
#endif
NTSTATUS fsctl_request(PDEVICE_OBJECT DeviceObject, PIRP Irp, UINT32 type, BOOL user) {
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
NTSTATUS Status;
switch (type) {
case FSCTL_REQUEST_OPLOCK_LEVEL_1:
WARN("STUB: FSCTL_REQUEST_OPLOCK_LEVEL_1\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_REQUEST_OPLOCK_LEVEL_2:
WARN("STUB: FSCTL_REQUEST_OPLOCK_LEVEL_2\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_REQUEST_BATCH_OPLOCK:
WARN("STUB: FSCTL_REQUEST_BATCH_OPLOCK\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE:
WARN("STUB: FSCTL_OPLOCK_BREAK_ACKNOWLEDGE\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_OPBATCH_ACK_CLOSE_PENDING:
WARN("STUB: FSCTL_OPBATCH_ACK_CLOSE_PENDING\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_OPLOCK_BREAK_NOTIFY:
WARN("STUB: FSCTL_OPLOCK_BREAK_NOTIFY\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_LOCK_VOLUME:
WARN("STUB: FSCTL_LOCK_VOLUME\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_UNLOCK_VOLUME:
WARN("STUB: FSCTL_UNLOCK_VOLUME\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_DISMOUNT_VOLUME:
WARN("STUB: FSCTL_DISMOUNT_VOLUME\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_IS_VOLUME_MOUNTED:
WARN("STUB: FSCTL_IS_VOLUME_MOUNTED\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_IS_PATHNAME_VALID:
WARN("STUB: FSCTL_IS_PATHNAME_VALID\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_MARK_VOLUME_DIRTY:
WARN("STUB: FSCTL_MARK_VOLUME_DIRTY\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_QUERY_RETRIEVAL_POINTERS:
WARN("STUB: FSCTL_QUERY_RETRIEVAL_POINTERS\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_GET_COMPRESSION:
WARN("STUB: FSCTL_GET_COMPRESSION\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_SET_COMPRESSION:
WARN("STUB: FSCTL_SET_COMPRESSION\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_SET_BOOTLOADER_ACCESSED:
WARN("STUB: FSCTL_SET_BOOTLOADER_ACCESSED\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_OPLOCK_BREAK_ACK_NO_2:
WARN("STUB: FSCTL_OPLOCK_BREAK_ACK_NO_2\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_INVALIDATE_VOLUMES:
WARN("STUB: FSCTL_INVALIDATE_VOLUMES\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_QUERY_FAT_BPB:
WARN("STUB: FSCTL_QUERY_FAT_BPB\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_REQUEST_FILTER_OPLOCK:
WARN("STUB: FSCTL_REQUEST_FILTER_OPLOCK\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_FILESYSTEM_GET_STATISTICS:
WARN("STUB: FSCTL_FILESYSTEM_GET_STATISTICS\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_GET_NTFS_VOLUME_DATA:
WARN("STUB: FSCTL_GET_NTFS_VOLUME_DATA\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_GET_NTFS_FILE_RECORD:
WARN("STUB: FSCTL_GET_NTFS_FILE_RECORD\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_GET_VOLUME_BITMAP:
WARN("STUB: FSCTL_GET_VOLUME_BITMAP\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_GET_RETRIEVAL_POINTERS:
WARN("STUB: FSCTL_GET_RETRIEVAL_POINTERS\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_MOVE_FILE:
WARN("STUB: FSCTL_MOVE_FILE\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_IS_VOLUME_DIRTY:
WARN("STUB: FSCTL_IS_VOLUME_DIRTY\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_ALLOW_EXTENDED_DASD_IO:
WARN("STUB: FSCTL_ALLOW_EXTENDED_DASD_IO\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_FIND_FILES_BY_SID:
WARN("STUB: FSCTL_FIND_FILES_BY_SID\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_SET_OBJECT_ID:
WARN("STUB: FSCTL_SET_OBJECT_ID\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_GET_OBJECT_ID:
WARN("STUB: FSCTL_GET_OBJECT_ID\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_DELETE_OBJECT_ID:
WARN("STUB: FSCTL_DELETE_OBJECT_ID\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_SET_REPARSE_POINT:
Status = set_reparse_point(DeviceObject, Irp);
break;
case FSCTL_GET_REPARSE_POINT:
Status = get_reparse_point(DeviceObject, IrpSp->FileObject, Irp->AssociatedIrp.SystemBuffer,
IrpSp->Parameters.DeviceIoControl.OutputBufferLength, &Irp->IoStatus.Information);
break;
case FSCTL_DELETE_REPARSE_POINT:
WARN("STUB: FSCTL_DELETE_REPARSE_POINT\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_ENUM_USN_DATA:
WARN("STUB: FSCTL_ENUM_USN_DATA\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_SECURITY_ID_CHECK:
WARN("STUB: FSCTL_SECURITY_ID_CHECK\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_READ_USN_JOURNAL:
WARN("STUB: FSCTL_READ_USN_JOURNAL\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_SET_OBJECT_ID_EXTENDED:
WARN("STUB: FSCTL_SET_OBJECT_ID_EXTENDED\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_CREATE_OR_GET_OBJECT_ID:
WARN("STUB: FSCTL_CREATE_OR_GET_OBJECT_ID\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_SET_SPARSE:
WARN("STUB: FSCTL_SET_SPARSE\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_SET_ZERO_DATA:
WARN("STUB: FSCTL_SET_ZERO_DATA\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_QUERY_ALLOCATED_RANGES:
WARN("STUB: FSCTL_QUERY_ALLOCATED_RANGES\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_ENABLE_UPGRADE:
WARN("STUB: FSCTL_ENABLE_UPGRADE\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_SET_ENCRYPTION:
WARN("STUB: FSCTL_SET_ENCRYPTION\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_ENCRYPTION_FSCTL_IO:
WARN("STUB: FSCTL_ENCRYPTION_FSCTL_IO\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_WRITE_RAW_ENCRYPTED:
WARN("STUB: FSCTL_WRITE_RAW_ENCRYPTED\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_READ_RAW_ENCRYPTED:
WARN("STUB: FSCTL_READ_RAW_ENCRYPTED\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_CREATE_USN_JOURNAL:
WARN("STUB: FSCTL_CREATE_USN_JOURNAL\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_READ_FILE_USN_DATA:
WARN("STUB: FSCTL_READ_FILE_USN_DATA\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_WRITE_USN_CLOSE_RECORD:
WARN("STUB: FSCTL_WRITE_USN_CLOSE_RECORD\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_EXTEND_VOLUME:
WARN("STUB: FSCTL_EXTEND_VOLUME\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_QUERY_USN_JOURNAL:
WARN("STUB: FSCTL_QUERY_USN_JOURNAL\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_DELETE_USN_JOURNAL:
WARN("STUB: FSCTL_DELETE_USN_JOURNAL\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_MARK_HANDLE:
WARN("STUB: FSCTL_MARK_HANDLE\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_SIS_COPYFILE:
WARN("STUB: FSCTL_SIS_COPYFILE\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_SIS_LINK_FILES:
WARN("STUB: FSCTL_SIS_LINK_FILES\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_RECALL_FILE:
WARN("STUB: FSCTL_RECALL_FILE\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_READ_FROM_PLEX:
WARN("STUB: FSCTL_READ_FROM_PLEX\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_FILE_PREFETCH:
WARN("STUB: FSCTL_FILE_PREFETCH\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
#if WIN32_WINNT >= 0x0600
case FSCTL_MAKE_MEDIA_COMPATIBLE:
WARN("STUB: FSCTL_MAKE_MEDIA_COMPATIBLE\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_SET_DEFECT_MANAGEMENT:
WARN("STUB: FSCTL_SET_DEFECT_MANAGEMENT\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_QUERY_SPARING_INFO:
WARN("STUB: FSCTL_QUERY_SPARING_INFO\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_QUERY_ON_DISK_VOLUME_INFO:
WARN("STUB: FSCTL_QUERY_ON_DISK_VOLUME_INFO\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_SET_VOLUME_COMPRESSION_STATE:
WARN("STUB: FSCTL_SET_VOLUME_COMPRESSION_STATE\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_TXFS_MODIFY_RM:
WARN("STUB: FSCTL_TXFS_MODIFY_RM\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_TXFS_QUERY_RM_INFORMATION:
WARN("STUB: FSCTL_TXFS_QUERY_RM_INFORMATION\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_TXFS_ROLLFORWARD_REDO:
WARN("STUB: FSCTL_TXFS_ROLLFORWARD_REDO\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_TXFS_ROLLFORWARD_UNDO:
WARN("STUB: FSCTL_TXFS_ROLLFORWARD_UNDO\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_TXFS_START_RM:
WARN("STUB: FSCTL_TXFS_START_RM\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_TXFS_SHUTDOWN_RM:
WARN("STUB: FSCTL_TXFS_SHUTDOWN_RM\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_TXFS_READ_BACKUP_INFORMATION:
WARN("STUB: FSCTL_TXFS_READ_BACKUP_INFORMATION\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_TXFS_WRITE_BACKUP_INFORMATION:
WARN("STUB: FSCTL_TXFS_WRITE_BACKUP_INFORMATION\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_TXFS_CREATE_SECONDARY_RM:
WARN("STUB: FSCTL_TXFS_CREATE_SECONDARY_RM\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_TXFS_GET_METADATA_INFO:
WARN("STUB: FSCTL_TXFS_GET_METADATA_INFO\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_TXFS_GET_TRANSACTED_VERSION:
WARN("STUB: FSCTL_TXFS_GET_TRANSACTED_VERSION\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_TXFS_SAVEPOINT_INFORMATION:
WARN("STUB: FSCTL_TXFS_SAVEPOINT_INFORMATION\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_TXFS_CREATE_MINIVERSION:
WARN("STUB: FSCTL_TXFS_CREATE_MINIVERSION\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_TXFS_TRANSACTION_ACTIVE:
WARN("STUB: FSCTL_TXFS_TRANSACTION_ACTIVE\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_SET_ZERO_ON_DEALLOCATION:
WARN("STUB: FSCTL_SET_ZERO_ON_DEALLOCATION\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_SET_REPAIR:
WARN("STUB: FSCTL_SET_REPAIR\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_GET_REPAIR:
WARN("STUB: FSCTL_GET_REPAIR\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_WAIT_FOR_REPAIR:
WARN("STUB: FSCTL_WAIT_FOR_REPAIR\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_INITIATE_REPAIR:
WARN("STUB: FSCTL_INITIATE_REPAIR\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_CSC_INTERNAL:
WARN("STUB: FSCTL_CSC_INTERNAL\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_SHRINK_VOLUME:
WARN("STUB: FSCTL_SHRINK_VOLUME\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_SET_SHORT_NAME_BEHAVIOR:
WARN("STUB: FSCTL_SET_SHORT_NAME_BEHAVIOR\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_DFSR_SET_GHOST_HANDLE_STATE:
WARN("STUB: FSCTL_DFSR_SET_GHOST_HANDLE_STATE\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_TXFS_LIST_TRANSACTION_LOCKED_FILES:
WARN("STUB: FSCTL_TXFS_LIST_TRANSACTION_LOCKED_FILES\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_TXFS_LIST_TRANSACTIONS:
WARN("STUB: FSCTL_TXFS_LIST_TRANSACTIONS\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_QUERY_PAGEFILE_ENCRYPTION:
WARN("STUB: FSCTL_QUERY_PAGEFILE_ENCRYPTION\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_RESET_VOLUME_ALLOCATION_HINTS:
WARN("STUB: FSCTL_RESET_VOLUME_ALLOCATION_HINTS\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_TXFS_READ_BACKUP_INFORMATION2:
WARN("STUB: FSCTL_TXFS_READ_BACKUP_INFORMATION2\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case FSCTL_CSV_CONTROL:
WARN("STUB: FSCTL_CSV_CONTROL\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
#endif
default:
WARN("unknown control code %x (DeviceType = %x, Access = %x, Function = %x, Method = %x)\n",
IrpSp->Parameters.FileSystemControl.FsControlCode, (IrpSp->Parameters.FileSystemControl.FsControlCode & 0xff0000) >> 16,
(IrpSp->Parameters.FileSystemControl.FsControlCode & 0xc000) >> 14, (IrpSp->Parameters.FileSystemControl.FsControlCode & 0x3ffc) >> 2,
IrpSp->Parameters.FileSystemControl.FsControlCode & 0x3);
Status = STATUS_NOT_IMPLEMENTED;
break;
}
return Status;
}

View file

@ -0,0 +1,231 @@
/*
* Adapted from code at http://support.fccps.cz/download/adv/frr/win32_ddk_mingw/win32_ddk_mingw.html - thanks!
*
File created by Frank Rysanek <rysanek@fccps.cz>
Source code taken almost verbatim from "Driver Development, Part 1"
published by Toby Opferman at CodeProject.com
*/
#include <stdio.h>
#include <windows.h>
/*#include <string.h>*/
#include <unistd.h> /* getcwd() */
#define MY_DRIVER_NAME "btrfs"
#define MY_DEVICE_NAME "\\Btrfs"
#define MY_DOSDEVICE_NAME "\\DosDevices\\" MY_DRIVER_NAME /* AKA symlink name */
/* for the loader and app */
#define MY_SERVICE_NAME_LONG "Driver Test2"
#define MY_SERVICE_NAME_SHORT MY_DRIVER_NAME
#define MY_DRIVER_FILENAME MY_DRIVER_NAME ".sys"
#define MAX_CWD_LEN 1024
static char cwd[MAX_CWD_LEN+3]; /* the XXXXX.sys filename will get appended to this as well */
/* geterrstr() taken verbatim from some code snippet at www.mingw.org by Dan Osborne. */
/* Apparently, it's a way to get a classic null-terminated string containing "last error". */
static char errbuffer[256];
static const char *geterrstr(DWORD errcode)
{
size_t skip = 0;
DWORD chars;
chars = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, errcode, 0, errbuffer, sizeof(errbuffer)-1, 0);
errbuffer[sizeof(errbuffer)-1] = 0;
if (chars)
{
while (errbuffer[chars-1] == '\r' || errbuffer[chars-1] == '\n')
{
errbuffer[--chars] = 0;
}
}
if (chars && errbuffer[chars-1] == '.') errbuffer[--chars] = 0;
if (chars >= 2 && errbuffer[0] == '%'
&& errbuffer[1] >= '0' && errbuffer[1] <= '9')
{
skip = 2;
while (chars > skip && errbuffer[skip] == ' ') ++skip;
if (chars >= skip+2 && errbuffer[skip] == 'i' && errbuffer[skip+1] == 's')
{
skip += 2;
while (chars > skip && errbuffer[skip] == ' ') ++skip;
}
}
if (chars > skip && errbuffer[skip] >= 'A' && errbuffer[skip] <= 'Z')
{
errbuffer[skip] += 'a' - 'A';
}
return errbuffer+skip;
}
void process_error(void)
{
DWORD err = GetLastError();
printf("Error: %lu = \"%s\"\n", (unsigned long)err, geterrstr(err));
}
int main(void)
{
HANDLE hSCManager;
HANDLE hService;
SERVICE_STATUS ss;
int retval = 0;
BOOL amd64;
/* First of all, maybe concatenate the current working directory
with the desired driver file name - before we start messing with
the service manager etc. */
if (getcwd(cwd, MAX_CWD_LEN) == NULL) /* error */
{
printf("Failed to learn the current working directory!\n");
retval = -8;
goto err_out1;
} /* else got CWD just fine */
if (strlen(cwd) + strlen(MY_DRIVER_FILENAME) + 1 > MAX_CWD_LEN)
{
printf("Current working dir + driver filename > longer than %d ?!?\n", MAX_CWD_LEN);
retval = -9;
goto err_out1;
} /* else our buffer is long enough :-) */
strcat(cwd, "\\");
IsWow64Process(GetCurrentProcess(),&amd64);
strcat(cwd, amd64 ? "x64" : "x86");
strcat(cwd, "\\");
strcat(cwd, MY_DRIVER_FILENAME);
printf("Driver path+name: %s\n", cwd);
printf("Going to open the service manager... ");
hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
if (! hSCManager)
{
printf("Uh oh:\n");
process_error();
retval = -1;
goto err_out1;
}
printf("okay.\n");
printf("Going to create the service... ");
hService = CreateService(hSCManager, MY_SERVICE_NAME_SHORT,
MY_SERVICE_NAME_LONG,
SERVICE_START | DELETE | SERVICE_STOP,
SERVICE_KERNEL_DRIVER,
SERVICE_DEMAND_START,
SERVICE_ERROR_IGNORE,
cwd,
NULL, NULL, NULL, NULL, NULL);
if(!hService)
{
process_error();
printf("\n already exists? Trying to open it... ");
hService = OpenService(hSCManager, MY_SERVICE_NAME_SHORT,
SERVICE_START | DELETE | SERVICE_STOP);
}
if(!hService)
{
printf("FAILED!\n");
process_error();
retval = -2;
goto err_out2;
}
printf("okay.\n");
printf("Going to start the service... ");
if (StartService(hService, 0, NULL) == 0) /* error */
{
printf("Uh oh:\n");
process_error();
retval = -3;
goto err_out3;
}
printf("okay.\n");
// TCHAR VolumeName[] = _T("Z:");
// TCHAR DeviceName[] = _T("\\Device\\VDisk1");
// printf("Mounting volume... ");
// if (!DefineDosDeviceA(DDD_RAW_TARGET_PATH, "T:", "\\Device\\HarddiskVolume3"))
// {
// printf("Uh oh:\n");
// process_error();
// } else {
// printf("okay.\n");
// }
// if (!SetVolumeMountPointA("T:\\", "\\\\?\\Volume{9bd714c3-4379-11e5-b26c-806e6f6e6963}\\")) {
// printf("Uh oh:\n");
// process_error();
// } else {
// printf("okay.\n");
// }
printf("\n >>> Press Enter to unload the driver! <<<\n");
getchar();
// printf("Unmounting volume... ");
// if (!DefineDosDeviceA(DDD_REMOVE_DEFINITION, "T:", NULL))
// {
// printf("Uh oh:\n");
// process_error();
// } else {
// printf("okay.\n");
// }
printf("Going to stop the service... ");
if (ControlService(hService, SERVICE_CONTROL_STOP, &ss) == 0) /* error */
{
printf("Uh oh:\n");
process_error();
retval = -4;
}
else printf("okay.\n");
err_out3:
printf("Going to close the service handle... ");
if (CloseServiceHandle(hService) == 0) /* error */
{
printf("Uh oh:\n");
process_error();
retval = -6;
}
else printf("okay.\n");
err_out2:
printf("Going to close the service manager... ");
if (CloseServiceHandle(hSCManager) == 0) /* error */
{
printf("Uh oh:\n");
process_error();
retval = -7;
}
else printf("okay.\n");
err_out1:
printf("Finished! :-b\n");
return(retval);
}

View file

@ -0,0 +1,723 @@
#include "btrfs_drv.h"
enum read_data_status {
ReadDataStatus_Pending,
ReadDataStatus_Success,
ReadDataStatus_Cancelling,
ReadDataStatus_Cancelled,
ReadDataStatus_Error,
ReadDataStatus_CRCError,
ReadDataStatus_MissingDevice
};
struct read_data_context;
typedef struct {
struct read_data_context* context;
UINT8* buf;
PIRP Irp;
IO_STATUS_BLOCK iosb;
enum read_data_status status;
} read_data_stripe;
typedef struct {
KEVENT Event;
NTSTATUS Status;
chunk* c;
UINT32 buflen;
UINT64 num_stripes;
UINT64 type;
read_data_stripe* stripes;
} read_data_context;
static NTSTATUS STDCALL read_data_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
read_data_stripe* stripe = conptr;
read_data_context* context = (read_data_context*)stripe->context;
UINT64 i;
BOOL complete;
if (stripe->status == ReadDataStatus_Cancelling) {
stripe->status = ReadDataStatus_Cancelled;
goto end;
}
stripe->iosb = Irp->IoStatus;
if (NT_SUCCESS(Irp->IoStatus.Status)) {
// FIXME - calculate and compare checksum
stripe->status = ReadDataStatus_Success;
for (i = 0; i < context->num_stripes; i++) {
if (context->stripes[i].status == ReadDataStatus_Pending) {
context->stripes[i].status = ReadDataStatus_Cancelling;
IoCancelIrp(context->stripes[i].Irp);
}
}
goto end;
} else {
stripe->status = ReadDataStatus_Error;
}
end:
complete = TRUE;
for (i = 0; i < context->num_stripes; i++) {
if (context->stripes[i].status == ReadDataStatus_Pending || context->stripes[i].status == ReadDataStatus_Cancelling) {
complete = FALSE;
break;
}
}
if (complete)
KeSetEvent(&context->Event, 0, FALSE);
return STATUS_MORE_PROCESSING_REQUIRED;
}
static NTSTATUS STDCALL read_data(device_extension* Vcb, UINT64 addr, UINT32 length, UINT8* buf) {
CHUNK_ITEM* ci;
CHUNK_ITEM_STRIPE* cis;
read_data_context* context;
UINT64 i/*, type*/, offset;
NTSTATUS Status;
device** devices;
// FIXME - make this work with RAID
if (Vcb->log_to_phys_loaded) {
chunk* c = get_chunk_from_address(Vcb, addr);
if (!c) {
ERR("get_chunk_from_address failed\n");
return STATUS_INTERNAL_ERROR;
}
ci = c->chunk_item;
offset = c->offset;
devices = c->devices;
}
// if (ci->type & BLOCK_FLAG_DUPLICATE) {
// type = BLOCK_FLAG_DUPLICATE;
// } else if (ci->type & BLOCK_FLAG_RAID0) {
// FIXME("RAID0 not yet supported\n");
// return STATUS_NOT_IMPLEMENTED;
// } else if (ci->type & BLOCK_FLAG_RAID1) {
// FIXME("RAID1 not yet supported\n");
// return STATUS_NOT_IMPLEMENTED;
// } else if (ci->type & BLOCK_FLAG_RAID10) {
// FIXME("RAID10 not yet supported\n");
// return STATUS_NOT_IMPLEMENTED;
// } else if (ci->type & BLOCK_FLAG_RAID5) {
// FIXME("RAID5 not yet supported\n");
// return STATUS_NOT_IMPLEMENTED;
// } else if (ci->type & BLOCK_FLAG_RAID6) {
// FIXME("RAID6 not yet supported\n");
// return STATUS_NOT_IMPLEMENTED;
// } else { // SINGLE
// type = 0;
// }
cis = (CHUNK_ITEM_STRIPE*)&ci[1];
context = ExAllocatePoolWithTag(NonPagedPool, sizeof(read_data_context), ALLOC_TAG);
if (!context) {
ERR("out of memory\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlZeroMemory(context, sizeof(read_data_context));
KeInitializeEvent(&context->Event, NotificationEvent, FALSE);
context->stripes = ExAllocatePoolWithTag(NonPagedPool, sizeof(read_data_stripe) * ci->num_stripes, ALLOC_TAG);
if (!context->stripes) {
ERR("out of memory\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlZeroMemory(context->stripes, sizeof(read_data_stripe) * ci->num_stripes);
context->buflen = length;
context->num_stripes = ci->num_stripes;
// context->type = type;
// FIXME - for RAID, check beforehand whether there's enough devices to satisfy request
for (i = 0; i < ci->num_stripes; i++) {
PIO_STACK_LOCATION IrpSp;
if (!devices[i]) {
context->stripes[i].status = ReadDataStatus_MissingDevice;
context->stripes[i].buf = NULL;
} else {
context->stripes[i].context = (struct read_data_context*)context;
context->stripes[i].buf = ExAllocatePoolWithTag(NonPagedPool, length, ALLOC_TAG);
if (!context->stripes[i].buf) {
ERR("out of memory\n");
Status = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
context->stripes[i].Irp = IoAllocateIrp(devices[i]->devobj->StackSize, FALSE);
if (!context->stripes[i].Irp) {
ERR("IoAllocateIrp failed\n");
Status = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
IrpSp = IoGetNextIrpStackLocation(context->stripes[i].Irp);
IrpSp->MajorFunction = IRP_MJ_READ;
if (devices[i]->devobj->Flags & DO_BUFFERED_IO) {
FIXME("FIXME - buffered IO\n");
} else if (devices[i]->devobj->Flags & DO_DIRECT_IO) {
context->stripes[i].Irp->MdlAddress = IoAllocateMdl(context->stripes[i].buf, length, FALSE, FALSE, NULL);
if (!context->stripes[i].Irp->MdlAddress) {
ERR("IoAllocateMdl failed\n");
Status = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
MmProbeAndLockPages(context->stripes[i].Irp->MdlAddress, KernelMode, IoWriteAccess);
} else {
context->stripes[i].Irp->UserBuffer = context->stripes[i].buf;
}
IrpSp->Parameters.Read.Length = length;
IrpSp->Parameters.Read.ByteOffset.QuadPart = addr - offset + cis[i].offset;
context->stripes[i].Irp->UserIosb = &context->stripes[i].iosb;
IoSetCompletionRoutine(context->stripes[i].Irp, read_data_completion, &context->stripes[i], TRUE, TRUE, TRUE);
context->stripes[i].status = ReadDataStatus_Pending;
}
}
for (i = 0; i < ci->num_stripes; i++) {
if (context->stripes[i].status != ReadDataStatus_MissingDevice) {
IoCallDriver(devices[i]->devobj, context->stripes[i].Irp);
}
}
KeWaitForSingleObject(&context->Event, Executive, KernelMode, FALSE, NULL);
// FIXME - if checksum error, write good data over bad
// check if any of the stripes succeeded
for (i = 0; i < ci->num_stripes; i++) {
if (context->stripes[i].status == ReadDataStatus_Success) {
RtlCopyMemory(buf, context->stripes[i].buf, length);
Status = STATUS_SUCCESS;
goto exit;
}
}
// if not, see if we got a checksum error
// for (i = 0; i < ci->num_stripes; i++) {
// if (context->stripes[i].status == ReadDataStatus_CRCError) {
// WARN("stripe %llu had a checksum error\n", i);
//
// Status = STATUS_IMAGE_CHECKSUM_MISMATCH;
// goto exit;
// }
// }
// failing that, return the first error we encountered
for (i = 0; i < ci->num_stripes; i++) {
if (context->stripes[i].status == ReadDataStatus_Error) {
Status = context->stripes[i].iosb.Status;
goto exit;
}
}
// if we somehow get here, return STATUS_INTERNAL_ERROR
Status = STATUS_INTERNAL_ERROR;
exit:
for (i = 0; i < ci->num_stripes; i++) {
if (context->stripes[i].Irp) {
if (devices[i]->devobj->Flags & DO_DIRECT_IO) {
MmUnlockPages(context->stripes[i].Irp->MdlAddress);
IoFreeMdl(context->stripes[i].Irp->MdlAddress);
}
IoFreeIrp(context->stripes[i].Irp);
}
if (context->stripes[i].buf)
ExFreePool(context->stripes[i].buf);
}
ExFreePool(context->stripes);
ExFreePool(context);
return Status;
}
static NTSTATUS STDCALL read_stream(fcb* fcb, UINT8* data, UINT64 start, ULONG length, ULONG* pbr) {
UINT8* xattrdata;
UINT16 xattrlen;
ULONG readlen;
NTSTATUS Status;
TRACE("(%p, %p, %llx, %llx, %p)\n", fcb, data, start, length, pbr);
if (pbr) *pbr = 0;
if (!get_xattr(fcb->Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, &xattrdata, &xattrlen)) {
ERR("get_xattr failed\n");
return STATUS_OBJECT_NAME_NOT_FOUND;
}
if (start >= xattrlen) {
TRACE("tried to read beyond end of stream\n");
Status = STATUS_END_OF_FILE;
goto end;
}
if (length == 0) {
WARN("tried to read zero bytes\n");
Status = STATUS_SUCCESS;
goto end;
}
if (start + length < xattrlen)
readlen = length;
else
readlen = (ULONG)xattrlen - (ULONG)start;
RtlCopyMemory(data + start, xattrdata, readlen);
if (pbr) *pbr = readlen;
Status = STATUS_SUCCESS;
end:
ExFreePool(xattrdata);
return Status;
}
NTSTATUS STDCALL read_file(device_extension* Vcb, root* subvol, UINT64 inode, UINT8* data, UINT64 start, UINT64 length, ULONG* pbr) {
KEY searchkey;
NTSTATUS Status;
traverse_ptr tp, next_tp;
EXTENT_DATA* ed;
UINT64 bytes_read = 0;
TRACE("(%p, %llx, %llx, %p, %llx, %llx, %p)\n", Vcb, subvol->id, inode, data, start, length, pbr);
if (pbr)
*pbr = 0;
searchkey.obj_id = inode;
searchkey.obj_type = TYPE_EXTENT_DATA;
searchkey.offset = start;
Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
goto exit;
}
if (tp.item->key.obj_id < searchkey.obj_id || tp.item->key.obj_type < searchkey.obj_type) {
if (find_next_item(Vcb, &tp, &next_tp, FALSE)) {
free_traverse_ptr(&tp);
tp = next_tp;
TRACE("moving on to %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
}
}
if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
free_traverse_ptr(&tp);
ERR("couldn't find EXTENT_DATA for inode %llx in subvol %llx\n", searchkey.obj_id, subvol->id);
Status = STATUS_INTERNAL_ERROR;
goto exit;
}
if (tp.item->key.offset > start) {
ERR("first EXTENT_DATA was after offset\n");
free_traverse_ptr(&tp);
Status = STATUS_INTERNAL_ERROR;
goto exit;
}
// while (TRUE) {
// BOOL foundnext = find_next_item(Vcb, &tp, &next_tp, NULL, FALSE);
//
// if (!foundnext || next_tp.item->key.obj_id != inode ||
// next_tp.item->key.obj_type != TYPE_EXTENT_DATA || next_tp.item->key.offset > start) {
// if (foundnext)
// free_traverse_ptr(&next_tp);
//
// break;
// }
//
// free_traverse_ptr(&tp);
// tp = next_tp;
// }
do {
ed = (EXTENT_DATA*)tp.item->data;
if (tp.item->size < sizeof(EXTENT_DATA)) {
ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA));
free_traverse_ptr(&tp);
Status = STATUS_INTERNAL_ERROR;
goto exit;
}
if ((ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) && tp.item->size < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) {
ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2));
free_traverse_ptr(&tp);
Status = STATUS_INTERNAL_ERROR;
goto exit;
}
if (tp.item->key.offset + ed->decoded_size < start) {
ERR("Tried to read beyond end of file\n");
free_traverse_ptr(&tp);
Status = STATUS_END_OF_FILE;
goto exit;
}
if (ed->compression != BTRFS_COMPRESSION_NONE) {
FIXME("FIXME - compression not yet supported\n");
free_traverse_ptr(&tp);
Status = STATUS_NOT_IMPLEMENTED;
goto exit;
}
if (ed->encryption != BTRFS_ENCRYPTION_NONE) {
WARN("Encryption not supported\n");
free_traverse_ptr(&tp);
Status = STATUS_NOT_IMPLEMENTED;
goto exit;
}
if (ed->encoding != BTRFS_ENCODING_NONE) {
WARN("Other encodings not supported\n");
free_traverse_ptr(&tp);
Status = STATUS_NOT_IMPLEMENTED;
goto exit;
}
switch (ed->type) {
case EXTENT_TYPE_INLINE:
{
UINT64 off = start + bytes_read - tp.item->key.offset;
UINT64 read;
read = ed->decoded_size - off;
if (read > length) read = length;
RtlCopyMemory(data + bytes_read, &ed->data[off], read);
bytes_read += read;
length -= read;
break;
}
case EXTENT_TYPE_REGULAR:
{
EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data;
UINT64 off = start + bytes_read - tp.item->key.offset;
UINT32 to_read, read;
UINT8* buf;
read = ed->decoded_size - off;
if (read > length) read = length;
if (ed2->address == 0) {
RtlZeroMemory(data + bytes_read, read);
} else {
to_read = sector_align(read, Vcb->superblock.sector_size);
buf = ExAllocatePoolWithTag(PagedPool, to_read, ALLOC_TAG);
if (!buf) {
ERR("out of memory\n");
free_traverse_ptr(&tp);
Status = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
// FIXME - load checksums
Status = read_data(Vcb, ed2->address + ed2->offset + off, to_read, buf);
if (!NT_SUCCESS(Status)) {
ERR("read_data returned %08x\n", Status);
ExFreePool(buf);
free_traverse_ptr(&tp);
goto exit;
}
RtlCopyMemory(data + bytes_read, buf, read);
ExFreePool(buf);
}
bytes_read += read;
length -= read;
break;
}
case EXTENT_TYPE_PREALLOC:
{
UINT64 off = start + bytes_read - tp.item->key.offset;
UINT32 read;
read = ed->decoded_size - off;
if (read > length) read = length;
RtlZeroMemory(data + bytes_read, read);
bytes_read += read;
length -= read;
break;
}
default:
WARN("Unsupported extent data type %u\n", ed->type);
free_traverse_ptr(&tp);
Status = STATUS_NOT_IMPLEMENTED;
goto exit;
}
if (length > 0) {
BOOL foundnext = find_next_item(Vcb, &tp, &next_tp, FALSE);
if (!foundnext)
break;
else if (next_tp.item->key.obj_id != inode ||
next_tp.item->key.obj_type != TYPE_EXTENT_DATA ||
next_tp.item->key.offset != tp.item->key.offset + ed->decoded_size
) {
free_traverse_ptr(&next_tp);
break;
} else {
TRACE("found next key (%llx,%x,%llx)\n", next_tp.item->key.obj_id, next_tp.item->key.obj_type, next_tp.item->key.offset);
free_traverse_ptr(&tp);
tp = next_tp;
}
} else
break;
} while (TRUE);
free_traverse_ptr(&tp);
Status = STATUS_SUCCESS;
if (pbr)
*pbr = bytes_read;
exit:
return Status;
}
NTSTATUS STDCALL drv_read(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
UINT8* data;
PFILE_OBJECT FileObject = IrpSp->FileObject;
fcb* fcb = FileObject->FsContext;
UINT64 start;
ULONG length, bytes_read;
NTSTATUS Status;
BOOL top_level;
FsRtlEnterFileSystem();
top_level = is_top_level(Irp);
TRACE("read\n");
switch (IrpSp->MinorFunction) {
case IRP_MN_COMPLETE:
FIXME("unsupported - IRP_MN_COMPLETE\n");
break;
case IRP_MN_COMPLETE_MDL:
FIXME("unsupported - IRP_MN_COMPLETE_MDL\n");
break;
case IRP_MN_COMPLETE_MDL_DPC:
FIXME("unsupported - IRP_MN_COMPLETE_MDL_DPC\n");
break;
case IRP_MN_COMPRESSED:
FIXME("unsupported - IRP_MN_COMPRESSED\n");
break;
case IRP_MN_DPC:
FIXME("unsupported - IRP_MN_DPC\n");
break;
case IRP_MN_MDL:
FIXME("unsupported - IRP_MN_MDL\n");
break;
case IRP_MN_MDL_DPC:
FIXME("unsupported - IRP_MN_MDL_DPC\n");
break;
case IRP_MN_NORMAL:
TRACE("IRP_MN_NORMAL\n");
break;
default:
WARN("unknown minor %u\n", IrpSp->MinorFunction);
}
data = map_user_buffer(Irp);
if (Irp->MdlAddress && !data) {
ERR("MmGetSystemAddressForMdlSafe returned NULL\n");
Status = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
Irp->IoStatus.Information = 0;
start = IrpSp->Parameters.Read.ByteOffset.QuadPart;
length = IrpSp->Parameters.Read.Length;
bytes_read = 0;
if (!fcb || !fcb->Vcb || !fcb->subvol) {
Status = STATUS_INTERNAL_ERROR; // FIXME - invalid param error?
goto exit;
}
TRACE("file = %.*S (fcb = %p)\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer, fcb);
TRACE("offset = %llx, length = %x\n", start, length);
TRACE("paging_io = %s, no cache = %s\n", Irp->Flags & IRP_PAGING_IO ? "TRUE" : "FALSE", Irp->Flags & IRP_NOCACHE ? "TRUE" : "FALSE");
// FIXME - shouldn't be able to read from a directory
if (!(Irp->Flags & IRP_PAGING_IO) && !FsRtlCheckLockForReadAccess(&fcb->lock, Irp)) {
WARN("tried to read locked region\n");
Status = STATUS_FILE_LOCK_CONFLICT;
goto exit;
}
if (length == 0) {
WARN("tried to read zero bytes\n");
Status = STATUS_SUCCESS;
goto exit;
}
if (start >= fcb->Header.FileSize.QuadPart) {
TRACE("tried to read with offset after file end (%llx >= %llx)\n", start, fcb->Header.FileSize.QuadPart);
Status = STATUS_END_OF_FILE;
goto exit;
}
TRACE("FileObject %p fcb %p FileSize = %llx st_size = %llx (%p)\n", FileObject, fcb, fcb->Header.FileSize.QuadPart, fcb->inode_item.st_size, &fcb->inode_item.st_size);
// int3;
if (length + start > fcb->Header.ValidDataLength.QuadPart) {
RtlZeroMemory(data + (fcb->Header.ValidDataLength.QuadPart - start), length - (fcb->Header.ValidDataLength.QuadPart - start));
length = fcb->Header.ValidDataLength.QuadPart - start;
}
if (!(Irp->Flags & IRP_NOCACHE)) {
BOOL wait;
Status = STATUS_SUCCESS;
// try {
if (!FileObject->PrivateCacheMap) {
CC_FILE_SIZES ccfs;
ccfs.AllocationSize = fcb->Header.AllocationSize;
ccfs.FileSize = fcb->Header.FileSize;
ccfs.ValidDataLength = fcb->Header.ValidDataLength;
TRACE("calling CcInitializeCacheMap (%llx, %llx, %llx)\n",
ccfs.AllocationSize.QuadPart, ccfs.FileSize.QuadPart, ccfs.ValidDataLength.QuadPart);
CcInitializeCacheMap(FileObject, &ccfs, FALSE, cache_callbacks, FileObject);
CcSetReadAheadGranularity(FileObject, READ_AHEAD_GRANULARITY);
}
// FIXME - uncomment this when async is working
// wait = IoIsOperationSynchronous(Irp) ? TRUE : FALSE;
wait = TRUE;
TRACE("CcCopyRead(%p, %llx, %x, %u, %p, %p)\n", FileObject, IrpSp->Parameters.Read.ByteOffset.QuadPart, length, wait, data, &Irp->IoStatus);
TRACE("sizes = %llx, %llx, %llx\n", fcb->Header.AllocationSize, fcb->Header.FileSize, fcb->Header.ValidDataLength);
if (!CcCopyRead(FileObject, &IrpSp->Parameters.Read.ByteOffset, length, wait, data, &Irp->IoStatus)) {
TRACE("CcCopyRead failed\n");
IoMarkIrpPending(Irp);
Status = STATUS_PENDING;
goto exit;
}
TRACE("CcCopyRead finished\n");
// } except (EXCEPTION_EXECUTE_HANDLER) {
// Status = GetExceptionCode();
// }
if (NT_SUCCESS(Status)) {
Status = Irp->IoStatus.Status;
bytes_read = Irp->IoStatus.Information;
} else
ERR("EXCEPTION - %08x\n", Status);
} else {
if (!(Irp->Flags & IRP_PAGING_IO) && FileObject->SectionObjectPointer->DataSectionObject) {
IO_STATUS_BLOCK iosb;
CcFlushCache(FileObject->SectionObjectPointer, &IrpSp->Parameters.Read.ByteOffset, length, &iosb);
if (!NT_SUCCESS(iosb.Status)) {
ERR("CcFlushCache returned %08x\n", iosb.Status);
Status = iosb.Status;
goto exit;
}
}
acquire_tree_lock(fcb->Vcb, FALSE);
if (fcb->ads)
Status = read_stream(fcb, data, start, length, &bytes_read);
else
Status = read_file(fcb->Vcb, fcb->subvol, fcb->inode, data, start, length, &bytes_read);
release_tree_lock(fcb->Vcb, FALSE);
TRACE("read %u bytes\n", bytes_read);
Irp->IoStatus.Information = bytes_read;
}
exit:
Irp->IoStatus.Status = Status;
if (FileObject->Flags & FO_SYNCHRONOUS_IO && !(Irp->Flags & IRP_PAGING_IO))
FileObject->CurrentByteOffset.QuadPart = start + (NT_SUCCESS(Status) ? bytes_read : 0);
TRACE("Irp->IoStatus.Status = %08x\n", Irp->IoStatus.Status);
TRACE("Irp->IoStatus.Information = %lu\n", Irp->IoStatus.Information);
TRACE("returning %08x\n", Status);
if (Status != STATUS_PENDING)
IoCompleteRequest(Irp, IO_NO_INCREMENT);
if (top_level)
IoSetTopLevelIrp(NULL);
FsRtlExitFileSystem();
return Status;
}

View file

@ -0,0 +1,605 @@
/* Copyright (c) Mark Harmstone 2016
*
* This file is part of WinBtrfs.
*
* WinBtrfs is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public Licence as published by
* the Free Software Foundation, either version 3 of the Licence, or
* (at your option) any later version.
*
* WinBtrfs is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public Licence for more details.
*
* You should have received a copy of the GNU Lesser General Public Licence
* along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */
#include "btrfs_drv.h"
BOOL follow_symlink(fcb* fcb, PFILE_OBJECT FileObject) {
NTSTATUS Status;
ULONG len, stringlen;
USHORT newlen;
OBJECT_NAME_INFORMATION* oni;
UINT8* data;
UINT32 i;
BOOL success = FALSE;
WCHAR* utf16;
UNICODE_STRING abspath;
if (fcb->inode_item.st_size == 0) {
WARN("can't follow symlink with no data\n");
return FALSE;
}
Status = ObQueryNameString(FileObject->DeviceObject, NULL, 0, &len);
if (Status != STATUS_INFO_LENGTH_MISMATCH) {
ERR("ObQueryNameString 1 returned %08x\n", Status);
return FALSE;
}
oni = ExAllocatePoolWithTag(PagedPool, len, ALLOC_TAG);
if (!oni) {
ERR("out of memory\n");
goto end;
}
Status = ObQueryNameString(FileObject->DeviceObject, oni, len, &len);
if (!NT_SUCCESS(Status)) {
ERR("ObQueryNameString 2 returned %08x\n", Status);
ExFreePool(oni);
return FALSE;
}
data = ExAllocatePoolWithTag(PagedPool, fcb->inode_item.st_size, ALLOC_TAG);
if (!data) {
ERR("out of memory\n");
goto end;
}
Status = read_file(fcb->Vcb, fcb->subvol, fcb->inode, data, 0, fcb->inode_item.st_size, NULL);
if (!NT_SUCCESS(Status)) {
ERR("read_file returned %08x\n", Status);
goto end;
}
for (i = 0; i < fcb->inode_item.st_size; i++) {
if (data[i] == '/')
data[i] = '\\';
}
Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, (char*)data, fcb->inode_item.st_size);
if (!NT_SUCCESS(Status)) {
ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
goto end;
}
utf16 = ExAllocatePoolWithTag(PagedPool, stringlen, ALLOC_TAG);
if (!utf16) {
ERR("out of memory\n");
goto end;
}
Status = RtlUTF8ToUnicodeN(utf16, stringlen, &stringlen, (char*)data, fcb->inode_item.st_size);
if (!NT_SUCCESS(Status)) {
ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
ExFreePool(utf16);
goto end;
}
if (utf16[0] == '\\') { // absolute path
abspath.MaximumLength = abspath.Length = stringlen;
abspath.Buffer = utf16;
} else { // relative path
abspath.MaximumLength = fcb->par->full_filename.Length + sizeof(WCHAR) + stringlen;
abspath.Buffer = ExAllocatePoolWithTag(PagedPool, abspath.MaximumLength, ALLOC_TAG);
if (!abspath.Buffer) {
ERR("out of memory\n");
ExFreePool(utf16);
goto end;
}
RtlCopyMemory(abspath.Buffer, fcb->par->full_filename.Buffer, fcb->par->full_filename.Length);
abspath.Length = fcb->par->full_filename.Length;
if (fcb->par->par) {
abspath.Buffer[abspath.Length / sizeof(WCHAR)] = '\\';
abspath.Length += sizeof(WCHAR);
}
RtlCopyMemory(&abspath.Buffer[abspath.Length / sizeof(WCHAR)], utf16, stringlen);
abspath.Length += stringlen;
ExFreePool(utf16);
}
Status = FsRtlRemoveDotsFromPath(abspath.Buffer, abspath.Length, &newlen);
if (!NT_SUCCESS(Status)) {
ERR("FsRtlRemoveDotsFromPath returned %08x\n", Status);
ExFreePool(abspath.Buffer);
goto end;
}
abspath.Length = newlen;
TRACE("abspath = %.*S\n", abspath.Length / sizeof(WCHAR), abspath.Buffer);
TRACE("filename was %.*S\n", FileObject->FileName.Length / sizeof(WCHAR), FileObject->FileName.Buffer);
if (FileObject->FileName.MaximumLength < oni->Name.Length + abspath.Length) {
ExFreePool(FileObject->FileName.Buffer);
FileObject->FileName.MaximumLength = oni->Name.Length + abspath.Length;
FileObject->FileName.Buffer = ExAllocatePoolWithTag(PagedPool, FileObject->FileName.MaximumLength, ALLOC_TAG);
if (!FileObject->FileName.Buffer) {
ERR("out of memory\n");
ExFreePool(abspath.Buffer);
goto end;
}
}
RtlCopyMemory(FileObject->FileName.Buffer, oni->Name.Buffer, oni->Name.Length);
RtlCopyMemory(&FileObject->FileName.Buffer[oni->Name.Length / sizeof(WCHAR)], abspath.Buffer, abspath.Length);
FileObject->FileName.Length = oni->Name.Length + abspath.Length;
TRACE("filename now %.*S\n", FileObject->FileName.Length / sizeof(WCHAR), FileObject->FileName.Buffer);
ExFreePool(abspath.Buffer);
success = TRUE;
end:
ExFreePool(data);
ExFreePool(oni);
return success;
}
NTSTATUS get_reparse_point(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, void* buffer, DWORD buflen, DWORD* retlen) {
USHORT subnamelen, printnamelen, i;
ULONG stringlen;
DWORD reqlen;
REPARSE_DATA_BUFFER* rdb = buffer;
fcb* fcb = FileObject->FsContext;
char* data;
NTSTATUS Status;
TRACE("(%p, %p, %p, %x, %p)\n", DeviceObject, FileObject, buffer, buflen, retlen);
acquire_tree_lock(fcb->Vcb, FALSE);
if (fcb->type != BTRFS_TYPE_SYMLINK) {
Status = STATUS_NOT_A_REPARSE_POINT;
goto end;
}
data = ExAllocatePoolWithTag(PagedPool, fcb->inode_item.st_size, ALLOC_TAG);
if (!data) {
ERR("out of memory\n");
Status = STATUS_INSUFFICIENT_RESOURCES;
goto end;
}
TRACE("data = %p, size = %x\n", data, fcb->inode_item.st_size);
Status = read_file(DeviceObject->DeviceExtension, fcb->subvol, fcb->inode, (UINT8*)data, 0, fcb->inode_item.st_size, NULL);
if (!NT_SUCCESS(Status)) {
ERR("read_file returned %08x\n", Status);
ExFreePool(data);
goto end;
}
Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, data, fcb->inode_item.st_size);
if (!NT_SUCCESS(Status)) {
ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
ExFreePool(data);
goto end;
}
subnamelen = stringlen;
printnamelen = stringlen;
reqlen = offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) + subnamelen + printnamelen;
if (buflen < reqlen) {
Status = STATUS_BUFFER_OVERFLOW;
goto end;
}
rdb->ReparseTag = IO_REPARSE_TAG_SYMLINK;
rdb->ReparseDataLength = reqlen - offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer);
rdb->Reserved = 0;
rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset = 0;
rdb->SymbolicLinkReparseBuffer.SubstituteNameLength = subnamelen;
rdb->SymbolicLinkReparseBuffer.PrintNameOffset = subnamelen;
rdb->SymbolicLinkReparseBuffer.PrintNameLength = printnamelen;
rdb->SymbolicLinkReparseBuffer.Flags = SYMLINK_FLAG_RELATIVE;
Status = RtlUTF8ToUnicodeN(&rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)],
stringlen, &stringlen, data, fcb->inode_item.st_size);
if (!NT_SUCCESS(Status)) {
ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
ExFreePool(data);
goto end;
}
for (i = 0; i < stringlen / sizeof(WCHAR); i++) {
if (rdb->SymbolicLinkReparseBuffer.PathBuffer[(rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)) + i] == '/')
rdb->SymbolicLinkReparseBuffer.PathBuffer[(rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)) + i] = '\\';
}
RtlCopyMemory(&rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(WCHAR)],
&rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)],
rdb->SymbolicLinkReparseBuffer.SubstituteNameLength);
*retlen = reqlen;
ExFreePool(data);
Status = STATUS_SUCCESS;
end:
release_tree_lock(fcb->Vcb, FALSE);
return Status;
}
static NTSTATUS change_file_type(device_extension* Vcb, UINT64 inode, root* subvol, UINT64 parinode, UINT64 index, PANSI_STRING utf8, UINT8 type, LIST_ENTRY* rollback) {
KEY searchkey;
UINT32 crc32;
traverse_ptr tp;
NTSTATUS Status;
crc32 = calc_crc32c(0xfffffffe, (UINT8*)utf8->Buffer, (ULONG)utf8->Length);
searchkey.obj_id = parinode;
searchkey.obj_type = TYPE_DIR_ITEM;
searchkey.offset = crc32;
Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
}
if (!keycmp(&tp.item->key, &searchkey)) {
if (tp.item->size < sizeof(DIR_ITEM)) {
ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM));
} else {
DIR_ITEM *di = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG), *di2;
BOOL found = FALSE;
ULONG len = tp.item->size;
if (!di) {
ERR("out of memory\n");
free_traverse_ptr(&tp);
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlCopyMemory(di, tp.item->data, tp.item->size);
di2 = di;
do {
if (len < sizeof(DIR_ITEM) || len < sizeof(DIR_ITEM) - 1 + di2->m + di2->n) {
ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
break;
}
if (di2->n == utf8->Length && RtlCompareMemory(di2->name, utf8->Buffer, utf8->Length) == utf8->Length) {
di2->type = type;
found = TRUE;
break;
}
if (len > sizeof(DIR_ITEM) - sizeof(char) + di2->m + di2->n) {
len -= sizeof(DIR_ITEM) - sizeof(char) + di2->m + di2->n;
di2 = (DIR_ITEM*)&di2->name[di2->m + di2->n];
} else
break;
} while (len > 0);
if (found) {
delete_tree_item(Vcb, &tp, rollback);
insert_tree_item(Vcb, subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, di, tp.item->size, NULL, rollback);
} else
ExFreePool(di);
}
} else {
WARN("search for DIR_ITEM by crc32 failed\n");
}
free_traverse_ptr(&tp);
searchkey.obj_id = parinode;
searchkey.obj_type = TYPE_DIR_INDEX;
searchkey.offset = index;
Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
}
if (!keycmp(&tp.item->key, &searchkey)) {
if (tp.item->size < sizeof(DIR_ITEM)) {
ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM));
} else {
DIR_ITEM* di = (DIR_ITEM*)tp.item->data;
DIR_ITEM* di2 = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
if (!di2) {
ERR("out of memory\n");
free_traverse_ptr(&tp);
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlCopyMemory(di2, di, tp.item->size);
di2->type = type;
delete_tree_item(Vcb, &tp, rollback);
insert_tree_item(Vcb, subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, di2, tp.item->size, NULL, rollback);
}
}
free_traverse_ptr(&tp);
return STATUS_SUCCESS;
}
NTSTATUS set_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
PFILE_OBJECT FileObject = IrpSp->FileObject;
void* buffer = Irp->AssociatedIrp.SystemBuffer;
DWORD buflen = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
NTSTATUS Status = STATUS_SUCCESS;
fcb* fcb;
ULONG tag, minlen;
UNICODE_STRING subname;
ANSI_STRING target;
REPARSE_DATA_BUFFER* rdb = buffer;
KEY searchkey;
traverse_ptr tp, next_tp;
BOOL b;
LARGE_INTEGER offset;
USHORT i;
LIST_ENTRY rollback;
// FIXME - send notification if this succeeds? The attributes will have changed.
TRACE("(%p, %p)\n", DeviceObject, Irp);
InitializeListHead(&rollback);
if (!FileObject) {
ERR("FileObject was NULL\n");
return STATUS_INVALID_PARAMETER;
}
fcb = FileObject->FsContext;
ERR("filename: %.*S\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
acquire_tree_lock(fcb->Vcb, TRUE);
if (fcb->type == BTRFS_TYPE_SYMLINK) {
WARN("tried to set a reparse point on an existing symlink\n");
Status = STATUS_INVALID_PARAMETER;
goto end;
}
if (!fcb->utf8.Buffer) {
ERR("error - utf8 on FCB not set\n");
Status = STATUS_INTERNAL_ERROR;
goto end;
}
// FIXME - die if not file
// FIXME - die if ADS
if (buflen < sizeof(ULONG)) {
WARN("buffer was not long enough to hold tag\n");
Status = STATUS_INVALID_PARAMETER;
goto end;
}
RtlCopyMemory(&tag, buffer, sizeof(ULONG));
if (tag != IO_REPARSE_TAG_SYMLINK) {
FIXME("FIXME: handle arbitrary reparse tags (%08x)\n", tag);
Status = STATUS_NOT_IMPLEMENTED;
goto end;
}
minlen = offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) + sizeof(WCHAR);
if (buflen < minlen) {
WARN("buffer was less than minimum length (%u < %u)\n", buflen, minlen);
Status = STATUS_INVALID_PARAMETER;
goto end;
}
if (!(rdb->SymbolicLinkReparseBuffer.Flags & SYMLINK_FLAG_RELATIVE)) {
FIXME("FIXME: support non-relative symlinks\n");
Status = STATUS_NOT_IMPLEMENTED;
}
subname.Buffer = &rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)];
subname.MaximumLength = subname.Length = rdb->SymbolicLinkReparseBuffer.SubstituteNameLength;
ERR("substitute name = %.*S\n", subname.Length / sizeof(WCHAR), subname.Buffer);
fcb->type = BTRFS_TYPE_SYMLINK;
searchkey.obj_id = fcb->inode;
searchkey.obj_type = TYPE_INODE_REF;
searchkey.offset = 0;
Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
goto end;
}
do {
if (tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_INODE_REF) {
if (tp.item->size < sizeof(INODE_REF)) {
WARN("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(INODE_REF));
} else {
INODE_REF* ir;
ULONG size = tp.item->size;
ANSI_STRING utf8;
ir = (INODE_REF*)tp.item->data;
do {
if (size < sizeof(INODE_REF) || size < sizeof(INODE_REF) - 1 + ir->n) {
WARN("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
break;
}
utf8.Buffer = ir->name;
utf8.Length = utf8.MaximumLength = ir->n;
Status = change_file_type(fcb->Vcb, fcb->inode, fcb->subvol, tp.item->key.offset, ir->index, &utf8, BTRFS_TYPE_SYMLINK, &rollback);
if (!NT_SUCCESS(Status)) {
ERR("error - change_file_type returned %08x\n", Status);
goto end;
}
if (size > sizeof(INODE_REF) - 1 + ir->n) {
size -= sizeof(INODE_REF) - 1 + ir->n;
ir = (INODE_REF*)&ir->name[ir->n];
} else
break;
} while (TRUE);
}
}
b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE);
if (b) {
free_traverse_ptr(&tp);
tp = next_tp;
b = tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_INODE_REF;
}
} while (b);
free_traverse_ptr(&tp);
if (fcb->Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF) {
searchkey.obj_id = fcb->inode;
searchkey.obj_type = TYPE_INODE_EXTREF;
searchkey.offset = 0;
Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
goto end;
}
do {
if (tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_INODE_EXTREF) {
if (tp.item->size < sizeof(INODE_EXTREF)) {
WARN("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(INODE_EXTREF));
} else {
INODE_EXTREF* ier;
ULONG size = tp.item->size;
ANSI_STRING utf8;
ier = (INODE_EXTREF*)tp.item->data;
do {
if (size < sizeof(INODE_EXTREF) || size < sizeof(INODE_EXTREF) - 1 + ier->n) {
WARN("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
break;
}
utf8.Buffer = ier->name;
utf8.Length = utf8.MaximumLength = ier->n;
Status = change_file_type(fcb->Vcb, fcb->inode, fcb->subvol, ier->dir, ier->index, &utf8, BTRFS_TYPE_SYMLINK, &rollback);
if (!NT_SUCCESS(Status)) {
ERR("error - change_file_type returned %08x\n", Status);
goto end;
}
if (size > sizeof(INODE_EXTREF) - 1 + ier->n) {
size -= sizeof(INODE_EXTREF) - 1 + ier->n;
ier = (INODE_EXTREF*)&ier->name[ier->n];
} else
break;
} while (TRUE);
}
}
b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE);
if (b) {
free_traverse_ptr(&tp);
tp = next_tp;
b = tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_INODE_EXTREF;
}
} while (b);
free_traverse_ptr(&tp);
}
fcb->inode_item.st_mode |= __S_IFLNK;
Status = truncate_file(fcb, 0, &rollback);
if (!NT_SUCCESS(Status)) {
ERR("truncate_file returned %08x\n", Status);
goto end;
}
Status = RtlUnicodeToUTF8N(NULL, 0, (PULONG)&target.Length, subname.Buffer, subname.Length);
if (!NT_SUCCESS(Status)) {
ERR("RtlUnicodeToUTF8N 3 failed with error %08x\n", Status);
goto end;
}
target.MaximumLength = target.Length;
target.Buffer = ExAllocatePoolWithTag(PagedPool, target.MaximumLength, ALLOC_TAG);
if (!target.Buffer) {
ERR("out of memory\n");
Status = STATUS_INSUFFICIENT_RESOURCES;
goto end;
}
Status = RtlUnicodeToUTF8N(target.Buffer, target.Length, (PULONG)&target.Length, subname.Buffer, subname.Length);
if (!NT_SUCCESS(Status)) {
ERR("RtlUnicodeToUTF8N 4 failed with error %08x\n", Status);
goto end;
}
for (i = 0; i < target.Length; i++) {
if (target.Buffer[i] == '\\')
target.Buffer[i] = '/';
}
offset.QuadPart = 0;
Status = write_file2(fcb->Vcb, Irp, offset, target.Buffer, (ULONG*)&target.Length, Irp->Flags & IRP_PAGING_IO, Irp->Flags & IRP_NOCACHE, &rollback);
ExFreePool(target.Buffer);
if (NT_SUCCESS(Status))
Status = consider_write(fcb->Vcb);
end:
if (NT_SUCCESS(Status))
clear_rollback(&rollback);
else
do_rollback(fcb->Vcb, &rollback);
release_tree_lock(fcb->Vcb, TRUE);
return Status;
}

View file

@ -0,0 +1,14 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by btrfs.rc
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 101
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

View file

@ -0,0 +1,332 @@
/* Copyright (c) Mark Harmstone 2016
*
* This file is part of WinBtrfs.
*
* WinBtrfs is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public Licence as published by
* the Free Software Foundation, either version 3 of the Licence, or
* (at your option) any later version.
*
* WinBtrfs is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public Licence for more details.
*
* You should have received a copy of the GNU Lesser General Public Licence
* along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */
#include "btrfs_drv.h"
#ifndef __REACTOS__
#include <ntddk.h>
#include <ntifs.h>
#include <mountmgr.h>
#include <windef.h>
#endif
#include <initguid.h>
#ifndef __REACTOS__
#include <winioctl.h>
#endif
#include <wdmguid.h>
#ifndef __REACTOS__
typedef struct _OBJECT_DIRECTORY_INFORMATION {
UNICODE_STRING Name;
UNICODE_STRING TypeName;
} OBJECT_DIRECTORY_INFORMATION, *POBJECT_DIRECTORY_INFORMATION;
#endif
#if !defined (_GNU_NTIFS_) || defined(__REACTOS__)
NTSTATUS WINAPI ZwQueryDirectoryObject(HANDLE DirectoryHandle, PVOID Buffer, ULONG Length,
BOOLEAN ReturnSingleEntry, BOOLEAN RestartScan, PULONG Context,
PULONG ReturnLength);
#endif
VOID WINAPI IopNotifyPlugPlayNotification(
IN PDEVICE_OBJECT DeviceObject,
IN IO_NOTIFICATION_EVENT_CATEGORY EventCategory,
IN LPCGUID Event,
IN PVOID EventCategoryData1,
IN PVOID EventCategoryData2
);
static const WCHAR devpath[] = {'\\','D','e','v','i','c','e',0};
static void STDCALL add_volume(PDEVICE_OBJECT mountmgr, PUNICODE_STRING us) {
ULONG tnsize;
MOUNTMGR_TARGET_NAME* tn;
KEVENT Event;
IO_STATUS_BLOCK IoStatusBlock;
PIRP Irp;
NTSTATUS Status;
ULONG mmdltsize;
MOUNTMGR_DRIVE_LETTER_TARGET* mmdlt;
MOUNTMGR_DRIVE_LETTER_INFORMATION mmdli;
TRACE("found BTRFS volume\n");
tnsize = sizeof(MOUNTMGR_TARGET_NAME) - sizeof(WCHAR) + us->Length;
tn = ExAllocatePoolWithTag(NonPagedPool, tnsize, ALLOC_TAG);
if (!tn) {
ERR("out of memory\n");
return;
}
tn->DeviceNameLength = us->Length;
RtlCopyMemory(tn->DeviceName, us->Buffer, tn->DeviceNameLength);
KeInitializeEvent(&Event, NotificationEvent, FALSE);
Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION,
mountmgr, tn, tnsize,
NULL, 0, FALSE, &Event, &IoStatusBlock);
if (!Irp) {
ERR("IoBuildDeviceIoControlRequest failed\n");
return;
}
Status = IoCallDriver(mountmgr, Irp);
if (Status == STATUS_PENDING) {
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
Status = IoStatusBlock.Status;
}
if (!NT_SUCCESS(Status))
ERR("IoCallDriver (1) returned %08x\n", Status);
ExFreePool(tn);
mmdltsize = sizeof(MOUNTMGR_DRIVE_LETTER_TARGET) - 1 + us->Length;
mmdlt = ExAllocatePoolWithTag(NonPagedPool, mmdltsize, ALLOC_TAG);
if (!mmdlt) {
ERR("out of memory\n");
return;
}
mmdlt->DeviceNameLength = us->Length;
RtlCopyMemory(&mmdlt->DeviceName, us->Buffer, us->Length);
TRACE("mmdlt = %.*S\n", mmdlt->DeviceNameLength / sizeof(WCHAR), mmdlt->DeviceName);
KeInitializeEvent(&Event, NotificationEvent, FALSE);
Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTMGR_NEXT_DRIVE_LETTER,
mountmgr, mmdlt, mmdltsize,
&mmdli, sizeof(MOUNTMGR_DRIVE_LETTER_INFORMATION), FALSE, &Event, &IoStatusBlock);
if (!Irp) {
ERR("IoBuildDeviceIoControlRequest failed\n");
return;
}
Status = IoCallDriver(mountmgr, Irp);
if (Status == STATUS_PENDING) {
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
Status = IoStatusBlock.Status;
}
if (!NT_SUCCESS(Status))
ERR("IoCallDriver (2) returned %08x\n", Status);
else
TRACE("DriveLetterWasAssigned = %u, CurrentDriveLetter = %c\n", mmdli.DriveLetterWasAssigned, mmdli.CurrentDriveLetter);
ExFreePool(mmdlt);
}
static void STDCALL test_vol(PDEVICE_OBJECT mountmgr, PUNICODE_STRING us, LIST_ENTRY* volumes) {
KEVENT Event;
PIRP Irp;
IO_STATUS_BLOCK IoStatusBlock;
NTSTATUS Status;
PFILE_OBJECT FileObject;
PDEVICE_OBJECT DeviceObject;
LARGE_INTEGER Offset;
ULONG toread;
UINT8* data;
UNICODE_STRING us2;
BOOL added_entry = FALSE;
TRACE("%.*S\n", us->Length / sizeof(WCHAR), us->Buffer);
us2.Length = ((wcslen(devpath) + 1) * sizeof(WCHAR)) + us->Length;
us2.MaximumLength = us2.Length;
us2.Buffer = ExAllocatePoolWithTag(PagedPool, us2.Length, ALLOC_TAG);
if (!us2.Buffer) {
ERR("out of memory\n");
return;
}
RtlCopyMemory(us2.Buffer, devpath, wcslen(devpath) * sizeof(WCHAR));
us2.Buffer[wcslen(devpath)] = '\\';
RtlCopyMemory(&us2.Buffer[wcslen(devpath)+1], us->Buffer, us->Length);
TRACE("%.*S\n", us2.Length / sizeof(WCHAR), us2.Buffer);
Status = IoGetDeviceObjectPointer(&us2, FILE_READ_ATTRIBUTES, &FileObject, &DeviceObject);
if (!NT_SUCCESS(Status)) {
ERR("IoGetDeviceObjectPointer returned %08x\n", Status);
goto exit;
}
KeInitializeEvent(&Event, NotificationEvent, FALSE);
Offset.QuadPart = superblock_addrs[0];
toread = sector_align(sizeof(superblock), DeviceObject->SectorSize);
data = ExAllocatePoolWithTag(NonPagedPool, toread, ALLOC_TAG);
if (!data) {
ERR("out of memory\n");
goto deref;
}
Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ, DeviceObject, data, toread, &Offset, &Event, &IoStatusBlock);
if (!Irp) {
ERR("IoBuildSynchronousFsdRequest failed\n");
goto deref;
}
Status = IoCallDriver(DeviceObject, Irp);
if (Status == STATUS_PENDING) {
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
Status = IoStatusBlock.Status;
}
if (NT_SUCCESS(Status) && IoStatusBlock.Information > 0 && ((superblock*)data)->magic == BTRFS_MAGIC) {
superblock* sb = (superblock*)data;
volume* v = ExAllocatePoolWithTag(PagedPool, sizeof(volume), ALLOC_TAG);
if (!v) {
ERR("out of memory\n");
goto deref;
}
v->devobj = DeviceObject;
RtlCopyMemory(&v->fsuuid, &sb->uuid, sizeof(BTRFS_UUID));
RtlCopyMemory(&v->devuuid, &sb->dev_item.device_uuid, sizeof(BTRFS_UUID));
v->devnum = sb->dev_item.dev_id;
v->devpath = us2;
v->processed = FALSE;
InsertTailList(volumes, &v->list_entry);
TRACE("volume found\n");
TRACE("FS uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
v->fsuuid.uuid[0], v->fsuuid.uuid[1], v->fsuuid.uuid[2], v->fsuuid.uuid[3], v->fsuuid.uuid[4], v->fsuuid.uuid[5], v->fsuuid.uuid[6], v->fsuuid.uuid[7],
v->fsuuid.uuid[8], v->fsuuid.uuid[9], v->fsuuid.uuid[10], v->fsuuid.uuid[11], v->fsuuid.uuid[12], v->fsuuid.uuid[13], v->fsuuid.uuid[14], v->fsuuid.uuid[15]);
TRACE("device uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
v->devuuid.uuid[0], v->devuuid.uuid[1], v->devuuid.uuid[2], v->devuuid.uuid[3], v->devuuid.uuid[4], v->devuuid.uuid[5], v->devuuid.uuid[6], v->devuuid.uuid[7],
v->devuuid.uuid[8], v->devuuid.uuid[9], v->devuuid.uuid[10], v->devuuid.uuid[11], v->devuuid.uuid[12], v->devuuid.uuid[13], v->devuuid.uuid[14], v->devuuid.uuid[15]);
TRACE("device number %llx\n", v->devnum);
added_entry = TRUE;
}
deref:
ExFreePool(data);
ObDereferenceObject(FileObject);
exit:
if (!added_entry)
ExFreePool(us2.Buffer);
}
void STDCALL look_for_vols(LIST_ENTRY* volumes) {
PFILE_OBJECT FileObject;
PDEVICE_OBJECT mountmgr;
OBJECT_ATTRIBUTES attr;
UNICODE_STRING mmdevpath, us;
HANDLE h;
OBJECT_DIRECTORY_INFORMATION* odi;
ULONG odisize;
ULONG context;
BOOL restart;
NTSTATUS Status;
LIST_ENTRY* le;
static const WCHAR hdv[] = {'H','a','r','d','d','i','s','k','V','o','l','u','m','e',0};
static const WCHAR device[] = {'D','e','v','i','c','e',0};
RtlInitUnicodeString(&mmdevpath, MOUNTMGR_DEVICE_NAME);
Status = IoGetDeviceObjectPointer(&mmdevpath, FILE_READ_ATTRIBUTES, &FileObject, &mountmgr);
if (!NT_SUCCESS(Status)) {
ERR("IoGetDeviceObjectPointer returned %08x\n", Status);
return;
}
RtlInitUnicodeString(&us, devpath);
attr.Length = sizeof(attr);
attr.RootDirectory = 0;
attr.Attributes = OBJ_CASE_INSENSITIVE;
attr.ObjectName = &us;
attr.SecurityDescriptor = NULL;
attr.SecurityQualityOfService = NULL;
Status = ZwOpenDirectoryObject(&h, DIRECTORY_TRAVERSE, &attr);
if (!NT_SUCCESS(Status)) {
ERR("ZwOpenDirectoryObject returned %08x\n", Status);
return;
}
odisize = sizeof(OBJECT_DIRECTORY_INFORMATION) * 16;
odi = ExAllocatePoolWithTag(PagedPool, odisize, ALLOC_TAG);
if (!odi) {
ERR("out of memory\n");
return;
}
restart = TRUE;
do {
Status = ZwQueryDirectoryObject(h, odi, odisize, FALSE, restart, &context, NULL/*&retlen*/);
restart = FALSE;
if (!NT_SUCCESS(Status)) {
if (Status != STATUS_NO_MORE_ENTRIES)
ERR("ZwQueryDirectoryObject returned %08x\n", Status);
} else {
OBJECT_DIRECTORY_INFORMATION* odi2 = odi;
while (odi2->Name.Buffer) {
if (odi2->TypeName.Length == wcslen(device) * sizeof(WCHAR) &&
RtlCompareMemory(odi2->TypeName.Buffer, device, wcslen(device) * sizeof(WCHAR)) == wcslen(device) * sizeof(WCHAR) &&
odi2->Name.Length > wcslen(hdv) * sizeof(WCHAR) &&
RtlCompareMemory(odi2->Name.Buffer, hdv, wcslen(hdv) * sizeof(WCHAR)) == wcslen(hdv) * sizeof(WCHAR)) {
test_vol(mountmgr, &odi2->Name, volumes);
}
odi2 = &odi2[1];
}
}
} while (NT_SUCCESS(Status));
ZwClose(h);
// FIXME - if Windows has already added the second device of a filesystem itself, delete it
le = volumes->Flink;
while (le != volumes) {
volume* v = CONTAINING_RECORD(le, volume, list_entry);
if (!v->processed) {
LIST_ENTRY* le2 = le;
volume* mountvol = v;
while (le2 != volumes) {
volume* v2 = CONTAINING_RECORD(le2, volume, list_entry);
if (RtlCompareMemory(&v2->fsuuid, &v->fsuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
v2->processed = TRUE;
if (v2->devnum < mountvol->devnum)
mountvol = v2;
}
le2 = le2->Flink;
}
add_volume(mountmgr, &mountvol->devpath);
}
le = le->Flink;
}
ObDereferenceObject(FileObject);
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff