Upgrade the WinBtrfs to release 0.4.

CORE-11172 #resolve #Committed in r71265

svn path=/trunk/; revision=71265
This commit is contained in:
Pierre Schweitzer 2016-05-05 17:26:47 +00:00
parent 3c7f167e24
commit ae7080e660
20 changed files with 9448 additions and 3985 deletions

View file

@ -8,10 +8,13 @@ list(APPEND SOURCE
crc32c.c
create.c
dirctrl.c
extent-tree.c
fastio.c
fileinfo.c
flushthread.c
free-space.c
fsctl.c
pnp.c
read.c
reparse.c
search.c
@ -30,7 +33,7 @@ endif()
add_definitions(-D__KERNEL__)
set_module_type(btrfs kernelmodedriver)
target_link_libraries(btrfs ntoskrnl_vista)
target_link_libraries(btrfs ntoskrnl_vista ${PSEH_LIB})
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

@ -37,6 +37,7 @@ static const UINT64 superblock_addrs[] = { 0x10000, 0x4000000, 0x4000000000, 0x4
#define TYPE_DEV_EXTENT 0xCC
#define TYPE_DEV_ITEM 0xD8
#define TYPE_CHUNK_ITEM 0xE4
#define TYPE_SUBVOL_UUID 0xFB
#define BTRFS_ROOT_ROOT 1
#define BTRFS_ROOT_EXTENT 2
@ -44,6 +45,7 @@ static const UINT64 superblock_addrs[] = { 0x10000, 0x4000000, 0x4000000000, 0x4
#define BTRFS_ROOT_DEVTREE 4
#define BTRFS_ROOT_FSTREE 5
#define BTRFS_ROOT_CHECKSUM 7
#define BTRFS_ROOT_UUID 9
#define BTRFS_COMPRESSION_NONE 0
#define BTRFS_COMPRESSION_ZLIB 1
@ -155,6 +157,33 @@ typedef struct {
} DEV_ITEM;
#define SYS_CHUNK_ARRAY_SIZE 0x800
#define BTRFS_NUM_BACKUP_ROOTS 4
typedef struct {
UINT64 root_tree_addr;
UINT64 root_tree_generation;
UINT64 chunk_tree_addr;
UINT64 chunk_tree_generation;
UINT64 extent_tree_addr;
UINT64 extent_tree_generation;
UINT64 fs_tree_addr;
UINT64 fs_tree_generation;
UINT64 dev_root_addr;
UINT64 dev_root_generation;
UINT64 csum_root_addr;
UINT64 csum_root_generation;
UINT64 total_bytes;
UINT64 bytes_used;
UINT64 num_devices;
UINT64 reserved[4];
UINT8 root_level;
UINT8 chunk_root_level;
UINT8 extent_root_level;
UINT8 fs_root_level;
UINT8 dev_root_level;
UINT8 csum_root_level;
UINT8 reserved2[10];
} superblock_backup;
typedef struct {
UINT8 checksum[32];
@ -190,8 +219,8 @@ typedef struct {
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_backup backup[BTRFS_NUM_BACKUP_ROOTS];
UINT8 reserved2[565];
} superblock;
#define BTRFS_TYPE_UNKNOWN 0
@ -324,6 +353,11 @@ typedef struct {
UINT64 flags;
} EXTENT_ITEM;
typedef struct {
KEY firstitem;
UINT8 level;
} EXTENT_ITEM2;
typedef struct {
UINT32 refcount;
} EXTENT_ITEM_V0;
@ -355,7 +389,7 @@ typedef struct {
UINT64 root;
UINT64 gen;
UINT64 objid;
UINT64 count;
UINT32 count;
} EXTENT_REF_V0;
typedef struct {

View file

@ -70,12 +70,12 @@ BEGIN
BLOCK "080904b0"
BEGIN
VALUE "FileDescription", "WinBtrfs"
VALUE "FileVersion", "0.2"
VALUE "FileVersion", "0.4"
VALUE "InternalName", "btrfs"
VALUE "LegalCopyright", "Copyright (c) Mark Harmstone 2016"
VALUE "OriginalFilename", "btrfs.sys"
VALUE "ProductName", "WinBtrfs"
VALUE "ProductVersion", "0.2"
VALUE "ProductVersion", "0.4"
END
END
BLOCK "VarFileInfo"

View file

@ -36,6 +36,7 @@
#ifdef __REACTOS__
#include <rtlfuncs.h>
#include <iotypes.h>
#include <pseh/pseh2.h>
#endif /* __REACTOS__ */
//#include <windows.h>
#include <windef.h>
@ -46,7 +47,6 @@
#include "btrfs.h"
#ifdef _DEBUG
// #define DEBUG_TREE_REFCOUNTS
// #define DEBUG_FCB_REFCOUNTS
// #define DEBUG_LONG_MESSAGES
#define DEBUG_PARANOID
@ -68,6 +68,9 @@
#define EA_DOSATTRIB "user.DOSATTRIB"
#define EA_DOSATTRIB_HASH 0x914f9939
#define EA_REPARSE "system.reparse"
#define EA_REPARSE_HASH 0x786f6167
#define READ_AHEAD_GRANULARITY 0x10000 // 64 KB
#ifdef _MSC_VER
@ -108,26 +111,18 @@ typedef struct _fcb {
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;
WCHAR* debug_desc;
BOOL ads;
UINT32 adssize;
@ -137,6 +132,24 @@ typedef struct _fcb {
LIST_ENTRY list_entry;
} fcb;
struct _file_ref;
typedef struct _file_ref {
fcb* fcb;
UNICODE_STRING filepart;
ANSI_STRING utf8;
UNICODE_STRING full_filename;
ULONG name_offset;
BOOL delete_on_close;
BOOL deleted;
LIST_ENTRY children;
LONG refcount;
struct _file_ref* parent;
WCHAR* debug_desc;
LIST_ENTRY list_entry;
} file_ref;
typedef struct _ccb {
USHORT NodeType;
CSHORT NodeSize;
@ -147,6 +160,8 @@ typedef struct _ccb {
UNICODE_STRING query_string;
BOOL has_wildcard;
BOOL specific_file;
ACCESS_MASK access;
file_ref* fileref;
} ccb;
// typedef struct _log_to_phys {
@ -203,7 +218,6 @@ typedef struct _tree {
// UINT64 address;
// UINT8 level;
tree_header header;
LONG refcount;
BOOL has_address;
UINT32 size;
struct _device_extension* Vcb;
@ -216,6 +230,7 @@ typedef struct _tree {
UINT64 new_address;
BOOL has_new_address;
UINT64 flags;
BOOL write;
} tree;
typedef struct {
@ -229,9 +244,9 @@ typedef struct _root {
root_nonpaged* nonpaged;
UINT64 lastinode;
ROOT_ITEM root_item;
struct _root* prev;
struct _root* next;
UNICODE_STRING path;
LIST_ENTRY fcbs;
LIST_ENTRY list_entry;
} root;
typedef struct {
@ -239,12 +254,6 @@ typedef struct {
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;
@ -272,6 +281,8 @@ typedef struct {
typedef struct {
PDEVICE_OBJECT devobj;
DEV_ITEM devitem;
BOOL removable;
ULONG change_count;
LIST_ENTRY disk_holes;
} device;
@ -283,6 +294,8 @@ typedef struct {
UINT32 oldused;
BOOL space_changed;
device** devices;
UINT64 cache_size;
UINT64 cache_inode;
LIST_ENTRY space;
LIST_ENTRY list_entry;
} chunk;
@ -301,9 +314,9 @@ typedef struct _device_extension {
superblock superblock;
// WCHAR label[MAX_LABEL_SIZE];
BOOL readonly;
fcb* fcbs;
BOOL removing;
fcb* volume_fcb;
fcb* root_fcb;
file_ref* root_fileref;
ERESOURCE DirResource;
KSPIN_LOCK FcbListLock;
ERESOURCE fcb_lock;
@ -318,20 +331,20 @@ typedef struct _device_extension {
// UINT64 chunk_root_phys_addr;
UINT64 root_tree_phys_addr;
// log_to_phys* log_to_phys;
root* roots;
LIST_ENTRY roots;
LIST_ENTRY drop_roots;
root* chunk_root;
root* root_root;
root* extent_root;
root* checksum_root;
root* dev_root;
root* uuid_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;
@ -341,6 +354,43 @@ typedef struct {
UINT32 uid;
} uid_map;
typedef struct {
LIST_ENTRY list_entry;
UINT64 key;
} ordered_list;
typedef struct {
ordered_list ol;
ULONG length;
UINT32* checksums;
BOOL deleted;
} changed_sector;
enum write_tree_status {
WriteTreeStatus_Pending,
WriteTreeStatus_Success,
WriteTreeStatus_Error,
WriteTreeStatus_Cancelling,
WriteTreeStatus_Cancelled
};
struct write_tree_context;
typedef struct {
struct write_tree_context* context;
UINT8* buf;
device* device;
PIRP Irp;
IO_STATUS_BLOCK iosb;
enum write_tree_status status;
LIST_ENTRY list_entry;
} write_tree_stripe;
typedef struct {
KEVENT Event;
LIST_ENTRY stripes;
} write_tree_context;
// #pragma pack(pop)
static __inline void init_tree_holder(tree_holder* th) {
@ -368,6 +418,27 @@ static __inline void win_time_to_unix(LARGE_INTEGER t, BTRFS_TIME* out) {
out->nanoseconds = (l % 10000000) * 100;
}
static __inline void insert_into_ordered_list(LIST_ENTRY* list, ordered_list* ins) {
LIST_ENTRY* le = list->Flink;
ordered_list* ol;
while (le != list) {
ol = (ordered_list*)le;
if (ol->key > ins->key) {
le->Blink->Flink = &ins->list_entry;
ins->list_entry.Blink = le->Blink;
le->Blink = &ins->list_entry;
ins->list_entry.Flink = le;
return;
}
le = le->Flink;
}
InsertTailList(list, &ins->list_entry);
}
// in btrfs.c
device* find_device_from_uuid(device_extension* Vcb, BTRFS_UUID* uuid);
ULONG sector_align( ULONG NumberToBeAligned, ULONG Alignment );
@ -377,15 +448,25 @@ BOOL STDCALL get_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char*
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);
void _free_fileref(file_ref* fr, 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);
NTSTATUS delete_fileref(file_ref* fileref, PFILE_OBJECT FileObject, LIST_ENTRY* rollback);
fcb* create_fcb();
file_ref* create_fileref();
void protect_superblocks(device_extension* Vcb, chunk* c);
BOOL is_top_level(PIRP Irp);
NTSTATUS create_root(device_extension* Vcb, UINT64 id, root** rootptr, BOOL no_tree, UINT64 offset, LIST_ENTRY* rollback);
void STDCALL uninit(device_extension* Vcb, BOOL flush);
NTSTATUS STDCALL dev_ioctl(PDEVICE_OBJECT DeviceObject, ULONG ControlCode, PVOID InputBuffer,
ULONG InputBufferSize, PVOID OutputBuffer, ULONG OutputBufferSize, BOOLEAN Override, IO_STATUS_BLOCK* iosb);
BOOL is_file_name_valid(PUNICODE_STRING us);
void send_notification_fileref(file_ref* fileref, ULONG filter_match, ULONG action);
WCHAR* file_desc(PFILE_OBJECT FileObject);
WCHAR* file_desc_fileref(file_ref* fileref);
#ifdef _MSC_VER
#define funcname __FUNCTION__
@ -395,26 +476,34 @@ BOOL is_top_level(PIRP Irp);
// 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__)
#define free_fileref(fileref) _free_fileref(fileref, funcname, __FILE__, __LINE__)
#ifdef _DEBUG
extern BOOL log_started;
extern UINT32 debug_log_level;
#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__)
#define MSG(fn, file, line, s, level, ...) (!log_started || level <= debug_log_level) ? _debug_message(fn, file, line, s, ##__VA_ARGS__) : 0
void STDCALL _debug_message(const char* func, UINT8 priority, const char* file, unsigned int line, char* s, ...);
#define TRACE(s, ...) MSG(funcname, __FILE__, __LINE__, s, 3, ##__VA_ARGS__)
#define WARN(s, ...) MSG(funcname, __FILE__, __LINE__, s, 2, ##__VA_ARGS__)
#define FIXME(s, ...) MSG(funcname, __FILE__, __LINE__, s, 1, ##__VA_ARGS__)
#define ERR(s, ...) MSG(funcname, __FILE__, __LINE__, s, 1, ##__VA_ARGS__)
void STDCALL _debug_message(const char* func, 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__)
#define MSG(fn, s, level, ...) (!log_started || level <= debug_log_level) ? _debug_message(fn, s, ##__VA_ARGS__) : 0
void STDCALL _debug_message(const char* func, UINT8 priority, char* s, ...);
#define TRACE(s, ...) MSG(funcname, s, 3, ##__VA_ARGS__)
#define WARN(s, ...) MSG(funcname, s, 2, ##__VA_ARGS__)
#define FIXME(s, ...) MSG(funcname, s, 1, ##__VA_ARGS__)
#define ERR(s, ...) MSG(funcname, s, 1, ##__VA_ARGS__)
void STDCALL _debug_message(const char* func, char* s, ...);
#endif
@ -432,6 +521,12 @@ void STDCALL _debug_message(const char* func, UINT8 priority, char* s, ...);
#endif
static __inline void increase_chunk_usage(chunk* c, UINT64 delta) {
c->used += delta;
TRACE("increasing size of chunk %llx by %llx\n", c->offset, delta);
}
// in fastio.c
void STDCALL init_fast_io_dispatch(FAST_IO_DISPATCH** fiod);
@ -442,23 +537,22 @@ UINT32 STDCALL calc_crc32c(UINT32 seed, UINT8* msg, ULONG msglen);
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);
void STDCALL free_trees(device_extension* Vcb);
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);
void free_trees_root(device_extension* Vcb, root* r);
NTSTATUS STDCALL read_tree(device_extension* Vcb, UINT64 addr, UINT8* buf);
#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
@ -474,43 +568,55 @@ 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 extend_file(fcb* fcb, file_ref* fileref, UINT64 end, BOOL prealloc, LIST_ENTRY* rollback);
NTSTATUS excise_extents_inode(device_extension* Vcb, root* subvol, UINT64 inode, INODE_ITEM* ii, UINT64 start_data, UINT64 end_data, LIST_ENTRY* changed_sector_list, 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);
BOOL insert_extent_chunk_inode(device_extension* Vcb, root* subvol, UINT64 inode, INODE_ITEM* inode_item, chunk* c, UINT64 start_data,
UINT64 length, BOOL prealloc, void* data, LIST_ENTRY* changed_sector_list, LIST_ENTRY* rollback);
chunk* alloc_chunk(device_extension* Vcb, UINT64 flags, LIST_ENTRY* rollback);
NTSTATUS STDCALL write_data(device_extension* Vcb, UINT64 address, void* data, UINT32 length);
NTSTATUS write_tree(device_extension* Vcb, UINT64 addr, UINT8* data, write_tree_context* wtc);
void free_write_tree_stripes(write_tree_context* wtc);
NTSTATUS get_tree_new_address(device_extension* Vcb, tree* t, LIST_ENTRY* rollback);
// in dirctrl.c
NTSTATUS STDCALL drv_directory_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
ULONG STDCALL get_reparse_tag(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type);
// 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);
void fcb_get_sd(fcb* fcb, struct _fcb* parent);
// UINT32 STDCALL get_uid();
void add_user_mapping(WCHAR* sidstring, ULONG sidstringlength, UINT32 uid);
NTSTATUS fcb_get_new_sd(fcb* fcb, ACCESS_STATE* as);
UINT32 sid_to_uid(PSID sid);
NTSTATUS fcb_get_new_sd(fcb* fcb, file_ref* fileref, 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);
NTSTATUS delete_root_ref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid, UINT64 parinode, PANSI_STRING utf8, UINT64* index, LIST_ENTRY* rollback);
NTSTATUS STDCALL update_root_backref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid, LIST_ENTRY* rollback);
BOOL has_open_children(file_ref* fileref);
NTSTATUS STDCALL stream_set_end_of_file_information(device_extension* Vcb, UINT64 end, fcb* fcb, file_ref* fileref, PFILE_OBJECT FileObject, BOOL advance_only, 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);
NTSTATUS delete_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);
NTSTATUS update_inode_item(device_extension* Vcb, root* subvol, UINT64 inode, INODE_ITEM* ii, LIST_ENTRY* rollback);
NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnus, file_ref* related, BOOL parent, USHORT* unparsed);
// in fsctl.c
NTSTATUS fsctl_request(PDEVICE_OBJECT DeviceObject, PIRP Irp, UINT32 type, BOOL user);
@ -522,13 +628,31 @@ void STDCALL flush_thread(void* context);
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);
// in pnp.c
NTSTATUS STDCALL drv_pnp(PDEVICE_OBJECT DeviceObject, PIRP Irp);
// in free-space.c
NTSTATUS load_free_space_cache(device_extension* Vcb, chunk* c);
NTSTATUS clear_free_space_cache(device_extension* Vcb);
NTSTATUS allocate_cache(device_extension* Vcb, BOOL* changed, LIST_ENTRY* rollback);
NTSTATUS update_chunk_caches(device_extension* Vcb, LIST_ENTRY* rollback);
// in extent-tree.c
NTSTATUS increase_extent_refcount_data(device_extension* Vcb, UINT64 address, UINT64 size, root* subvol, UINT64 inode, UINT64 offset, UINT32 refcount, LIST_ENTRY* rollback);
NTSTATUS decrease_extent_refcount_data(device_extension* Vcb, UINT64 address, UINT64 size, root* subvol, UINT64 inode, UINT64 offset, UINT32 refcount, LIST_ENTRY* changed_sector_list, LIST_ENTRY* rollback);
void decrease_chunk_usage(chunk* c, UINT64 delta);
NTSTATUS convert_shared_data_extent(device_extension* Vcb, UINT64 address, UINT64 size, LIST_ENTRY* rollback);
NTSTATUS convert_old_data_extent(device_extension* Vcb, UINT64 address, UINT64 size, LIST_ENTRY* rollback);
#define fast_io_possible(fcb) (!FsRtlAreThereCurrentFileLocks(&fcb->lock) && !fcb->Vcb->readonly ? FastIoIsPossible : FastIoIsQuestionable)
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);
ERR("tree %p: root %llx, level %u, first key (%llx,%x,%llx)\n",
t, t->root->id, t->header.level, td->key.obj_id, td->key.obj_type, td->key.offset);
le = le->Flink;
}
@ -580,24 +704,6 @@ static __inline void InsertAfter(LIST_ENTRY* head, LIST_ENTRY* item, LIST_ENTRY*
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. */
@ -613,6 +719,14 @@ static __inline void InsertAfter(LIST_ENTRY* head, LIST_ENTRY* item, LIST_ENTRY*
#define S_ISDIR(mode) __S_ISTYPE((mode), __S_IFDIR)
#endif
#ifndef S_IRUSR
#define S_IRUSR 0000400
#endif
#ifndef S_IWUSR
#define S_IWUSR 0000200
#endif
#ifndef S_IXUSR
#define S_IXUSR 0000100
#endif
@ -622,10 +736,26 @@ static __inline void InsertAfter(LIST_ENTRY* head, LIST_ENTRY* item, LIST_ENTRY*
#define S_IFREG __S_IFREG
#endif /* __REACTOS__ */
#ifndef S_IRGRP
#define S_IRGRP (S_IRUSR >> 3)
#endif
#ifndef S_IWGRP
#define S_IWGRP (S_IWUSR >> 3)
#endif
#ifndef S_IXGRP
#define S_IXGRP (S_IXUSR >> 3)
#endif
#ifndef S_IROTH
#define S_IROTH (S_IRGRP >> 3)
#endif
#ifndef S_IWOTH
#define S_IWOTH (S_IWGRP >> 3)
#endif
#ifndef S_IXOTH
#define S_IXOTH (S_IXGRP >> 3)
#endif
@ -641,6 +771,8 @@ NTSTATUS WINAPI RtlUTF8ToUnicodeN(WCHAR *uni_dest, ULONG uni_bytes_max,
#if defined(__REACTOS__) && (NTDDI_VERSION < NTDDI_VISTA)
NTSTATUS NTAPI FsRtlRemoveDotsFromPath(PWSTR OriginalString,
USHORT PathLength, USHORT *NewLength);
NTSTATUS NTAPI FsRtlValidateReparsePointBuffer(ULONG BufferLength,
PREPARSE_DATA_BUFFER ReparseBuffer);
#endif /* defined(__REACTOS__) && (NTDDI_VERSION < NTDDI_WIN7) */
#endif

View file

@ -2,6 +2,8 @@
#define BTRFSIOCTL_H_DEFINED
#define FSCTL_BTRFS_GET_FILE_IDS CTL_CODE(FILE_DEVICE_UNKNOWN, 0x829, METHOD_OUT_DIRECT, FILE_ANY_ACCESS)
#define FSCTL_BTRFS_CREATE_SUBVOL CTL_CODE(FILE_DEVICE_UNKNOWN, 0x82a, METHOD_IN_DIRECT, FILE_ANY_ACCESS)
#define FSCTL_BTRFS_CREATE_SNAPSHOT CTL_CODE(FILE_DEVICE_UNKNOWN, 0x82b, METHOD_IN_DIRECT, FILE_ANY_ACCESS)
typedef struct {
UINT64 subvol;
@ -9,4 +11,10 @@ typedef struct {
BOOL top;
} btrfs_get_file_ids;
typedef struct {
HANDLE subvol;
UINT32 namelen;
WCHAR name[1];
} btrfs_create_snapshot;
#endif

File diff suppressed because it is too large Load diff

View file

@ -31,7 +31,59 @@ typedef struct {
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) {
ULONG STDCALL get_reparse_tag(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type) {
ULONG att, tag, br;
NTSTATUS Status;
// FIXME - will this slow things down?
if (type == BTRFS_TYPE_SYMLINK)
return IO_REPARSE_TAG_SYMLINK;
if (type != BTRFS_TYPE_FILE && type != BTRFS_TYPE_DIRECTORY)
return 0;
att = get_file_attributes(Vcb, NULL, subvol, inode, type, FALSE, FALSE);
if (!(att & FILE_ATTRIBUTE_REPARSE_POINT))
return 0;
if (type == BTRFS_TYPE_DIRECTORY) {
UINT8* data;
UINT16 datalen;
if (!get_xattr(Vcb, subvol, inode, EA_REPARSE, EA_REPARSE_HASH, &data, &datalen))
return 0;
if (!data)
return 0;
if (datalen < sizeof(ULONG)) {
ExFreePool(data);
return 0;
}
RtlCopyMemory(&tag, data, sizeof(ULONG));
ExFreePool(data);
} else {
// FIXME - see if file loaded and cached, and do CcCopyRead if it is
Status = read_file(Vcb, subvol, inode, (UINT8*)&tag, 0, sizeof(ULONG), &br);
if (!NT_SUCCESS(Status)) {
ERR("read_file returned %08x\n", Status);
return 0;
}
if (br < sizeof(ULONG))
return 0;
}
return tag;
}
static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, LONG* len, PIRP Irp, dir_entry* de, root* r) {
PIO_STACK_LOCATION IrpSp;
UINT32 needed;
UINT64 inode;
@ -43,9 +95,21 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, void* buf, LONG* len, PIRP Irp,
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;
LIST_ENTRY* le;
r = NULL;
le = fcb->Vcb->roots.Flink;
while (le != &fcb->Vcb->roots) {
root* subvol = CONTAINING_RECORD(le, root, list_entry);
if (subvol->id == de->key.obj_id) {
r = subvol;
break;
}
le = le->Flink;
}
if (!r) {
ERR("could not find root %llx\n", de->key.obj_id);
@ -64,17 +128,23 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, void* buf, LONG* len, PIRP Irp,
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 (fileref) {
ExAcquireResourceSharedLite(&fcb->Vcb->fcb_lock, TRUE);
if (c->subvol == r && c->inode == inode) {
ii = c->inode_item;
found = TRUE;
break;
le = fileref->children.Flink;
while (le != &fileref->children) {
file_ref* c = CONTAINING_RECORD(le, file_ref, list_entry);
if (c->fcb->subvol == r && c->fcb->inode == inode && !c->fcb->ads) {
ii = c->fcb->inode_item;
found = TRUE;
break;
}
le = le->Flink;
}
le = le->Flink;
ExReleaseResourceLite(&fcb->Vcb->fcb_lock);
}
if (!found) {
@ -93,7 +163,6 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, void* buf, LONG* len, PIRP Irp,
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;
}
@ -101,8 +170,6 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, void* buf, LONG* len, PIRP Irp,
if (tp.item->size > 0)
RtlCopyMemory(&ii, tp.item->data, min(sizeof(INODE_ITEM), tp.item->size));
free_traverse_ptr(&tp);
}
break;
@ -115,9 +182,14 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, void* buf, LONG* len, PIRP Irp,
break;
case DirEntryType_Parent:
ii = fcb->par->inode_item;
r = fcb->par->subvol;
inode = fcb->par->inode;
if (fileref && fileref->parent) {
ii = fileref->parent->fcb->inode_item;
r = fileref->parent->fcb->subvol;
inode = fileref->parent->fcb->inode;
} else {
ERR("no fileref\n");
return STATUS_INTERNAL_ERROR;
}
break;
}
}
@ -162,7 +234,7 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, void* buf, LONG* len, PIRP Irp,
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->EaSize = get_reparse_tag(fcb->Vcb, r, inode, de->type);
fbdi->ShortNameLength = 0;
// fibdi->ShortName[12];
@ -237,7 +309,7 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, void* buf, LONG* len, PIRP Irp,
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;
ffdi->EaSize = get_reparse_tag(fcb->Vcb, r, inode, de->type);
Status = RtlUTF8ToUnicodeN(ffdi->FileName, stringlen, &stringlen, de->name, de->namelen);
@ -277,7 +349,7 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, void* buf, LONG* len, PIRP Irp,
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->EaSize = get_reparse_tag(fcb->Vcb, r, inode, de->type);
fibdi->ShortNameLength = 0;
// fibdi->ShortName[12];
fibdi->FileId.QuadPart = inode;
@ -347,13 +419,13 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, void* buf, LONG* len, PIRP Irp,
return STATUS_NO_MORE_FILES;
}
static NTSTATUS STDCALL next_dir_entry(fcb* fcb, UINT64* offset, dir_entry* de, traverse_ptr* tp) {
static NTSTATUS STDCALL next_dir_entry(fcb* fcb, file_ref* fileref, 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 (fileref && fileref->parent) { // don't return . and .. if root directory
if (*offset == 0) {
de->key.obj_id = fcb->inode;
de->key.obj_type = TYPE_INODE_ITEM;
@ -367,7 +439,7 @@ static NTSTATUS STDCALL next_dir_entry(fcb* fcb, UINT64* offset, dir_entry* de,
return STATUS_SUCCESS;
} else if (*offset == 1) {
de->key.obj_id = fcb->par->inode;
de->key.obj_id = fileref->parent->fcb->inode;
de->key.obj_type = TYPE_INODE_ITEM;
de->key.offset = 0;
de->dir_entry_type = DirEntryType_Parent;
@ -389,7 +461,6 @@ static NTSTATUS STDCALL next_dir_entry(fcb* fcb, UINT64* offset, dir_entry* de,
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;
}
@ -398,7 +469,6 @@ static NTSTATUS STDCALL next_dir_entry(fcb* fcb, UINT64* offset, dir_entry* de,
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);
@ -406,7 +476,6 @@ static NTSTATUS STDCALL next_dir_entry(fcb* fcb, UINT64* offset, dir_entry* de,
}
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;
}
@ -418,7 +487,6 @@ static NTSTATUS STDCALL next_dir_entry(fcb* fcb, UINT64* offset, dir_entry* de,
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;
}
@ -433,7 +501,6 @@ static NTSTATUS STDCALL next_dir_entry(fcb* fcb, UINT64* offset, dir_entry* de,
} 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;
@ -442,8 +509,6 @@ static NTSTATUS STDCALL next_dir_entry(fcb* fcb, UINT64* offset, dir_entry* de,
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;
}
@ -454,10 +519,8 @@ static NTSTATUS STDCALL next_dir_entry(fcb* fcb, UINT64* offset, dir_entry* de,
de->dir_entry_type = DirEntryType_File;
return STATUS_SUCCESS;
} else {
free_traverse_ptr(&next_tp);
} else
return STATUS_NO_MORE_FILES;
}
} else
return STATUS_NO_MORE_FILES;
}
@ -468,11 +531,12 @@ static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
NTSTATUS Status, status2;
fcb* fcb;
ccb* ccb;
file_ref* fileref;
void* buf;
UINT8 *curitem, *lastitem;
LONG length;
ULONG count;
BOOL has_wildcard = FALSE, specific_file = FALSE;
BOOL has_wildcard = FALSE, specific_file = FALSE, initial;
// UINT64 num_reads_orig;
traverse_ptr tp;
dir_entry de;
@ -486,10 +550,11 @@ static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
IrpSp = IoGetCurrentIrpStackLocation(Irp);
fcb = IrpSp->FileObject->FsContext;
ccb = IrpSp->FileObject->FsContext2;
fileref = ccb ? ccb->fileref : NULL;
acquire_tree_lock(fcb->Vcb, FALSE);
TRACE("%.*S\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
TRACE("%S\n", file_desc(IrpSp->FileObject));
if (IrpSp->Flags == 0) {
TRACE("QD flags: (none)\n");
@ -517,6 +582,8 @@ static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
TRACE(" unknown flags: %u\n", flags);
}
initial = !ccb->query_string.Buffer;
if (IrpSp->Flags & SL_RESTART_SCAN) {
ccb->query_dir_offset = 0;
@ -567,6 +634,9 @@ static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
} else {
has_wildcard = ccb->has_wildcard;
specific_file = ccb->specific_file;
if (!(IrpSp->Flags & SL_RESTART_SCAN))
initial = FALSE;
}
if (ccb->query_string.Buffer) {
@ -574,10 +644,10 @@ static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
}
tp.tree = NULL;
Status = next_dir_entry(fcb, &ccb->query_dir_offset, &de, &tp);
Status = next_dir_entry(fcb, fileref, &ccb->query_dir_offset, &de, &tp);
if (!NT_SUCCESS(Status)) {
if (Status == STATUS_NO_MORE_FILES && IrpSp->Flags & SL_RETURN_SINGLE_ENTRY)
if (Status == STATUS_NO_MORE_FILES && initial)
Status = STATUS_NO_SUCH_FILE;
goto end;
}
@ -604,7 +674,6 @@ static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
if (Irp->MdlAddress && !buf) {
ERR("MmGetSystemAddressForMdlSafe returned NULL\n");
Status = STATUS_INSUFFICIENT_RESOURCES;
if (tp.tree) free_traverse_ptr(&tp);
goto end;
}
@ -619,14 +688,12 @@ static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
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;
}
@ -635,7 +702,6 @@ static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
if (!NT_SUCCESS(Status)) {
ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
if (tp.tree) free_traverse_ptr(&tp);
goto end;
}
@ -643,21 +709,19 @@ static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
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);
Status = next_dir_entry(fcb, fileref, &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;
}
@ -667,16 +731,13 @@ static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
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)
if (Status == STATUS_NO_MORE_FILES && initial)
Status = STATUS_NO_SUCH_FILE;
goto end;
@ -689,7 +750,7 @@ static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
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);
Status = query_dir_item(fcb, fileref, buf, &length, Irp, &de, fcb->subvol);
count = 0;
if (NT_SUCCESS(Status) && !(IrpSp->Flags & SL_RETURN_SINGLE_ENTRY) && !specific_file) {
@ -717,7 +778,7 @@ static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
WCHAR* uni_fn = NULL;
UNICODE_STRING di_uni_fn;
Status = next_dir_entry(fcb, &ccb->query_dir_offset, &de, &tp);
Status = next_dir_entry(fcb, fileref, &ccb->query_dir_offset, &de, &tp);
if (NT_SUCCESS(Status)) {
if (has_wildcard) {
ULONG stringlen;
@ -725,14 +786,12 @@ static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
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;
}
@ -742,7 +801,6 @@ static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
if (!NT_SUCCESS(Status)) {
ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
ExFreePool(uni_fn);
if (tp.tree) free_traverse_ptr(&tp);
goto end;
}
@ -757,7 +815,7 @@ static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
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);
status2 = query_dir_item(fcb, fileref, curitem, &length, Irp, &de, fcb->subvol);
if (NT_SUCCESS(status2)) {
ULONG* lastoffset = (ULONG*)lastitem;
@ -789,8 +847,6 @@ static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
Irp->IoStatus.Information = IrpSp->Parameters.QueryDirectory.Length - length;
if (tp.tree) free_traverse_ptr(&tp);
end:
release_tree_lock(fcb->Vcb, FALSE);
@ -804,11 +860,18 @@ 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;
ccb* ccb = FileObject->FsContext2;
file_ref* fileref = ccb->fileref;
NTSTATUS Status;
// WCHAR fn[MAX_PATH];
TRACE("IRP_MN_NOTIFY_CHANGE_DIRECTORY\n");
if (!fileref) {
ERR("no fileref\n");
return STATUS_INVALID_PARAMETER;
}
acquire_tree_lock(fcb->Vcb, FALSE);
if (fcb->type != BTRFS_TYPE_DIRECTORY) {
@ -818,9 +881,9 @@ static NTSTATUS STDCALL notify_change_directory(device_extension* Vcb, PIRP Irp)
// FIXME - raise exception if FCB marked for deletion?
TRACE("%.*S\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
TRACE("%S\n", file_desc(FileObject));
FsRtlNotifyFullChangeDirectory(Vcb->NotifySync, &Vcb->DirNotifyList, FileObject->FsContext2, (PSTRING)&fcb->full_filename,
FsRtlNotifyFullChangeDirectory(Vcb->NotifySync, &Vcb->DirNotifyList, FileObject->FsContext2, (PSTRING)&fileref->full_filename,
IrpSp->Flags & SL_WATCH_TREE, FALSE, IrpSp->Parameters.NotifyDirectory.CompletionFilter, Irp, NULL, NULL);
Status = STATUS_PENDING;
@ -867,6 +930,10 @@ NTSTATUS STDCALL drv_directory_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP I
if (func != IRP_MN_NOTIFY_CHANGE_DIRECTORY || Status != STATUS_PENDING) {
Irp->IoStatus.Status = Status;
if (Irp->UserIosb)
*Irp->UserIosb = Irp->IoStatus;
IoCompleteRequest( Irp, IO_DISK_INCREMENT );
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -31,7 +31,7 @@ static void do_flush(device_extension* Vcb) {
if (Vcb->write_trees > 0)
do_write(Vcb, &rollback);
free_tree_cache(&Vcb->tree_cache);
free_trees(Vcb);
clear_rollback(&rollback);
@ -41,23 +41,31 @@ static void do_flush(device_extension* Vcb) {
}
void STDCALL flush_thread(void* context) {
device_extension* Vcb = context;
DEVICE_OBJECT* devobj = context;
device_extension* Vcb = devobj->DeviceExtension;
LARGE_INTEGER due_time;
KTIMER flush_thread_timer;
KeInitializeTimer(&Vcb->flush_thread_timer);
ObReferenceObject(devobj);
KeInitializeTimer(&flush_thread_timer);
due_time.QuadPart = -INTERVAL * 10000;
KeSetTimer(&Vcb->flush_thread_timer, due_time, NULL);
KeSetTimer(&flush_thread_timer, due_time, NULL);
while (TRUE) {
KeWaitForSingleObject(&Vcb->flush_thread_timer, Executive, KernelMode, FALSE, NULL);
KeWaitForSingleObject(&flush_thread_timer, Executive, KernelMode, FALSE, NULL);
if (!(devobj->Vpb->Flags & VPB_MOUNTED))
break;
do_flush(Vcb);
KeSetTimer(&Vcb->flush_thread_timer, due_time, NULL);
KeSetTimer(&flush_thread_timer, due_time, NULL);
}
KeCancelTimer(&Vcb->flush_thread_timer);
ObDereferenceObject(devobj);
KeCancelTimer(&flush_thread_timer);
PsTerminateSystemThread(STATUS_SUCCESS);
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,231 +0,0 @@
/*
* 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,283 @@
#include "btrfs_drv.h"
struct pnp_context;
typedef struct {
struct pnp_context* context;
PIRP Irp;
IO_STATUS_BLOCK iosb;
NTSTATUS Status;
} pnp_stripe;
typedef struct {
KEVENT Event;
NTSTATUS Status;
LONG left;
pnp_stripe* stripes;
} pnp_context;
static NTSTATUS STDCALL pnp_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
pnp_stripe* stripe = conptr;
pnp_context* context = (pnp_context*)stripe->context;
stripe->Status = Irp->IoStatus.Status;
InterlockedDecrement(&context->left);
if (context->left == 0)
KeSetEvent(&context->Event, 0, FALSE);
return STATUS_MORE_PROCESSING_REQUIRED;
}
static NTSTATUS send_disks_pnp_message(device_extension* Vcb, UCHAR minor) {
pnp_context* context;
UINT64 num_devices, i;
NTSTATUS Status;
context = ExAllocatePoolWithTag(NonPagedPool, sizeof(pnp_context), ALLOC_TAG);
if (!context) {
ERR("out of memory\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlZeroMemory(context, sizeof(pnp_context));
KeInitializeEvent(&context->Event, NotificationEvent, FALSE);
num_devices = Vcb->superblock.num_devices;
context->stripes = ExAllocatePoolWithTag(NonPagedPool, sizeof(pnp_stripe) * num_devices, ALLOC_TAG);
if (!context->stripes) {
ERR("out of memory\n");
ExFreePool(context);
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlZeroMemory(context->stripes, sizeof(pnp_stripe) * num_devices);
for (i = 0; i < num_devices; i++) {
PIO_STACK_LOCATION IrpSp;
if (Vcb->devices[i].devobj) {
context->stripes[i].context = (struct pnp_context*)context;
context->stripes[i].Irp = IoAllocateIrp(Vcb->devices[i].devobj->StackSize, FALSE);
if (!context->stripes[i].Irp) {
UINT64 j;
ERR("IoAllocateIrp failed\n");
for (j = 0; j < i; j++) {
if (Vcb->devices[j].devobj) {
IoFreeIrp(context->stripes[j].Irp);
}
}
ExFreePool(context->stripes);
ExFreePool(context);
return STATUS_INSUFFICIENT_RESOURCES;
}
IrpSp = IoGetNextIrpStackLocation(context->stripes[i].Irp);
IrpSp->MajorFunction = IRP_MJ_PNP;
IrpSp->MinorFunction = minor;
context->stripes[i].Irp->UserIosb = &context->stripes[i].iosb;
IoSetCompletionRoutine(context->stripes[i].Irp, pnp_completion, &context->stripes[i], TRUE, TRUE, TRUE);
context->stripes[i].Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
context->left++;
}
}
if (context->left == 0) {
Status = STATUS_SUCCESS;
goto end;
}
for (i = 0; i < num_devices; i++) {
if (context->stripes[i].Irp) {
IoCallDriver(Vcb->devices[i].devobj, context->stripes[i].Irp);
}
}
KeWaitForSingleObject(&context->Event, Executive, KernelMode, FALSE, NULL);
Status = STATUS_SUCCESS;
for (i = 0; i < num_devices; i++) {
if (context->stripes[i].Irp) {
if (context->stripes[i].Status != STATUS_SUCCESS)
Status = context->stripes[i].Status;
}
}
end:
for (i = 0; i < num_devices; i++) {
if (context->stripes[i].Irp) {
IoFreeIrp(context->stripes[i].Irp);
}
}
ExFreePool(context->stripes);
ExFreePool(context);
return Status;
}
static NTSTATUS pnp_cancel_remove_device(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
device_extension* Vcb = DeviceObject->DeviceExtension;
NTSTATUS Status;
ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
if (Vcb->root_fileref && Vcb->root_fileref->fcb && (Vcb->root_fileref->fcb->open_count > 0 || has_open_children(Vcb->root_fileref))) {
Status = STATUS_ACCESS_DENIED;
goto end;
}
Status = send_disks_pnp_message(Vcb, IRP_MN_CANCEL_REMOVE_DEVICE);
if (!NT_SUCCESS(Status)) {
WARN("send_disks_pnp_message returned %08x\n", Status);
goto end;
}
Vcb->removing = FALSE;
end:
ExReleaseResourceLite(&Vcb->fcb_lock);
return STATUS_SUCCESS;
}
static NTSTATUS pnp_query_remove_device(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
device_extension* Vcb = DeviceObject->DeviceExtension;
NTSTATUS Status;
LIST_ENTRY rollback;
ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
if (Vcb->root_fileref && Vcb->root_fileref->fcb && (Vcb->root_fileref->fcb->open_count > 0 || has_open_children(Vcb->root_fileref))) {
Status = STATUS_ACCESS_DENIED;
goto end;
}
Status = send_disks_pnp_message(Vcb, IRP_MN_QUERY_REMOVE_DEVICE);
if (!NT_SUCCESS(Status)) {
WARN("send_disks_pnp_message returned %08x\n", Status);
goto end;
}
Vcb->removing = TRUE;
InitializeListHead(&rollback);
acquire_tree_lock(Vcb, TRUE);
if (Vcb->write_trees > 0)
do_write(Vcb, &rollback);
clear_rollback(&rollback);
release_tree_lock(Vcb, TRUE);
Status = STATUS_SUCCESS;
end:
ExReleaseResourceLite(&Vcb->fcb_lock);
return Status;
}
static NTSTATUS pnp_remove_device(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
device_extension* Vcb = DeviceObject->DeviceExtension;
NTSTATUS Status;
Status = send_disks_pnp_message(Vcb, IRP_MN_REMOVE_DEVICE);
if (!NT_SUCCESS(Status)) {
WARN("send_disks_pnp_message returned %08x\n", Status);
}
if (DeviceObject->Vpb->Flags & VPB_MOUNTED) {
uninit(Vcb, FALSE);
DeviceObject->Vpb->Flags &= ~VPB_MOUNTED;
}
return STATUS_SUCCESS;
}
static NTSTATUS pnp_start_device(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
FIXME("STUB\n");
return STATUS_NOT_IMPLEMENTED;
}
static NTSTATUS pnp_surprise_removal(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
FIXME("STUB\n");
return STATUS_SUCCESS;
}
NTSTATUS STDCALL drv_pnp(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
device_extension* Vcb = DeviceObject->DeviceExtension;
NTSTATUS Status;
BOOL top_level;
FsRtlEnterFileSystem();
top_level = is_top_level(Irp);
Status = STATUS_NOT_IMPLEMENTED;
switch (IrpSp->MinorFunction) {
case IRP_MN_CANCEL_REMOVE_DEVICE:
Status = pnp_cancel_remove_device(DeviceObject, Irp);
break;
case IRP_MN_QUERY_REMOVE_DEVICE:
Status = pnp_query_remove_device(DeviceObject, Irp);
break;
case IRP_MN_REMOVE_DEVICE:
Status = pnp_remove_device(DeviceObject, Irp);
break;
case IRP_MN_START_DEVICE:
Status = pnp_start_device(DeviceObject, Irp);
break;
case IRP_MN_SURPRISE_REMOVAL:
Status = pnp_surprise_removal(DeviceObject, Irp);
break;
default:
TRACE("passing minor function 0x%x on\n", IrpSp->MinorFunction);
IoSkipCurrentIrpStackLocation(Irp);
Status = IoCallDriver(Vcb->devices[0].devobj, Irp);
goto end;
}
// // Irp->IoStatus.Status = Status;
// // Irp->IoStatus.Information = 0;
//
// IoSkipCurrentIrpStackLocation(Irp);
//
// Status = IoCallDriver(Vcb->devices[0].devobj, Irp);
//
// // IoCompleteRequest(Irp, IO_NO_INCREMENT);
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
end:
if (top_level)
IoSetTopLevelIrp(NULL);
FsRtlExitFileSystem();
return Status;
}

View file

@ -331,7 +331,6 @@ NTSTATUS STDCALL read_file(device_extension* Vcb, root* subvol, UINT64 inode, UI
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);
@ -339,7 +338,6 @@ NTSTATUS STDCALL read_file(device_extension* Vcb, root* subvol, UINT64 inode, UI
}
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;
@ -347,7 +345,6 @@ NTSTATUS STDCALL read_file(device_extension* Vcb, root* subvol, UINT64 inode, UI
if (tp.item->key.offset > start) {
ERR("first EXTENT_DATA was after offset\n");
free_traverse_ptr(&tp);
Status = STATUS_INTERNAL_ERROR;
goto exit;
}
@ -375,14 +372,12 @@ NTSTATUS STDCALL read_file(device_extension* Vcb, root* subvol, UINT64 inode, UI
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;
}
@ -393,28 +388,24 @@ NTSTATUS STDCALL read_file(device_extension* Vcb, root* subvol, UINT64 inode, UI
if (tp.item->key.offset + len < 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;
}
@ -452,7 +443,6 @@ NTSTATUS STDCALL read_file(device_extension* Vcb, root* subvol, UINT64 inode, UI
if (!buf) {
ERR("out of memory\n");
free_traverse_ptr(&tp);
Status = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
@ -463,7 +453,6 @@ NTSTATUS STDCALL read_file(device_extension* Vcb, root* subvol, UINT64 inode, UI
if (!NT_SUCCESS(Status)) {
ERR("read_data returned %08x\n", Status);
ExFreePool(buf);
free_traverse_ptr(&tp);
goto exit;
}
@ -495,7 +484,6 @@ NTSTATUS STDCALL read_file(device_extension* Vcb, root* subvol, UINT64 inode, UI
default:
WARN("Unsupported extent data type %u\n", ed->type);
free_traverse_ptr(&tp);
Status = STATUS_NOT_IMPLEMENTED;
goto exit;
}
@ -509,20 +497,16 @@ NTSTATUS STDCALL read_file(device_extension* Vcb, root* subvol, UINT64 inode, UI
next_tp.item->key.obj_type != TYPE_EXTENT_DATA ||
next_tp.item->key.offset != tp.item->key.offset + len
) {
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;
@ -547,53 +531,18 @@ NTSTATUS STDCALL drv_read(PDEVICE_OBJECT DeviceObject, PIRP 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;
Irp->IoStatus.Information = 0;
if (IrpSp->MinorFunction & IRP_MN_COMPLETE) {
CcMdlReadComplete(IrpSp->FileObject, Irp->MdlAddress);
Irp->MdlAddress = NULL;
Status = STATUS_SUCCESS;
bytes_read = 0;
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;
@ -603,11 +552,14 @@ NTSTATUS STDCALL drv_read(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
goto exit;
}
TRACE("file = %.*S (fcb = %p)\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer, fcb);
TRACE("file = %S (fcb = %p)\n", file_desc(FileObject), 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 (fcb->type == BTRFS_TYPE_DIRECTORY) {
Status = STATUS_INVALID_DEVICE_REQUEST;
goto exit;
}
if (!(Irp->Flags & IRP_PAGING_IO) && !FsRtlCheckLockForReadAccess(&fcb->lock, Irp)) {
WARN("tried to read locked region\n");
@ -630,9 +582,19 @@ NTSTATUS STDCALL drv_read(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
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 || !(IrpSp->MinorFunction & IRP_MN_MDL)) {
data = map_user_buffer(Irp);
if (Irp->MdlAddress && !data) {
ERR("MmGetSystemAddressForMdlSafe returned NULL\n");
Status = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
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)) {
@ -640,7 +602,7 @@ NTSTATUS STDCALL drv_read(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
Status = STATUS_SUCCESS;
// try {
try {
if (!FileObject->PrivateCacheMap) {
CC_FILE_SIZES ccfs;
@ -659,19 +621,23 @@ NTSTATUS STDCALL drv_read(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
// 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;
if (IrpSp->MinorFunction & IRP_MN_MDL) {
CcMdlRead(FileObject,&IrpSp->Parameters.Read.ByteOffset, length, &Irp->MdlAddress, &Irp->IoStatus);
} else {
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");
}
TRACE("CcCopyRead finished\n");
// } except (EXCEPTION_EXECUTE_HANDLER) {
// Status = GetExceptionCode();
// }
} except (EXCEPTION_EXECUTE_HANDLER) {
Status = GetExceptionCode();
}
if (NT_SUCCESS(Status)) {
Status = Irp->IoStatus.Status;
@ -711,6 +677,10 @@ exit:
if (FileObject->Flags & FO_SYNCHRONOUS_IO && !(Irp->Flags & IRP_PAGING_IO))
FileObject->CurrentByteOffset.QuadPart = start + (NT_SUCCESS(Status) ? bytes_read : 0);
// fastfat doesn't do this, but the Wine ntdll file test seems to think we ought to
if (Irp->UserIosb)
*Irp->UserIosb = Irp->IoStatus;
TRACE("Irp->IoStatus.Status = %08x\n", Irp->IoStatus.Status);
TRACE("Irp->IoStatus.Information = %lu\n", Irp->IoStatus.Information);
TRACE("returning %08x\n", Status);

View file

@ -17,148 +17,6 @@
#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;
@ -168,81 +26,118 @@ NTSTATUS get_reparse_point(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject,
char* data;
NTSTATUS Status;
// FIXME - check permissions
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 (fcb->type == BTRFS_TYPE_SYMLINK) {
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);
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);
goto end;
Status = STATUS_SUCCESS;
} else if (fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT) {
if (fcb->type == BTRFS_TYPE_FILE) {
Status = read_file(DeviceObject->DeviceExtension, fcb->subvol, fcb->inode, buffer, 0, buflen, retlen);
if (!NT_SUCCESS(Status)) {
ERR("read_file returned %08x\n", Status);
}
} else if (fcb->type == BTRFS_TYPE_DIRECTORY) {
UINT8* data;
UINT16 datalen;
if (!get_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_REPARSE, EA_REPARSE_HASH, &data, &datalen)) {
Status = STATUS_NOT_A_REPARSE_POINT;
goto end;
}
if (!data) {
Status = STATUS_NOT_A_REPARSE_POINT;
goto end;
}
if (datalen < sizeof(ULONG)) {
ExFreePool(data);
Status = STATUS_NOT_A_REPARSE_POINT;
goto end;
}
if (buflen > 0) {
*retlen = min(buflen, datalen);
RtlCopyMemory(buffer, data, *retlen);
} else
*retlen = 0;
ExFreePool(data);
} else
Status = STATUS_NOT_A_REPARSE_POINT;
} else {
Status = STATUS_NOT_A_REPARSE_POINT;
}
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);
@ -278,7 +173,6 @@ static NTSTATUS change_file_type(device_extension* Vcb, UINT64 inode, root* subv
if (!di) {
ERR("out of memory\n");
free_traverse_ptr(&tp);
return STATUS_INSUFFICIENT_RESOURCES;
}
@ -314,8 +208,6 @@ static NTSTATUS change_file_type(device_extension* Vcb, UINT64 inode, root* subv
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;
@ -334,7 +226,6 @@ static NTSTATUS change_file_type(device_extension* Vcb, UINT64 inode, root* subv
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;
}
@ -346,85 +237,24 @@ static NTSTATUS change_file_type(device_extension* Vcb, UINT64 inode, root* subv
}
}
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;
static NTSTATUS set_symlink(PIRP Irp, fcb* fcb, REPARSE_DATA_BUFFER* rdb, ULONG buflen, LIST_ENTRY* rollback) {
NTSTATUS Status;
ULONG 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;
return STATUS_INVALID_PARAMETER;
}
subname.Buffer = &rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)];
@ -441,7 +271,7 @@ NTSTATUS set_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
goto end;
return Status;
}
do {
@ -464,11 +294,11 @@ NTSTATUS set_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
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);
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;
return Status;
}
if (size > sizeof(INODE_REF) - 1 + ir->n) {
@ -483,15 +313,12 @@ NTSTATUS set_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
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;
@ -500,7 +327,7 @@ NTSTATUS set_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
goto end;
return Status;
}
do {
@ -523,11 +350,11 @@ NTSTATUS set_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
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);
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;
return Status;
}
if (size > sizeof(INODE_EXTREF) - 1 + ier->n) {
@ -542,42 +369,39 @@ NTSTATUS set_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
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);
Status = truncate_file(fcb, 0, rollback);
if (!NT_SUCCESS(Status)) {
ERR("truncate_file returned %08x\n", Status);
goto end;
return Status;
}
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;
return Status;
}
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;
return STATUS_INSUFFICIENT_RESOURCES;
}
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;
ExFreePool(target.Buffer);
return Status;
}
for (i = 0; i < target.Length; i++) {
@ -586,10 +410,427 @@ NTSTATUS set_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
}
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);
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);
return Status;
}
static NTSTATUS delete_symlink(fcb* fcb, LIST_ENTRY* rollback) {
NTSTATUS Status;
KEY searchkey;
traverse_ptr tp, next_tp;
BOOL b;
LARGE_INTEGER time;
BTRFS_TIME now;
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);
return Status;
}
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_FILE, rollback);
if (!NT_SUCCESS(Status)) {
ERR("error - change_file_type returned %08x\n", Status);
return Status;
}
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) {
tp = next_tp;
b = tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_INODE_REF;
}
} while (b);
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);
return Status;
}
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_FILE, rollback);
if (!NT_SUCCESS(Status)) {
ERR("error - change_file_type returned %08x\n", Status);
return Status;
}
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) {
tp = next_tp;
b = tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_INODE_EXTREF;
}
} while (b);
}
Status = truncate_file(fcb, 0, rollback);
if (!NT_SUCCESS(Status)) {
ERR("truncate_file returned %08x\n", Status);
return Status;
}
KeQuerySystemTime(&time);
win_time_to_unix(time, &now);
fcb->type = BTRFS_TYPE_FILE;
fcb->inode_item.st_mode &= ~__S_IFLNK;
fcb->inode_item.st_mode |= __S_IFREG;
fcb->inode_item.transid = fcb->Vcb->superblock.generation;
fcb->inode_item.sequence++;
fcb->inode_item.st_ctime = now;
fcb->inode_item.st_mtime = now;
Status = update_inode_item(fcb->Vcb, fcb->subvol, fcb->inode, &fcb->inode_item, rollback);
if (!NT_SUCCESS(Status)) {
ERR("update_inode_item returned %08x\n", Status);
return Status;
}
fcb->subvol->root_item.ctransid = fcb->Vcb->superblock.generation;
fcb->subvol->root_item.ctime = now;
return Status;
}
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;
REPARSE_DATA_BUFFER* rdb = buffer;
DWORD buflen = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
NTSTATUS Status = STATUS_SUCCESS;
fcb* fcb;
ULONG tag;
LIST_ENTRY rollback;
// FIXME - send notification if this succeeds? The attributes will have changed.
// FIXME - check permissions
TRACE("(%p, %p)\n", DeviceObject, Irp);
InitializeListHead(&rollback);
if (!FileObject) {
ERR("FileObject was NULL\n");
return STATUS_INVALID_PARAMETER;
}
fcb = FileObject->FsContext;
TRACE("%S\n", file_desc(FileObject));
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;
}
// FIXME - fail if we already have the attribute FILE_ATTRIBUTE_REPARSE_POINT
// FIXME - die if not file or directory
// 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 (fcb->type == BTRFS_TYPE_FILE && tag == IO_REPARSE_TAG_SYMLINK && rdb->SymbolicLinkReparseBuffer.Flags & SYMLINK_FLAG_RELATIVE) {
Status = set_symlink(Irp, fcb, rdb, buflen, &rollback);
} else {
LARGE_INTEGER offset;
char val[64];
if (fcb->type == BTRFS_TYPE_DIRECTORY) { // for directories, store as xattr
Status = set_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_REPARSE, EA_REPARSE_HASH, buffer, buflen, &rollback);
if (!NT_SUCCESS(Status)) {
ERR("set_xattr returned %08x\n", Status);
goto end;
}
} else { // otherwise, store as file data
Status = truncate_file(fcb, 0, &rollback);
if (!NT_SUCCESS(Status)) {
ERR("truncate_file returned %08x\n", Status);
goto end;
}
offset.QuadPart = 0;
Status = write_file2(fcb->Vcb, Irp, offset, buffer, &buflen, Irp->Flags & IRP_PAGING_IO, Irp->Flags & IRP_NOCACHE, &rollback);
if (!NT_SUCCESS(Status)) {
ERR("write_file2 returned %08x\n", Status);
goto end;
}
}
fcb->atts |= FILE_ATTRIBUTE_REPARSE_POINT;
sprintf(val, "0x%lx", fcb->atts);
Status = set_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (UINT8*)val, strlen(val), &rollback);
if (!NT_SUCCESS(Status)) {
ERR("set_xattr returned %08x\n", Status);
goto end;
}
}
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;
}
NTSTATUS delete_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
PFILE_OBJECT FileObject = IrpSp->FileObject;
REPARSE_DATA_BUFFER* rdb = Irp->AssociatedIrp.SystemBuffer;
DWORD buflen = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
NTSTATUS Status;
fcb* fcb;
ccb* ccb;
file_ref* fileref;
LIST_ENTRY rollback;
// FIXME - send notification if this succeeds? The attributes will have changed.
// FIXME - check permissions
TRACE("(%p, %p)\n", DeviceObject, Irp);
InitializeListHead(&rollback);
if (!FileObject) {
ERR("FileObject was NULL\n");
return STATUS_INVALID_PARAMETER;
}
fcb = FileObject->FsContext;
ccb = FileObject->FsContext2;
fileref = ccb ? ccb->fileref : NULL;
TRACE("%S\n", file_desc(FileObject));
if (!fileref) {
ERR("fileref was NULL\n");
return STATUS_INVALID_PARAMETER;
}
if (buflen < offsetof(REPARSE_DATA_BUFFER, GenericReparseBuffer.DataBuffer)) {
ERR("buffer was too short\n");
return STATUS_INVALID_PARAMETER;
}
if (rdb->ReparseDataLength > 0) {
WARN("rdb->ReparseDataLength was not zero\n");
return STATUS_INVALID_PARAMETER;
}
acquire_tree_lock(fcb->Vcb, TRUE);
if (fcb->ads) {
WARN("tried to delete reparse point on ADS\n");
Status = STATUS_INVALID_PARAMETER;
goto end;
}
if (fcb->type == BTRFS_TYPE_SYMLINK) {
if (rdb->ReparseTag != IO_REPARSE_TAG_SYMLINK) {
WARN("reparse tag was not IO_REPARSE_TAG_SYMLINK\n");
Status = STATUS_INVALID_PARAMETER;
goto end;
}
Status = delete_symlink(fcb, &rollback);
if (!NT_SUCCESS(Status)) {
ERR("delete_symlink returned %08x\n", Status);
goto end;
}
} else if (fcb->type == BTRFS_TYPE_FILE) {
LARGE_INTEGER time;
BTRFS_TIME now;
ULONG defda;
// FIXME - do we need to check that the reparse tags match?
Status = truncate_file(fcb, 0, &rollback);
if (!NT_SUCCESS(Status)) {
ERR("truncate_file returned %08x\n", Status);
goto end;
}
fcb->atts &= ~FILE_ATTRIBUTE_REPARSE_POINT;
defda = get_file_attributes(fcb->Vcb, &fcb->inode_item, fcb->subvol, fcb->inode, fcb->type, fileref->filepart.Length > 0 && fileref->filepart.Buffer[0] == '.', TRUE);
if (defda != fcb->atts) {
char val[64];
sprintf(val, "0x%lx", fcb->atts);
Status = set_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (UINT8*)val, strlen(val), &rollback);
if (!NT_SUCCESS(Status)) {
ERR("set_xattr returned %08x\n", Status);
goto end;
}
} else
delete_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, &rollback);
KeQuerySystemTime(&time);
win_time_to_unix(time, &now);
fcb->inode_item.transid = fcb->Vcb->superblock.generation;
fcb->inode_item.sequence++;
fcb->inode_item.st_ctime = now;
fcb->inode_item.st_mtime = now;
Status = update_inode_item(fcb->Vcb, fcb->subvol, fcb->inode, &fcb->inode_item, &rollback);
if (!NT_SUCCESS(Status)) {
ERR("update_inode_item returned %08x\n", Status);
goto end;
}
fcb->subvol->root_item.ctransid = fcb->Vcb->superblock.generation;
fcb->subvol->root_item.ctime = now;
} else if (fcb->type == BTRFS_TYPE_DIRECTORY) {
LARGE_INTEGER time;
BTRFS_TIME now;
ULONG defda;
// FIXME - do we need to check that the reparse tags match?
fcb->atts &= ~FILE_ATTRIBUTE_REPARSE_POINT;
defda = get_file_attributes(fcb->Vcb, &fcb->inode_item, fcb->subvol, fcb->inode, fcb->type, fileref->filepart.Length > 0 && fileref->filepart.Buffer[0] == '.', TRUE);
defda |= FILE_ATTRIBUTE_DIRECTORY;
if (defda != fcb->atts) {
char val[64];
sprintf(val, "0x%lx", fcb->atts);
Status = set_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (UINT8*)val, strlen(val), &rollback);
if (!NT_SUCCESS(Status)) {
ERR("set_xattr returned %08x\n", Status);
goto end;
}
} else
delete_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, &rollback);
delete_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_REPARSE, EA_REPARSE_HASH, &rollback);
KeQuerySystemTime(&time);
win_time_to_unix(time, &now);
fcb->inode_item.transid = fcb->Vcb->superblock.generation;
fcb->inode_item.sequence++;
fcb->inode_item.st_ctime = now;
fcb->inode_item.st_mtime = now;
Status = update_inode_item(fcb->Vcb, fcb->subvol, fcb->inode, &fcb->inode_item, &rollback);
if (!NT_SUCCESS(Status)) {
ERR("update_inode_item returned %08x\n", Status);
goto end;
}
fcb->subvol->root_item.ctransid = fcb->Vcb->superblock.generation;
fcb->subvol->root_item.ctime = now;
} else {
ERR("unsupported file type %u\n", fcb->type);
Status = STATUS_INVALID_PARAMETER;
goto end;
}
Status = STATUS_SUCCESS;
if (NT_SUCCESS(Status))
Status = consider_write(fcb->Vcb);

View file

@ -281,7 +281,7 @@ static void uid_to_sid(UINT32 uid, PSID* sid) {
*sid = sh;
}
static UINT32 sid_to_uid(PSID sid) {
UINT32 sid_to_uid(PSID sid) {
LIST_ENTRY* le;
uid_map* um;
sid_header* sh = sid;
@ -646,7 +646,7 @@ static BOOL get_sd_from_xattr(fcb* fcb) {
return TRUE;
}
void fcb_get_sd(fcb* fcb) {
void fcb_get_sd(fcb* fcb, struct _fcb* parent) {
NTSTATUS Status;
SECURITY_DESCRIPTOR sd;
ULONG buflen;
@ -694,10 +694,10 @@ void fcb_get_sd(fcb* fcb) {
}
// }
if (!fcb->par)
if (!parent)
acl = load_default_acl();
else
acl = inherit_acl(fcb->par->sd, fcb->type != BTRFS_TYPE_DIRECTORY);
acl = inherit_acl(parent->sd, fcb->type != BTRFS_TYPE_DIRECTORY);
if (!acl) {
ERR("out of memory\n");
@ -758,9 +758,17 @@ end:
static NTSTATUS STDCALL get_file_security(device_extension* Vcb, PFILE_OBJECT FileObject, SECURITY_DESCRIPTOR* relsd, ULONG* buflen, SECURITY_INFORMATION flags) {
NTSTATUS Status;
fcb* fcb = FileObject->FsContext;
ccb* ccb = FileObject->FsContext2;
file_ref* fileref = ccb ? ccb->fileref : NULL;
if (fcb->ads)
fcb = fcb->par;
if (fcb->ads) {
if (fileref && fileref->parent)
fcb = fileref->parent->fcb;
else {
ERR("could not get parent fcb for stream\n");
return STATUS_INTERNAL_ERROR;
}
}
// TRACE("buflen = %u, fcb->sdlen = %u\n", *buflen, fcb->sdlen);
@ -846,6 +854,8 @@ NTSTATUS STDCALL drv_query_security(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
static NTSTATUS STDCALL set_file_security(device_extension* Vcb, PFILE_OBJECT FileObject, SECURITY_DESCRIPTOR* sd, SECURITY_INFORMATION flags) {
NTSTATUS Status;
fcb* fcb = FileObject->FsContext;
ccb* ccb = FileObject->FsContext2;
file_ref* fileref = ccb ? ccb->fileref : NULL;
SECURITY_DESCRIPTOR* oldsd;
INODE_ITEM* ii;
KEY searchkey;
@ -863,8 +873,14 @@ static NTSTATUS STDCALL set_file_security(device_extension* Vcb, PFILE_OBJECT Fi
acquire_tree_lock(Vcb, TRUE);
if (fcb->ads)
fcb = fcb->par;
if (fcb->ads) {
if (fileref && fileref->parent)
fcb = fileref->parent->fcb;
else {
ERR("could not find parent fcb for stream\n");
return STATUS_INTERNAL_ERROR;
}
}
if (fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY) {
Status = STATUS_ACCESS_DENIED;
@ -895,12 +911,10 @@ static NTSTATUS STDCALL set_file_security(device_extension* Vcb, PFILE_OBJECT Fi
if (keycmp(&tp.item->key, &searchkey)) {
ERR("error - could not find INODE_ITEM for inode %llx in subvol %llx\n", fcb->inode, fcb->subvol->id);
Status = STATUS_INTERNAL_ERROR;
free_traverse_ptr(&tp);
goto end;
}
delete_tree_item(Vcb, &tp, &rollback);
free_traverse_ptr(&tp);
KeQuerySystemTime(&time);
win_time_to_unix(time, &now);
@ -1009,12 +1023,12 @@ NTSTATUS STDCALL drv_set_security(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
return Status;
}
NTSTATUS fcb_get_new_sd(fcb* fcb, ACCESS_STATE* as) {
NTSTATUS fcb_get_new_sd(fcb* fcb, file_ref* fileref, ACCESS_STATE* as) {
NTSTATUS Status;
PSID owner;
BOOLEAN defaulted;
Status = SeAssignSecurity(fcb->par ? fcb->par->sd : NULL, as->SecurityDescriptor, (void**)&fcb->sd, fcb->type == BTRFS_TYPE_DIRECTORY,
Status = SeAssignSecurity((fileref && fileref->parent) ? fileref->parent->fcb->sd : NULL, as->SecurityDescriptor, (void**)&fcb->sd, fcb->type == BTRFS_TYPE_DIRECTORY,
&as->SubjectSecurityContext, IoGetFileObjectGenericMapping(), PagedPool);
if (!NT_SUCCESS(Status)) {

View file

@ -105,7 +105,7 @@ end:
return STATUS_MORE_PROCESSING_REQUIRED;
}
static NTSTATUS STDCALL read_tree(device_extension* Vcb, UINT64 addr, UINT8* buf) {
NTSTATUS STDCALL read_tree(device_extension* Vcb, UINT64 addr, UINT8* buf) {
CHUNK_ITEM* ci;
CHUNK_ITEM_STRIPE* cis;
read_tree_context* context;
@ -277,6 +277,17 @@ static NTSTATUS STDCALL read_tree(device_extension* Vcb, UINT64 addr, UINT8* buf
// FIXME - if checksum error, write good data over bad
// check if any of the devices return a "user-induced" error
for (i = 0; i < ci->num_stripes; i++) {
if (context->stripes[i].status == ReadTreeStatus_Error && IoIsErrorUserInduced(context->stripes[i].iosb.Status)) {
IoSetHardErrorOrVerifyDevice(context->stripes[i].Irp, devices[i]->devobj);
Status = context->stripes[i].iosb.Status;
goto exit;
}
}
// check if any of the stripes succeeded
for (i = 0; i < ci->num_stripes; i++) {
@ -385,7 +396,6 @@ NTSTATUS STDCALL _load_tree(device_extension* Vcb, UINT64 addr, root* r, tree**
RtlCopyMemory(&t->header, th, sizeof(tree_header));
// t->address = addr;
// t->level = th->level;
t->refcount = 1;
t->has_address = TRUE;
t->Vcb = Vcb;
t->parent = NULL;
@ -395,13 +405,7 @@ NTSTATUS STDCALL _load_tree(device_extension* Vcb, UINT64 addr, root* r, tree**
t->size = 0;
t->new_address = 0;
t->has_new_address = FALSE;
#ifdef DEBUG_TREE_REFCOUNTS
#ifdef DEBUG_LONG_MESSAGES
_debug_message(func, file, line, "loaded tree %p (%llx)\n", t, addr);
#else
_debug_message(func, "loaded tree %p (%llx)\n", t, addr);
#endif
#endif
t->write = FALSE;
c = get_chunk_from_address(Vcb, addr);
@ -494,85 +498,56 @@ NTSTATUS STDCALL _load_tree(device_extension* Vcb, UINT64 addr, root* r, tree**
}
static tree* free_tree2(tree* t, const char* func, const char* file, unsigned int line) {
LONG rc;
LIST_ENTRY* le;
tree_data* td;
tree* par;
#ifdef DEBUG_TREE_REFCOUNTS
TRACE("(%p)\n", t);
#endif
root* r = t->root;
par = t->parent;
// if (par) ExAcquireResourceExclusiveLite(&par->nonpaged->load_tree_lock, TRUE);
rc = InterlockedDecrement(&t->refcount);
if (r && r->treeholder.tree != t)
r = NULL;
#ifdef DEBUG_TREE_REFCOUNTS
#ifdef DEBUG_LONG_MESSAGES
_debug_message(func, file, line, "tree %p: refcount decreased to %i (free_tree2)\n", t, rc);
#else
_debug_message(func, "tree %p: refcount decreased to %i (free_tree2)\n", t, rc);
#endif
#endif
if (rc < 0) {
ERR("error - negative refcount (%i)\n", rc);
int3;
}
if (rc == 0) {
root* r = t->root;
if (r && r->treeholder.tree != t)
r = NULL;
// if (r) {
// FsRtlEnterFileSystem();
// ExAcquireResourceExclusiveLite(&r->nonpaged->load_tree_lock, TRUE);
// }
if (par) {
if (t->paritem)
t->paritem->treeholder.tree = NULL;
if (par) {
if (t->paritem)
t->paritem->treeholder.tree = NULL;
// ExReleaseResourceLite(&par->nonpaged->load_tree_lock);
}
if (t->parent)
t->parent = free_tree2(t->parent, func, file, line);
// ExDeleteResourceLite(&t->nonpaged->load_tree_lock);
// ExFreePool(t->nonpaged);
while (!IsListEmpty(&t->itemlist)) {
le = RemoveHeadList(&t->itemlist);
td = CONTAINING_RECORD(le, tree_data, list_entry);
if (t->header.level == 0 && td->data)
ExFreePool(td->data);
ExFreePool(td);
}
InterlockedDecrement(&t->Vcb->open_trees);
RemoveEntryList(&t->list_entry);
if (r) {
r->treeholder.tree = NULL;
// ExReleaseResourceLite(&r->nonpaged->load_tree_lock);
// FsRtlExitFileSystem();
}
ExFreePool(t);
return NULL;
} else {
// if (par) ExReleaseResourceLite(&par->nonpaged->load_tree_lock);
}
return t;
// ExDeleteResourceLite(&t->nonpaged->load_tree_lock);
// ExFreePool(t->nonpaged);
while (!IsListEmpty(&t->itemlist)) {
le = RemoveHeadList(&t->itemlist);
td = CONTAINING_RECORD(le, tree_data, list_entry);
if (t->header.level == 0 && td->data)
ExFreePool(td->data);
ExFreePool(td);
}
InterlockedDecrement(&t->Vcb->open_trees);
RemoveEntryList(&t->list_entry);
if (r) {
r->treeholder.tree = NULL;
// ExReleaseResourceLite(&r->nonpaged->load_tree_lock);
// FsRtlExitFileSystem();
}
ExFreePool(t);
return NULL;
}
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) {
@ -623,11 +598,8 @@ NTSTATUS STDCALL _do_load_tree(device_extension* Vcb, tree_holder* th, root* r,
th->tree->paritem = td;
ret = TRUE;
} else {
_increase_tree_rc(th->tree, func, file, line);
} else
ret = FALSE;
}
// KeReleaseSpinLock(&thnp->spin_lock, irql);
@ -726,13 +698,11 @@ static NTSTATUS STDCALL find_item_in_tree(device_extension* Vcb, tree* t, traver
oldtp.tree = t;
oldtp.item = td;
_increase_tree_rc(t, func, file, line);
while (_find_prev_item(Vcb, &oldtp, tp, TRUE, func, file, line)) {
if (!tp->item->ignore)
return STATUS_SUCCESS;
free_traverse_ptr(&oldtp);
oldtp = *tp;
}
@ -740,23 +710,18 @@ static NTSTATUS STDCALL find_item_in_tree(device_extension* Vcb, tree* t, traver
oldtp.tree = t;
oldtp.item = td;
_increase_tree_rc(t, func, file, line);
while (_find_next_item(Vcb, &oldtp, tp, TRUE, func, file, line)) {
if (!tp->item->ignore)
return STATUS_SUCCESS;
free_traverse_ptr(&oldtp);
oldtp = *tp;
}
return STATUS_INTERNAL_ERROR;
} else {
tp->tree = t;
_increase_tree_rc(t, func, file, line);
tp->item = td;
add_to_tree_cache(Vcb, t, FALSE);
}
return STATUS_SUCCESS;
@ -780,14 +745,8 @@ static NTSTATUS STDCALL find_item_in_tree(device_extension* Vcb, tree* t, traver
return Status;
}
if (loaded)
_increase_tree_rc(t, func, file, line);
Status = find_item_in_tree(Vcb, td->treeholder.tree, tp, searchkey, ignore, func, file, line);
td->treeholder.tree = _free_tree(td->treeholder.tree, func, file, line);
TRACE("tree now %p\n", td->treeholder.tree);
return Status;
}
}
@ -799,18 +758,18 @@ NTSTATUS STDCALL _find_item(device_extension* Vcb, root* r, traverse_ptr* tp, co
TRACE("(%p, %p, %p, %p)\n", Vcb, r, tp, searchkey);
Status = _do_load_tree(Vcb, &r->treeholder, r, NULL, NULL, &loaded, func, file, line);
if (!NT_SUCCESS(Status)) {
ERR("do_load_tree returned %08x\n", Status);
return Status;
if (!r->treeholder.tree) {
Status = _do_load_tree(Vcb, &r->treeholder, r, NULL, NULL, &loaded, func, file, line);
if (!NT_SUCCESS(Status)) {
ERR("do_load_tree returned %08x\n", Status);
return Status;
}
}
Status = find_item_in_tree(Vcb, r->treeholder.tree, tp, searchkey, ignore, func, file, line);
if (!NT_SUCCESS(Status)) {
ERR("find_item_in_tree returned %08x\n", Status);
}
_free_tree(r->treeholder.tree, func, file, line);
// #ifdef DEBUG_PARANOID
// if (b && !ignore && tp->item->ignore) {
@ -822,12 +781,6 @@ NTSTATUS STDCALL _find_item(device_extension* Vcb, root* r, traverse_ptr* tp, co
return Status;
}
void STDCALL _free_traverse_ptr(traverse_ptr* tp, const char* func, const char* file, unsigned int line) {
if (tp->tree) {
tp->tree = free_tree2(tp->tree, func, file, 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) {
tree* t;
tree_data *td, *next;
@ -843,7 +796,6 @@ BOOL STDCALL _find_next_item(device_extension* Vcb, const traverse_ptr* tp, trav
if (next) {
next_tp->tree = tp->tree;
_increase_tree_rc(next_tp->tree, func, file, line);
next_tp->item = next;
#ifdef DEBUG_PARANOID
@ -879,9 +831,6 @@ BOOL STDCALL _find_next_item(device_extension* Vcb, const traverse_ptr* tp, trav
return FALSE;
}
if (loaded)
_increase_tree_rc(t->parent, func, file, line);
t = td->treeholder.tree;
while (t->header.level != 0) {
@ -906,21 +855,16 @@ BOOL STDCALL _find_next_item(device_extension* Vcb, const traverse_ptr* tp, trav
BOOL b;
while ((b = _find_next_item(Vcb, next_tp, &ntp2, TRUE, func, file, line))) {
_free_traverse_ptr(next_tp, func, file, line);
*next_tp = ntp2;
if (!next_tp->item->ignore)
break;
}
if (!b) {
_free_traverse_ptr(next_tp, func, file, line);
if (!b)
return FALSE;
}
}
add_to_tree_cache(Vcb, t, FALSE);
#ifdef DEBUG_PARANOID
if (!ignore && next_tp->item->ignore) {
ERR("error - returning ignored item\n");
@ -949,7 +893,6 @@ BOOL STDCALL _find_prev_item(device_extension* Vcb, const traverse_ptr* tp, trav
// FIXME - support ignore flag
if (prev_item(tp->tree, tp->item)) {
prev_tp->tree = tp->tree;
_increase_tree_rc(prev_tp->tree, func, file, line);
prev_tp->item = prev_item(tp->tree, tp->item);
return TRUE;
@ -974,9 +917,6 @@ BOOL STDCALL _find_prev_item(device_extension* Vcb, const traverse_ptr* tp, trav
return FALSE;
}
if (loaded)
_increase_tree_rc(t->parent, func, file, line);
t = td->treeholder.tree;
while (t->header.level != 0) {
@ -993,8 +933,6 @@ BOOL STDCALL _find_prev_item(device_extension* Vcb, const traverse_ptr* tp, trav
t = li->treeholder.tree;
}
add_to_tree_cache(Vcb, t, FALSE);
prev_tp->tree = t;
prev_tp->item = last_item(t);
@ -1013,74 +951,54 @@ BOOL STDCALL _find_prev_item(device_extension* Vcb, const traverse_ptr* tp, trav
// ExReleaseResourceLite(&r->nonpaged->load_tree_lock);
// }
void STDCALL free_tree_cache(LIST_ENTRY* tc) {
void free_trees_root(device_extension* Vcb, root* r) {
LIST_ENTRY* le;
tree_cache* tc2;
root* r;
while (tc->Flink != tc) {
le = tc->Flink;
tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
r = tc2->tree->root;
UINT8 level;
for (level = 0; level <= 255; level++) {
BOOL empty = TRUE;
ExAcquireResourceExclusiveLite(&r->nonpaged->load_tree_lock, TRUE);
le = Vcb->trees.Flink;
while (le != tc) {
while (le != &Vcb->trees) {
LIST_ENTRY* nextle = le->Flink;
tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
tree* t = CONTAINING_RECORD(le, tree, list_entry);
if (tc2->tree->root == r) {
tree* nt;
BOOL top = !tc2->tree->paritem;
if (t->root == r && t->header.level == level) {
BOOL top = !t->paritem;
nt = free_tree2(tc2->tree, funcname, __FILE__, __LINE__);
if (top && !nt && r->treeholder.tree == tc2->tree)
empty = FALSE;
free_tree2(t, funcname, __FILE__, __LINE__);
if (top && r->treeholder.tree == t)
r->treeholder.tree = NULL;
RemoveEntryList(&tc2->list_entry);
ExFreePool(tc2);
if (IsListEmpty(&Vcb->trees))
return;
}
le = nextle;
}
ExReleaseResourceLite(&r->nonpaged->load_tree_lock);
if (empty)
break;
}
}
void STDCALL add_to_tree_cache(device_extension* Vcb, tree* t, BOOL write) {
LIST_ENTRY* le;
tree_cache* tc2;
le = Vcb->tree_cache.Flink;
while (le != &Vcb->tree_cache) {
tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
if (tc2->tree == t) {
if (write && !tc2->write) {
Vcb->write_trees++;
tc2->write = TRUE;
}
return;
}
le = le->Flink;
}
tc2 = ExAllocatePoolWithTag(PagedPool, sizeof(tree_cache), ALLOC_TAG);
if (!tc2) {
ERR("out of memory\n");
return;
}
TRACE("adding %p to tree cache\n", t);
tc2->tree = t;
tc2->write = write;
increase_tree_rc(t);
InsertTailList(&Vcb->tree_cache, &tc2->list_entry);
void STDCALL free_trees(device_extension* Vcb) {
tree* t;
root* r;
// print_trees(tc);
while (!IsListEmpty(&Vcb->trees)) {
t = CONTAINING_RECORD(Vcb->trees.Flink, tree, list_entry);
r = t->root;
ExAcquireResourceExclusiveLite(&r->nonpaged->load_tree_lock, TRUE);
free_trees_root(Vcb, r);
ExReleaseResourceLite(&r->nonpaged->load_tree_lock);
}
}
static void add_rollback(LIST_ENTRY* rollback, enum rollback_type type, void* ptr) {
@ -1152,7 +1070,6 @@ BOOL STDCALL insert_tree_item(device_extension* Vcb, root* r, UINT64 obj_id, UIN
if (cmp == 0 && !tp.item->ignore) { // FIXME - look for all items of the same key to make sure none are non-ignored
ERR("error: key (%llx,%x,%llx) already present\n", obj_id, obj_type, offset);
free_traverse_ptr(&tp);
goto end;
}
} else
@ -1161,7 +1078,6 @@ BOOL STDCALL insert_tree_item(device_extension* Vcb, root* r, UINT64 obj_id, UIN
td = ExAllocatePoolWithTag(PagedPool, sizeof(tree_data), ALLOC_TAG);
if (!td) {
ERR("out of memory\n");
free_traverse_ptr(&tp);
goto end;
}
@ -1205,11 +1121,12 @@ BOOL STDCALL insert_tree_item(device_extension* Vcb, root* r, UINT64 obj_id, UIN
// ERR("tree %p, num_items now %x\n", tp.tree, tp.tree->header.num_items);
// ERR("size now %x\n", tp.tree->size);
add_to_tree_cache(Vcb, tp.tree, TRUE);
if (!tp.tree->write) {
tp.tree->write = TRUE;
Vcb->write_trees++;
}
if (!ptp)
free_traverse_ptr(&tp);
else
if (ptp)
*ptp = tp;
t = tp.tree;
@ -1276,7 +1193,10 @@ void STDCALL delete_tree_item(device_extension* Vcb, traverse_ptr* tp, LIST_ENTR
tp->item->ignore = TRUE;
add_to_tree_cache(Vcb, tp->tree, TRUE);
if (!tp->tree->write) {
tp->tree->write = TRUE;
Vcb->write_trees++;
}
tp->tree->header.num_items--;

File diff suppressed because it is too large Load diff