Sync btrfs to 0.5.
This breaks BTRFS in ReactOS.

CORE-11674

svn path=/trunk/; revision=72023
This commit is contained in:
Pierre Schweitzer 2016-07-27 19:24:26 +00:00
parent 8c418c8bdb
commit 7695f39c11
21 changed files with 15007 additions and 9814 deletions

View file

@ -16,10 +16,12 @@ list(APPEND SOURCE
fsctl.c
pnp.c
read.c
registry.c
reparse.c
search.c
security.c
treefuncs.c
worker-thread.c
write.c
btrfs_drv.h)

File diff suppressed because it is too large Load diff

View file

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

View file

@ -73,6 +73,8 @@
#define READ_AHEAD_GRANULARITY 0x10000 // 64 KB
#define MAX_EXTENT_SIZE 0x8000000 // 128 MB
#ifdef _MSC_VER
#define try __try
#define except __except
@ -88,11 +90,12 @@
struct device_extension;
typedef struct {
PDEVICE_OBJECT devobj;
BTRFS_UUID fsuuid;
BTRFS_UUID devuuid;
UINT64 devnum;
UNICODE_STRING devpath;
UINT64 length;
UINT64 gen1, gen2;
BOOL processed;
LIST_ENTRY list_entry;
} volume;
@ -102,10 +105,42 @@ typedef struct _fcb_nonpaged {
SECTION_OBJECT_POINTERS segment_object;
ERESOURCE resource;
ERESOURCE paging_resource;
ERESOURCE index_lock;
} fcb_nonpaged;
struct _root;
typedef struct {
UINT64 offset;
EXTENT_DATA* data;
ULONG datalen;
BOOL unique;
BOOL ignore;
LIST_ENTRY list_entry;
} extent;
typedef struct {
UINT32 hash;
KEY key;
UINT8 type;
UINT64 index;
ANSI_STRING utf8;
UNICODE_STRING filepart_uc;
LIST_ENTRY list_entry;
} index_entry;
typedef struct {
UINT64 parent;
UINT64 index;
UNICODE_STRING name;
ANSI_STRING utf8;
LIST_ENTRY list_entry;
} hardlink;
struct _file_ref;
typedef struct _fcb {
FSRTL_ADVANCED_FCB_HEADER Header;
struct _fcb_nonpaged* nonpaged;
@ -123,33 +158,66 @@ typedef struct _fcb {
ULONG atts;
SHARE_ACCESS share_access;
WCHAR* debug_desc;
LIST_ENTRY extents;
UINT64 last_dir_index;
ANSI_STRING reparse_xattr;
LIST_ENTRY hardlinks;
struct _file_ref* fileref;
BOOL index_loaded;
LIST_ENTRY index_list;
BOOL dirty;
BOOL sd_dirty;
BOOL atts_changed, atts_deleted;
BOOL extents_changed;
BOOL reparse_xattr_changed;
BOOL created;
BOOL ads;
UINT32 adssize;
UINT32 adshash;
ANSI_STRING adsxattr;
ANSI_STRING adsdata;
LIST_ENTRY list_entry;
LIST_ENTRY list_entry_all;
} fcb;
struct _file_ref;
typedef struct {
fcb* fcb;
LIST_ENTRY list_entry;
} dirty_fcb;
typedef struct {
ERESOURCE children_lock;
} file_ref_nonpaged;
typedef struct _file_ref {
fcb* fcb;
UNICODE_STRING filepart;
UNICODE_STRING filepart_uc;
ANSI_STRING utf8;
UNICODE_STRING full_filename;
ULONG name_offset;
ANSI_STRING oldutf8;
UINT64 index;
BOOL delete_on_close;
BOOL deleted;
BOOL created;
file_ref_nonpaged* nonpaged;
LIST_ENTRY children;
LONG refcount;
struct _file_ref* parent;
WCHAR* debug_desc;
BOOL dirty;
LIST_ENTRY list_entry;
} file_ref;
typedef struct {
file_ref* fileref;
LIST_ENTRY list_entry;
} dirty_fileref;
typedef struct _ccb {
USHORT NodeType;
CSHORT NodeSize;
@ -162,6 +230,7 @@ typedef struct _ccb {
BOOL specific_file;
ACCESS_MASK access;
file_ref* fileref;
UNICODE_STRING filename;
} ccb;
// typedef struct _log_to_phys {
@ -259,47 +328,77 @@ typedef struct _root_cache {
struct _root_cache* next;
} root_cache;
#define SPACE_TYPE_FREE 0
#define SPACE_TYPE_USED 1
#define SPACE_TYPE_DELETING 2
#define SPACE_TYPE_WRITING 3
typedef struct {
UINT64 offset;
UINT64 size;
UINT8 type;
LIST_ENTRY list_entry;
} space;
typedef struct {
UINT64 address;
UINT64 size;
BOOL provisional;
LIST_ENTRY listentry;
} disk_hole;
LIST_ENTRY list_entry;
LIST_ENTRY list_entry_size;
} space;
typedef struct {
PDEVICE_OBJECT devobj;
DEV_ITEM devitem;
BOOL removable;
ULONG change_count;
LIST_ENTRY disk_holes;
UINT64 length;
LIST_ENTRY space;
} device;
typedef struct {
ERESOURCE lock;
ERESOURCE changed_extents_lock;
} chunk_nonpaged;
typedef struct {
CHUNK_ITEM* chunk_item;
UINT32 size;
UINT64 offset;
UINT64 used;
UINT32 oldused;
BOOL space_changed;
device** devices;
UINT64 cache_size;
UINT64 cache_inode;
fcb* cache;
LIST_ENTRY space;
LIST_ENTRY space_size;
LIST_ENTRY deleting;
LIST_ENTRY changed_extents;
chunk_nonpaged* nonpaged;
BOOL created;
LIST_ENTRY list_entry;
LIST_ENTRY list_entry_changed;
} chunk;
typedef struct {
UINT64 address;
UINT64 size;
UINT64 old_size;
UINT64 count;
UINT64 old_count;
BOOL no_csum;
LIST_ENTRY refs;
LIST_ENTRY old_refs;
LIST_ENTRY list_entry;
} changed_extent;
typedef struct {
EXTENT_DATA_REF edr;
LIST_ENTRY list_entry;
} changed_extent_ref;
typedef struct {
UINT64 address;
UINT64 size;
EXTENT_DATA_REF edr;
LIST_ENTRY list_entry;
} shared_data_entry;
typedef struct {
UINT64 address;
UINT64 parent;
LIST_ENTRY entries;
LIST_ENTRY list_entry;
} shared_data;
typedef struct {
KEY key;
void* data;
@ -307,14 +406,46 @@ typedef struct {
LIST_ENTRY list_entry;
} sys_chunk;
typedef struct {
PIRP Irp;
LIST_ENTRY list_entry;
} thread_job;
typedef struct {
PDEVICE_OBJECT DeviceObject;
HANDLE handle;
KEVENT event, finished;
BOOL quit;
LIST_ENTRY jobs;
KSPIN_LOCK spin_lock;
} drv_thread;
typedef struct {
ULONG num_threads;
LONG next_thread;
drv_thread* threads;
} drv_threads;
typedef struct {
BOOL ignore;
} mount_options;
#define VCB_TYPE_VOLUME 1
#define VCB_TYPE_PARTITION0 2
typedef struct _device_extension {
UINT32 type;
mount_options options;
PVPB Vpb;
device* devices;
UINT64 devices_loaded;
// DISK_GEOMETRY geometry;
UINT64 length;
superblock superblock;
// WCHAR label[MAX_LABEL_SIZE];
BOOL readonly;
BOOL removing;
BOOL locked;
PFILE_OBJECT locked_fileobj;
fcb* volume_fcb;
file_ref* root_fileref;
ERESOURCE DirResource;
@ -324,12 +455,12 @@ typedef struct _device_extension {
ERESOURCE tree_lock;
PNOTIFY_SYNC NotifySync;
LIST_ENTRY DirNotifyList;
LONG tree_lock_counter;
LONG open_trees;
ULONG write_trees;
BOOL need_write;
// ERESOURCE LogToPhysLock;
// UINT64 chunk_root_phys_addr;
UINT64 root_tree_phys_addr;
UINT64 data_flags;
// log_to_phys* log_to_phys;
LIST_ENTRY roots;
LIST_ENTRY drop_roots;
@ -343,11 +474,33 @@ typedef struct _device_extension {
UINT32 max_inline;
LIST_ENTRY sys_chunks;
LIST_ENTRY chunks;
LIST_ENTRY chunks_changed;
LIST_ENTRY trees;
LIST_ENTRY all_fcbs;
LIST_ENTRY dirty_fcbs;
KSPIN_LOCK dirty_fcbs_lock;
LIST_ENTRY dirty_filerefs;
KSPIN_LOCK dirty_filerefs_lock;
ERESOURCE checksum_lock;
ERESOURCE chunk_lock;
LIST_ENTRY sector_checksums;
LIST_ENTRY shared_extents;
KSPIN_LOCK shared_extents_lock;
HANDLE flush_thread_handle;
KTIMER flush_thread_timer;
KEVENT flush_thread_finished;
drv_threads threads;
PFILE_OBJECT root_file;
LIST_ENTRY list_entry;
} device_extension;
typedef struct {
UINT32 type;
PDEVICE_OBJECT devobj;
BTRFS_UUID uuid;
UNICODE_STRING name;
} part0_device_extension;
typedef struct {
LIST_ENTRY listentry;
PSID sid;
@ -366,39 +519,37 @@ typedef struct {
BOOL deleted;
} changed_sector;
enum write_tree_status {
WriteTreeStatus_Pending,
WriteTreeStatus_Success,
WriteTreeStatus_Error,
WriteTreeStatus_Cancelling,
WriteTreeStatus_Cancelled
enum write_data_status {
WriteDataStatus_Pending,
WriteDataStatus_Success,
WriteDataStatus_Error,
WriteDataStatus_Cancelling,
WriteDataStatus_Cancelled,
WriteDataStatus_Ignore
};
struct write_tree_context;
struct write_data_context;
typedef struct {
struct write_tree_context* context;
struct write_data_context* context;
UINT8* buf;
BOOL need_free;
device* device;
PIRP Irp;
IO_STATUS_BLOCK iosb;
enum write_tree_status status;
enum write_data_status status;
LIST_ENTRY list_entry;
} write_tree_stripe;
} write_data_stripe;
typedef struct {
KEVENT Event;
LIST_ENTRY stripes;
} write_tree_context;
LONG stripes_left;
BOOL tree;
} write_data_context;
// #pragma pack(pop)
static __inline void init_tree_holder(tree_holder* th) {
// th->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(tree_holder_nonpaged), ALLOC_TAG);
// KeInitializeSpinLock(&th->nonpaged->spin_lock);
// ExInitializeResourceLite(&th->nonpaged->lock); // FIXME - delete this later
}
static __inline void* map_user_buffer(PIRP Irp) {
if (!Irp->MdlAddress) {
return Irp->UserBuffer;
@ -439,22 +590,28 @@ static __inline void insert_into_ordered_list(LIST_ENTRY* list, ordered_list* in
InsertTailList(list, &ins->list_entry);
}
static __inline void get_raid0_offset(UINT64 off, UINT64 stripe_length, UINT16 num_stripes, UINT64* stripeoff, UINT16* stripe) {
UINT64 initoff, startoff;
startoff = off % (num_stripes * stripe_length);
initoff = (off / (num_stripes * stripe_length)) * stripe_length;
*stripe = startoff / stripe_length;
*stripeoff = initoff + startoff - (*stripe * stripe_length);
}
// in btrfs.c
device* find_device_from_uuid(device_extension* Vcb, BTRFS_UUID* uuid);
ULONG sector_align( ULONG NumberToBeAligned, ULONG Alignment );
UINT64 sector_align( UINT64 NumberToBeAligned, UINT64 Alignment );
int keycmp(const KEY* key1, const KEY* key2);
ULONG STDCALL get_file_attributes(device_extension* Vcb, INODE_ITEM* ii, root* r, UINT64 inode, UINT8 type, BOOL dotfile, BOOL ignore_xa);
BOOL STDCALL get_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, UINT8** data, UINT16* datalen);
NTSTATUS STDCALL set_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, UINT8* data, UINT16 datalen, LIST_ENTRY* rollback);
BOOL STDCALL delete_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, LIST_ENTRY* rollback);
void _free_fcb(fcb* fcb, const char* func, const char* file, unsigned int line);
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_fileref(file_ref* fileref, PFILE_OBJECT FileObject, LIST_ENTRY* rollback);
NTSTATUS delete_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, LIST_ENTRY* rollback);
fcb* create_fcb();
file_ref* create_fileref();
void protect_superblocks(device_extension* Vcb, chunk* c);
@ -465,8 +622,14 @@ NTSTATUS STDCALL dev_ioctl(PDEVICE_OBJECT DeviceObject, ULONG ControlCode, PVOID
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);
void send_notification_fcb(file_ref* fileref, ULONG filter_match, ULONG action);
WCHAR* file_desc(PFILE_OBJECT FileObject);
WCHAR* file_desc_fileref(file_ref* fileref);
BOOL add_thread_job(device_extension* Vcb, PIRP Irp);
NTSTATUS part0_passthrough(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
void mark_fcb_dirty(fcb* fcb);
void mark_fileref_dirty(file_ref* fileref);
NTSTATUS delete_fileref(file_ref* fileref, PFILE_OBJECT FileObject, LIST_ENTRY* rollback);
#ifdef _MSC_VER
#define funcname __FUNCTION__
@ -533,6 +696,13 @@ void STDCALL init_fast_io_dispatch(FAST_IO_DISPATCH** fiod);
// in crc32c.c
UINT32 STDCALL calc_crc32c(UINT32 seed, UINT8* msg, ULONG msglen);
enum rollback_type {
ROLLBACK_INSERT_ITEM,
ROLLBACK_DELETE_ITEM,
ROLLBACK_INSERT_EXTENT,
ROLLBACK_DELETE_EXTENT
};
// in treefuncs.c
NTSTATUS STDCALL _find_item(device_extension* Vcb, root* r, traverse_ptr* tp, const KEY* searchkey, BOOL ignore, const char* func, const char* file, unsigned int line);
BOOL STDCALL _find_next_item(device_extension* Vcb, const traverse_ptr* tp, traverse_ptr* next_tp, BOOL ignore, const char* func, const char* file, unsigned int line);
@ -541,22 +711,22 @@ 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);
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 _load_tree(device_extension* Vcb, UINT64 addr, root* r, tree** pt, tree* parent, 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);
void add_rollback(LIST_ENTRY* rollback, enum rollback_type type, void* ptr);
#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 load_tree(t, addr, r, pt, parent) _load_tree(t, addr, r, pt, parent, funcname, __FILE__, __LINE__)
#define do_load_tree(Vcb, th, r, t, td, loaded) _do_load_tree(Vcb, th, r, t, td, loaded, funcname, __FILE__, __LINE__)
// in search.c
void STDCALL look_for_vols(LIST_ENTRY* volumes);
void STDCALL look_for_vols(PDRIVER_OBJECT DriverObject, LIST_ENTRY* volumes);
// in cache.c
NTSTATUS STDCALL init_cache();
@ -565,28 +735,32 @@ extern CACHE_MANAGER_CALLBACKS* cache_callbacks;
// in write.c
NTSTATUS STDCALL do_write(device_extension* Vcb, LIST_ENTRY* rollback);
NTSTATUS write_file(PDEVICE_OBJECT DeviceObject, PIRP Irp);
NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void* buf, ULONG* length, BOOL paging_io, BOOL no_cache, LIST_ENTRY* rollback);
NTSTATUS write_file(device_extension* Vcb, PIRP Irp, BOOL wait, BOOL deferred_write);
NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void* buf, ULONG* length, BOOL paging_io, BOOL no_cache,
BOOL wait, BOOL deferred_write, LIST_ENTRY* rollback);
NTSTATUS truncate_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 extend_file(fcb* fcb, file_ref* fileref, UINT64 end, BOOL prealloc, PIRP Irp, LIST_ENTRY* rollback);
NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, LIST_ENTRY* rollback);
void commit_checksum_changes(device_extension* Vcb, LIST_ENTRY* changed_sector_list);
NTSTATUS insert_sparse_extent(fcb* fcb, UINT64 start, UINT64 length, LIST_ENTRY* rollback);
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 STDCALL write_data(device_extension* Vcb, UINT64 address, void* data, BOOL need_free, UINT32 length, write_data_context* wtc, PIRP Irp);
NTSTATUS STDCALL write_data_complete(device_extension* Vcb, UINT64 address, void* data, UINT32 length, PIRP Irp);
void free_write_data_stripes(write_data_context* wtc);
NTSTATUS get_tree_new_address(device_extension* Vcb, tree* t, LIST_ENTRY* rollback);
NTSTATUS STDCALL drv_write(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
void flush_fcb(fcb* fcb, BOOL cache, LIST_ENTRY* rollback);
BOOL insert_extent_chunk(device_extension* Vcb, fcb* fcb, chunk* c, UINT64 start_data, UINT64 length, BOOL prealloc, void* data, LIST_ENTRY* changed_sector_list,
PIRP Irp, LIST_ENTRY* rollback);
NTSTATUS do_nocow_write(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback);
NTSTATUS insert_extent(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 length, void* data, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback);
void remove_fcb_extent(extent* ext, LIST_ENTRY* rollback);
NTSTATUS update_changed_extent_ref(device_extension* Vcb, chunk* c, UINT64 address, UINT64 size, UINT64 root, UINT64 objid, UINT64 offset,
signed long long count, BOOL no_csum, UINT64 new_size);
// 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);
@ -595,16 +769,16 @@ void fcb_get_sd(fcb* fcb, struct _fcb* parent);
// UINT32 STDCALL get_uid();
void add_user_mapping(WCHAR* sidstring, ULONG sidstringlength, UINT32 uid);
UINT32 sid_to_uid(PSID sid);
NTSTATUS fcb_get_new_sd(fcb* fcb, file_ref* fileref, ACCESS_STATE* as);
NTSTATUS fcb_get_new_sd(fcb* fcb, file_ref* parfileref, 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);
NTSTATUS fileref_get_filename(file_ref* fileref, PUNICODE_STRING fn, USHORT* name_offset);
NTSTATUS open_fileref_by_inode(device_extension* Vcb, root* subvol, UINT64 inode, file_ref** pfr);
// in reparse.c
NTSTATUS get_reparse_point(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, void* buffer, DWORD buflen, DWORD* retlen);
@ -613,20 +787,28 @@ NTSTATUS delete_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp);
// in create.c
NTSTATUS STDCALL drv_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
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 STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STRING filename, UINT32 crc32, file_ref* fr, root** subvol,
UINT64* inode, UINT8* type, UINT64* index, PANSI_STRING utf8);
NTSTATUS STDCALL find_file_in_dir(device_extension* Vcb, PUNICODE_STRING filename, file_ref* fr,
root** subvol, UINT64* inode, UINT8* type, UINT64* index, PANSI_STRING utf8);
NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnus, file_ref* related, BOOL parent, USHORT* unparsed);
NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, PANSI_STRING utf8, fcb* parent, fcb** pfcb);
NTSTATUS open_fcb_stream(device_extension* Vcb, root* subvol, UINT64 inode, ANSI_STRING* xattr, UINT32 streamhash, fcb* parent, fcb** pfcb);
void insert_fileref_child(file_ref* parent, file_ref* child, BOOL do_lock);
NTSTATUS fcb_get_last_dir_index(fcb* fcb, UINT64* index);
// in fsctl.c
NTSTATUS fsctl_request(PDEVICE_OBJECT DeviceObject, PIRP Irp, UINT32 type, BOOL user);
void do_unlock_volume(device_extension* Vcb);
// in flushthread.c
void STDCALL flush_thread(void* context);
// in read.c
NTSTATUS STDCALL drv_read(PDEVICE_OBJECT DeviceObject, PIRP Irp);
NTSTATUS STDCALL read_file(device_extension* Vcb, root* subvol, UINT64 inode, UINT8* data, UINT64 start, UINT64 length, ULONG* pbr);
NTSTATUS STDCALL read_data(device_extension* Vcb, UINT64 addr, UINT32 length, UINT32* csum, BOOL is_tree, UINT8* buf, chunk** pc, PIRP Irp);
NTSTATUS STDCALL read_file(fcb* fcb, UINT8* data, UINT64 start, UINT64 length, ULONG* pbr, PIRP Irp);
NTSTATUS do_read(PIRP Irp, BOOL wait, ULONG* bytes_read);
// in pnp.c
NTSTATUS STDCALL drv_pnp(PDEVICE_OBJECT DeviceObject, PIRP Irp);
@ -636,13 +818,36 @@ 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);
NTSTATUS add_space_entry(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 offset, UINT64 size);
void _space_list_add(device_extension* Vcb, chunk* c, BOOL deleting, UINT64 address, UINT64 length, LIST_ENTRY* rollback, const char* func);
void _space_list_add2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, UINT64 length, LIST_ENTRY* rollback, const char* func);
void _space_list_subtract(device_extension* Vcb, chunk* c, BOOL deleting, UINT64 address, UINT64 length, LIST_ENTRY* rollback, const char* func);
void _space_list_subtract2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, UINT64 length, LIST_ENTRY* rollback, const char* func);
#define space_list_add(Vcb, c, deleting, address, length, rollback) _space_list_add(Vcb, c, deleting, address, length, rollback, funcname)
#define space_list_add2(list, list_size, address, length, rollback) _space_list_add2(list, list_size, address, length, rollback, funcname)
#define space_list_subtract(Vcb, c, deleting, address, length, rollback) _space_list_subtract(Vcb, c, deleting, address, length, rollback, funcname)
#define space_list_subtract2(list, list_size, address, length, rollback) _space_list_subtract2(list, list_size, address, length, rollback, funcname)
// 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);
NTSTATUS increase_extent_refcount_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 inode, UINT64 offset, UINT32 refcount, LIST_ENTRY* rollback);
NTSTATUS decrease_extent_refcount_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 inode, UINT64 offset, UINT32 refcount, LIST_ENTRY* rollback);
NTSTATUS decrease_extent_refcount_shared_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 treeaddr, UINT64 parent, LIST_ENTRY* rollback);
NTSTATUS decrease_extent_refcount_old(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 treeaddr, 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);
UINT64 find_extent_data_refcount(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 objid, UINT64 offset);
// in worker-thread.c
void STDCALL worker_thread(void* context);
void do_read_job(PIRP Irp);
void do_write_job(device_extension* Vcb, PIRP Irp);
// in registry.c
void STDCALL read_registry(PUNICODE_STRING regpath);
NTSTATUS registry_mark_volume_mounted(BTRFS_UUID* uuid);
NTSTATUS registry_mark_volume_unmounted(BTRFS_UUID* uuid);
NTSTATUS registry_load_volume_options(BTRFS_UUID* uuid, mount_options* options);
#define fast_io_possible(fcb) (!FsRtlAreThereCurrentFileLocks(&fcb->lock) && !fcb->Vcb->readonly ? FastIoIsPossible : FastIoIsQuestionable)
@ -669,6 +874,22 @@ static __inline void InsertAfter(LIST_ENTRY* head, LIST_ENTRY* item, LIST_ENTRY*
head->Blink = item;
}
#ifdef DEBUG_FCB_REFCOUNTS
#ifdef DEBUG_LONG_MESSAGES
#define increase_fileref_refcount(fileref) {\
LONG rc = InterlockedIncrement(&fileref->refcount);\
MSG(funcname, __FILE__, __LINE__, "fileref %p: refcount now %i\n", 1, fileref, rc);\
}
#else
#define increase_fileref_refcount(fileref) {\
LONG rc = InterlockedIncrement(&fileref->refcount);\
MSG(funcname, "fileref %p: refcount now %i\n", 1, fileref, rc);\
}
#endif
#else
#define increase_fileref_refcount(fileref) InterlockedIncrement(&fileref->refcount)
#endif
#ifdef _MSC_VER
// #define int3 __asm { int 3 }
#define int3 __debugbreak()
@ -676,34 +897,11 @@ static __inline void InsertAfter(LIST_ENTRY* head, LIST_ENTRY* item, LIST_ENTRY*
#define int3 asm("int3;")
#endif
#define acquire_tree_lock(Vcb, exclusive) {\
LONG ref = InterlockedIncrement(&Vcb->tree_lock_counter); \
ref = ref; \
if (exclusive) { \
TRACE("getting tree_lock (exclusive) %u->%u\n", ref-1, ref); \
ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE); \
TRACE("open tree count = %i\n", Vcb->open_trees); \
} else { \
TRACE("getting tree_lock %u->%u\n", ref-1, ref); \
ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE); \
} \
}
// if (Vcb->open_trees > 0) { ERR("open tree count = %i\n", Vcb->open_trees); print_open_trees(Vcb); int3; }
// else TRACE("open tree count = %i\n", Vcb->open_trees);
// FIXME - find a way to catch unfreed trees again
#define release_tree_lock(Vcb, exclusive) {\
LONG ref = InterlockedDecrement(&Vcb->tree_lock_counter); \
ref = ref; \
TRACE("releasing tree_lock %u->%u\n", ref+1, ref); \
if (exclusive) {\
TRACE("open tree count = %i\n", Vcb->open_trees); \
} \
ExReleaseResourceLite(&Vcb->tree_lock); \
}
// from sys/stat.h
#define __S_IFMT 0170000 /* These bits determine file type. */
#define __S_IFDIR 0040000 /* Directory. */
@ -773,6 +971,7 @@ 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) */
ULONG NTAPI KeQueryActiveProcessorCount(PKAFFINITY ActiveProcessors);
#endif /* defined(__REACTOS__) && (NTDDI_VERSION < NTDDI_VISTA) */
#endif

File diff suppressed because it is too large Load diff

View file

@ -25,14 +25,16 @@ enum DirEntryType {
typedef struct {
KEY key;
BOOL name_alloc;
char* name;
ULONG namelen;
UINT8 type;
enum DirEntryType dir_entry_type;
} dir_entry;
ULONG STDCALL get_reparse_tag(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type) {
ULONG att, tag, br;
static ULONG STDCALL get_reparse_tag(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, ULONG atts) {
fcb* fcb;
ULONG tag = 0, br;
NTSTATUS Status;
// FIXME - will this slow things down?
@ -43,43 +45,41 @@ ULONG STDCALL get_reparse_tag(device_extension* Vcb, root* subvol, UINT64 inode,
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))
if (!(atts & FILE_ATTRIBUTE_REPARSE_POINT))
return 0;
ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
Status = open_fcb(Vcb, subvol, inode, type, NULL, NULL, &fcb);
if (!NT_SUCCESS(Status)) {
ERR("open_fcb returned %08x\n", Status);
ExReleaseResourceLite(&Vcb->fcb_lock);
return 0;
}
ExReleaseResourceLite(&Vcb->fcb_lock);
ExAcquireResourceSharedLite(fcb->Header.Resource, TRUE);
if (type == BTRFS_TYPE_DIRECTORY) {
UINT8* data;
UINT16 datalen;
if (!fcb->reparse_xattr.Buffer || fcb->reparse_xattr.Length < sizeof(ULONG))
goto end;
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);
RtlCopyMemory(&tag, fcb->reparse_xattr.Buffer, sizeof(ULONG));
} 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);
Status = read_file(fcb, (UINT8*)&tag, 0, sizeof(ULONG), &br, NULL);
if (!NT_SUCCESS(Status)) {
ERR("read_file returned %08x\n", Status);
return 0;
goto end;
}
if (br < sizeof(ULONG))
return 0;
goto end;
}
end:
ExReleaseResourceLite(fcb->Header.Resource);
free_fcb(fcb);
return tag;
}
@ -90,7 +90,7 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L
INODE_ITEM ii;
NTSTATUS Status;
ULONG stringlen;
BOOL dotfile;
ULONG atts;
IrpSp = IoGetCurrentIrpStackLocation(Irp);
@ -128,24 +128,23 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L
LIST_ENTRY* le;
BOOL found = FALSE;
if (fileref) {
ExAcquireResourceSharedLite(&fcb->Vcb->fcb_lock, TRUE);
le = fileref->children.Flink;
while (le != &fileref->children) {
file_ref* c = CONTAINING_RECORD(le, file_ref, list_entry);
ExAcquireResourceSharedLite(&fcb->Vcb->fcb_lock, TRUE);
if (!IsListEmpty(&r->fcbs)) {
le = r->fcbs.Flink;
while (le != &r->fcbs) {
struct _fcb* fcb2 = CONTAINING_RECORD(le, struct _fcb, list_entry);
if (c->fcb->subvol == r && c->fcb->inode == inode && !c->fcb->ads) {
ii = c->fcb->inode_item;
if (fcb2->inode == inode && !fcb2->ads) {
ii = fcb2->inode_item;
atts = fcb2->atts;
found = TRUE;
break;
}
le = le->Flink;
}
ExReleaseResourceLite(&fcb->Vcb->fcb_lock);
}
ExReleaseResourceLite(&fcb->Vcb->fcb_lock);
if (!found) {
KEY searchkey;
@ -170,6 +169,16 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L
if (tp.item->size > 0)
RtlCopyMemory(&ii, tp.item->data, min(sizeof(INODE_ITEM), tp.item->size));
if (IrpSp->Parameters.QueryDirectory.FileInformationClass == FileBothDirectoryInformation ||
IrpSp->Parameters.QueryDirectory.FileInformationClass == FileDirectoryInformation ||
IrpSp->Parameters.QueryDirectory.FileInformationClass == FileFullDirectoryInformation ||
IrpSp->Parameters.QueryDirectory.FileInformationClass == FileIdBothDirectoryInformation) {
BOOL dotfile = de->namelen > 1 && de->name[0] == '.';
atts = get_file_attributes(fcb->Vcb, &ii, r, inode, de->type, dotfile, FALSE);
}
}
break;
@ -179,6 +188,7 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L
ii = fcb->inode_item;
r = fcb->subvol;
inode = fcb->inode;
atts = fcb->atts;
break;
case DirEntryType_Parent:
@ -186,6 +196,7 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L
ii = fileref->parent->fcb->inode_item;
r = fileref->parent->fcb->subvol;
inode = fileref->parent->fcb->inode;
atts = fileref->parent->fcb->atts;
} else {
ERR("no fileref\n");
return STATUS_INTERNAL_ERROR;
@ -208,8 +219,6 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L
}
}
dotfile = de->name[0] == '.' && (de->name[1] != '.' || de->name[2] != 0) && (de->name[1] != 0);
switch (IrpSp->Parameters.QueryDirectory.FileInformationClass) {
case FileBothDirectoryInformation:
{
@ -220,7 +229,7 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L
needed = sizeof(FILE_BOTH_DIR_INFORMATION) - sizeof(WCHAR) + stringlen;
if (needed > *len) {
WARN("buffer overflow - %u > %u\n", needed, *len);
TRACE("buffer overflow - %u > %u\n", needed, *len);
return STATUS_BUFFER_OVERFLOW;
}
@ -232,9 +241,9 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L
fbdi->ChangeTime.QuadPart = 0;
fbdi->EndOfFile.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_size;
fbdi->AllocationSize.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_blocks;
fbdi->FileAttributes = get_file_attributes(fcb->Vcb, &ii, r, inode, de->type, dotfile, FALSE);
fbdi->FileAttributes = atts;
fbdi->FileNameLength = stringlen;
fbdi->EaSize = get_reparse_tag(fcb->Vcb, r, inode, de->type);
fbdi->EaSize = get_reparse_tag(fcb->Vcb, r, inode, de->type, atts);
fbdi->ShortNameLength = 0;
// fibdi->ShortName[12];
@ -259,7 +268,7 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L
needed = sizeof(FILE_DIRECTORY_INFORMATION) - sizeof(WCHAR) + stringlen;
if (needed > *len) {
WARN("buffer overflow - %u > %u\n", needed, *len);
TRACE("buffer overflow - %u > %u\n", needed, *len);
return STATUS_BUFFER_OVERFLOW;
}
@ -271,7 +280,7 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L
fdi->ChangeTime.QuadPart = 0;
fdi->EndOfFile.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_size;
fdi->AllocationSize.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_blocks;
fdi->FileAttributes = get_file_attributes(fcb->Vcb, &ii, r, inode, de->type, dotfile, FALSE);
fdi->FileAttributes = atts;
fdi->FileNameLength = stringlen;
Status = RtlUTF8ToUnicodeN(fdi->FileName, stringlen, &stringlen, de->name, de->namelen);
@ -295,7 +304,7 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L
needed = sizeof(FILE_FULL_DIR_INFORMATION) - sizeof(WCHAR) + stringlen;
if (needed > *len) {
WARN("buffer overflow - %u > %u\n", needed, *len);
TRACE("buffer overflow - %u > %u\n", needed, *len);
return STATUS_BUFFER_OVERFLOW;
}
@ -307,9 +316,9 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L
ffdi->ChangeTime.QuadPart = 0;
ffdi->EndOfFile.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_size;
ffdi->AllocationSize.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_blocks;
ffdi->FileAttributes = get_file_attributes(fcb->Vcb, &ii, r, inode, de->type, dotfile, FALSE);
ffdi->FileAttributes = atts;
ffdi->FileNameLength = stringlen;
ffdi->EaSize = get_reparse_tag(fcb->Vcb, r, inode, de->type);
ffdi->EaSize = get_reparse_tag(fcb->Vcb, r, inode, de->type, atts);
Status = RtlUTF8ToUnicodeN(ffdi->FileName, stringlen, &stringlen, de->name, de->namelen);
@ -332,7 +341,7 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L
needed = sizeof(FILE_ID_BOTH_DIR_INFORMATION) - sizeof(WCHAR) + stringlen;
if (needed > *len) {
WARN("buffer overflow - %u > %u\n", needed, *len);
TRACE("buffer overflow - %u > %u\n", needed, *len);
return STATUS_BUFFER_OVERFLOW;
}
@ -347,9 +356,9 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L
fibdi->ChangeTime.QuadPart = 0;
fibdi->EndOfFile.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_size;
fibdi->AllocationSize.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_blocks;
fibdi->FileAttributes = get_file_attributes(fcb->Vcb, &ii, r, inode, de->type, dotfile, FALSE);
fibdi->FileAttributes = atts;
fibdi->FileNameLength = stringlen;
fibdi->EaSize = get_reparse_tag(fcb->Vcb, r, inode, de->type);
fibdi->EaSize = get_reparse_tag(fcb->Vcb, r, inode, de->type, atts);
fibdi->ShortNameLength = 0;
// fibdi->ShortName[12];
fibdi->FileId.QuadPart = inode;
@ -379,7 +388,7 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L
needed = sizeof(FILE_NAMES_INFORMATION) - sizeof(WCHAR) + stringlen;
if (needed > *len) {
WARN("buffer overflow - %u > %u\n", needed, *len);
TRACE("buffer overflow - %u > %u\n", needed, *len);
return STATUS_BUFFER_OVERFLOW;
}
@ -419,19 +428,23 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L
return STATUS_NO_MORE_FILES;
}
static NTSTATUS STDCALL next_dir_entry(fcb* fcb, file_ref* fileref, UINT64* offset, dir_entry* de, traverse_ptr* tp) {
static NTSTATUS STDCALL next_dir_entry(file_ref* fileref, UINT64* offset, dir_entry* de) {
KEY searchkey;
traverse_ptr next_tp;
traverse_ptr tp, next_tp;
DIR_ITEM* di;
NTSTATUS Status;
file_ref* fr;
LIST_ENTRY* le;
char* name;
if (fileref && fileref->parent) { // don't return . and .. if root directory
if (fileref->parent) { // don't return . and .. if root directory
if (*offset == 0) {
de->key.obj_id = fcb->inode;
de->key.obj_id = fileref->fcb->inode;
de->key.obj_type = TYPE_INODE_ITEM;
de->key.offset = 0;
de->dir_entry_type = DirEntryType_Self;
de->name = ".";
de->name_alloc = FALSE;
de->namelen = 1;
de->type = BTRFS_TYPE_DIRECTORY;
@ -444,6 +457,7 @@ static NTSTATUS STDCALL next_dir_entry(fcb* fcb, file_ref* fileref, UINT64* offs
de->key.offset = 0;
de->dir_entry_type = DirEntryType_Parent;
de->name = "..";
de->name_alloc = FALSE;
de->namelen = 2;
de->type = BTRFS_TYPE_DIRECTORY;
@ -453,77 +467,171 @@ static NTSTATUS STDCALL next_dir_entry(fcb* fcb, file_ref* fileref, UINT64* offs
}
}
if (!tp->tree) {
searchkey.obj_id = fcb->inode;
searchkey.obj_type = TYPE_DIR_INDEX;
searchkey.offset = *offset;
if (*offset < 2)
*offset = 2;
ExAcquireResourceSharedLite(&fileref->nonpaged->children_lock, TRUE);
fr = NULL;
le = fileref->children.Flink;
// skip entries before offset
while (le != &fileref->children) {
file_ref* fr2 = CONTAINING_RECORD(le, file_ref, list_entry);
Status = find_item(fcb->Vcb, fcb->subvol, tp, &searchkey, FALSE);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
tp->tree = NULL;
return Status;
if (fr2->index >= *offset) {
fr = fr2;
break;
}
TRACE("found item %llx,%x,%llx\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset);
if (keycmp(&tp->item->key, &searchkey) == -1) {
if (find_next_item(fcb->Vcb, tp, &next_tp, FALSE)) {
*tp = next_tp;
le = le->Flink;
}
do {
if (fr && fr->index == *offset) {
if (!fr->deleted) {
if (fr->fcb->subvol == fileref->fcb->subvol) {
de->key.obj_id = fr->fcb->inode;
de->key.obj_type = TYPE_INODE_ITEM;
de->key.offset = 0;
} else {
de->key.obj_id = fr->fcb->subvol->id;
de->key.obj_type = TYPE_ROOT_ITEM;
de->key.offset = 0;
}
TRACE("moving on to %llx,%x,%llx\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset);
name = fr->utf8.Buffer;
de->namelen = fr->utf8.Length;
de->type = fr->fcb->type;
de->dir_entry_type = DirEntryType_File;
(*offset)++;
Status = STATUS_SUCCESS;
goto end;
} else {
(*offset)++;
fr = fr->list_entry.Flink == &fileref->children ? NULL : CONTAINING_RECORD(fr->list_entry.Flink, file_ref, list_entry);
continue;
}
}
if (tp->item->key.obj_id != searchkey.obj_id || tp->item->key.obj_type != searchkey.obj_type || tp->item->key.offset < *offset) {
tp->tree = NULL;
return STATUS_NO_MORE_FILES;
searchkey.obj_id = fileref->fcb->inode;
searchkey.obj_type = TYPE_DIR_INDEX;
searchkey.offset = *offset;
Status = find_item(fileref->fcb->Vcb, fileref->fcb->subvol, &tp, &searchkey, FALSE);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
goto end;
}
*offset = tp->item->key.offset + 1;
di = (DIR_ITEM*)tp->item->data;
if (tp->item->size < sizeof(DIR_ITEM) || tp->item->size < sizeof(DIR_ITEM) - 1 + di->m + di->n) {
ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset, tp->item->size, sizeof(DIR_ITEM));
tp->tree = NULL;
return STATUS_INTERNAL_ERROR;
if (keycmp(&tp.item->key, &searchkey) == -1) {
if (find_next_item(fileref->fcb->Vcb, &tp, &next_tp, FALSE))
tp = next_tp;
}
de->key = di->key;
de->name = di->name;
de->namelen = di->n;
de->type = di->type;
de->dir_entry_type = DirEntryType_File;
return STATUS_SUCCESS;
} else {
if (find_next_item(fcb->Vcb, tp, &next_tp, FALSE)) {
if (next_tp.item->key.obj_type == TYPE_DIR_INDEX && next_tp.item->key.obj_id == tp->item->key.obj_id) {
*tp = next_tp;
*offset = tp->item->key.offset + 1;
di = (DIR_ITEM*)tp->item->data;
if (tp->item->size < sizeof(DIR_ITEM) || tp->item->size < sizeof(DIR_ITEM) - 1 + di->m + di->n) {
ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset, tp->item->size, sizeof(DIR_ITEM));
return STATUS_INTERNAL_ERROR;
if (keycmp(&tp.item->key, &searchkey) != -1 && tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
do {
if (fr) {
if (fr->index <= tp.item->key.offset && !fr->deleted) {
if (fr->fcb->subvol == fileref->fcb->subvol) {
de->key.obj_id = fr->fcb->inode;
de->key.obj_type = TYPE_INODE_ITEM;
de->key.offset = 0;
} else {
de->key.obj_id = fr->fcb->subvol->id;
de->key.obj_type = TYPE_ROOT_ITEM;
de->key.offset = 0;
}
name = fr->utf8.Buffer;
de->namelen = fr->utf8.Length;
de->type = fr->fcb->type;
de->dir_entry_type = DirEntryType_File;
*offset = fr->index + 1;
Status = STATUS_SUCCESS;
goto end;
}
if (fr->index == tp.item->key.offset && fr->deleted)
break;
fr = fr->list_entry.Flink == &fileref->children ? NULL : CONTAINING_RECORD(fr->list_entry.Flink, file_ref, list_entry);
}
de->key = di->key;
de->name = di->name;
de->namelen = di->n;
de->type = di->type;
} while (fr && fr->index < tp.item->key.offset);
if (fr && fr->index == tp.item->key.offset && fr->deleted) {
*offset = fr->index + 1;
fr = fr->list_entry.Flink == &fileref->children ? NULL : CONTAINING_RECORD(fr->list_entry.Flink, file_ref, list_entry);
continue;
}
*offset = tp.item->key.offset + 1;
di = (DIR_ITEM*)tp.item->data;
if (tp.item->size < sizeof(DIR_ITEM) || tp.item->size < sizeof(DIR_ITEM) - 1 + di->m + di->n) {
ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM));
Status = STATUS_INTERNAL_ERROR;
goto end;
}
de->key = di->key;
name = di->name;
de->namelen = di->n;
de->type = di->type;
de->dir_entry_type = DirEntryType_File;
Status = STATUS_SUCCESS;
goto end;
} else {
if (fr) {
if (fr->fcb->subvol == fileref->fcb->subvol) {
de->key.obj_id = fr->fcb->inode;
de->key.obj_type = TYPE_INODE_ITEM;
de->key.offset = 0;
} else {
de->key.obj_id = fr->fcb->subvol->id;
de->key.obj_type = TYPE_ROOT_ITEM;
de->key.offset = 0;
}
name = fr->utf8.Buffer;
de->namelen = fr->utf8.Length;
de->type = fr->fcb->type;
de->dir_entry_type = DirEntryType_File;
return STATUS_SUCCESS;
} else
return STATUS_NO_MORE_FILES;
} else
return STATUS_NO_MORE_FILES;
}
*offset = fr->index + 1;
Status = STATUS_SUCCESS;
goto end;
} else {
Status = STATUS_NO_MORE_FILES;
goto end;
}
}
} while (TRUE);
end:
ExReleaseResourceLite(&fileref->nonpaged->children_lock);
if (NT_SUCCESS(Status)) {
de->name_alloc = TRUE;
de->name = ExAllocatePoolWithTag(PagedPool, de->namelen, ALLOC_TAG);
if (!de->name) {
ERR("out of memory\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlCopyMemory(de->name, name, de->namelen);
} else
de->name_alloc = FALSE;
return Status;
}
static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
@ -538,8 +646,9 @@ static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
ULONG count;
BOOL has_wildcard = FALSE, specific_file = FALSE, initial;
// UINT64 num_reads_orig;
traverse_ptr tp;
dir_entry de;
UINT64 newoffset;
ANSI_STRING utf8;
TRACE("query directory\n");
@ -552,7 +661,22 @@ static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
ccb = IrpSp->FileObject->FsContext2;
fileref = ccb ? ccb->fileref : NULL;
acquire_tree_lock(fcb->Vcb, FALSE);
utf8.Buffer = NULL;
if (!fileref)
return STATUS_INVALID_PARAMETER;
if (!ccb) {
ERR("ccb was NULL\n");
return STATUS_INVALID_PARAMETER;
}
if (!(ccb->access & FILE_LIST_DIRECTORY)) {
WARN("insufficient privileges\n");
return STATUS_ACCESS_DENIED;
}
ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, TRUE);
TRACE("%S\n", file_desc(IrpSp->FileObject));
@ -593,82 +717,65 @@ static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
}
}
if (IrpSp->Parameters.QueryDirectory.FileName) {
// int i;
// WCHAR* us;
if (IrpSp->Parameters.QueryDirectory.FileName && IrpSp->Parameters.QueryDirectory.FileName->Length > 1) {
TRACE("QD filename: %.*S\n", IrpSp->Parameters.QueryDirectory.FileName->Length / sizeof(WCHAR), IrpSp->Parameters.QueryDirectory.FileName->Buffer);
// if (IrpSp->Parameters.QueryDirectory.FileName->Length > 1 || IrpSp->Parameters.QueryDirectory.FileName->Buffer[0] != '*') {
// specific_file = TRUE;
// for (i = 0; i < IrpSp->Parameters.QueryDirectory.FileName->Length; i++) {
// if (IrpSp->Parameters.QueryDirectory.FileName->Buffer[i] == '?' || IrpSp->Parameters.QueryDirectory.FileName->Buffer[i] == '*') {
// has_wildcard = TRUE;
// specific_file = FALSE;
// }
// }
// }
has_wildcard = TRUE;
if (IrpSp->Parameters.QueryDirectory.FileName->Buffer[0] != '*') {
specific_file = TRUE;
if (FsRtlDoesNameContainWildCards(IrpSp->Parameters.QueryDirectory.FileName)) {
has_wildcard = TRUE;
specific_file = FALSE;
}
}
if (ccb->query_string.Buffer)
RtlFreeUnicodeString(&ccb->query_string);
// us = ExAllocatePoolWithTag(PagedPool, IrpSp->Parameters.QueryDirectory.FileName->Length + sizeof(WCHAR), ALLOC_TAG);
// RtlCopyMemory(us, IrpSp->Parameters.QueryDirectory.FileName->Buffer, IrpSp->Parameters.QueryDirectory.FileName->Length);
// us[IrpSp->Parameters.QueryDirectory.FileName->Length / sizeof(WCHAR)] = 0;
// ccb->query_string = ExAllocatePoolWithTag(NonPagedPool, utf16_to_utf8_len(us), ALLOC_TAG);
// utf16_to_utf8(us, ccb->query_string);
// ccb->query_string.Buffer = ExAllocatePoolWithTag(PagedPool, IrpSp->Parameters.QueryDirectory.FileName->Length, ALLOC_TAG);
// RtlCopyMemory(ccb->query_string.Buffer, IrpSp->Parameters.QueryDirectory.FileName->Buffer,
// IrpSp->Parameters.QueryDirectory.FileName->Length);
// ccb->query_string.Length = IrpSp->Parameters.QueryDirectory.FileName->Length;
// ccb->query_string.MaximumLength = IrpSp->Parameters.QueryDirectory.FileName->Length;
if (has_wildcard)
RtlUpcaseUnicodeString(&ccb->query_string, IrpSp->Parameters.QueryDirectory.FileName, TRUE);
else {
ccb->query_string.Buffer = ExAllocatePoolWithTag(PagedPool, IrpSp->Parameters.QueryDirectory.FileName->Length, ALLOC_TAG);
if (!ccb->query_string.Buffer) {
ERR("out of memory\n");
Status = STATUS_INSUFFICIENT_RESOURCES;
goto end;
}
ccb->query_string.Length = ccb->query_string.MaximumLength = IrpSp->Parameters.QueryDirectory.FileName->Length;
RtlCopyMemory(ccb->query_string.Buffer, IrpSp->Parameters.QueryDirectory.FileName->Buffer, IrpSp->Parameters.QueryDirectory.FileName->Length);
}
ccb->has_wildcard = has_wildcard;
ccb->specific_file = specific_file;
// ExFreePool(us);
} else {
has_wildcard = ccb->has_wildcard;
specific_file = ccb->specific_file;
if (!(IrpSp->Flags & SL_RESTART_SCAN))
if (!(IrpSp->Flags & SL_RESTART_SCAN)) {
initial = FALSE;
if (specific_file) {
Status = STATUS_NO_MORE_FILES;
goto end;
}
}
}
if (ccb->query_string.Buffer) {
TRACE("query string = %.*S\n", ccb->query_string.Length / sizeof(WCHAR), ccb->query_string.Buffer);
}
tp.tree = NULL;
Status = next_dir_entry(fcb, fileref, &ccb->query_dir_offset, &de, &tp);
newoffset = ccb->query_dir_offset;
Status = next_dir_entry(fileref, &newoffset, &de);
if (!NT_SUCCESS(Status)) {
if (Status == STATUS_NO_MORE_FILES && initial)
Status = STATUS_NO_SUCH_FILE;
goto end;
}
// FIXME - make this work
// if (specific_file) {
// UINT64 filesubvol, fileinode;
// WCHAR* us;
//
// us = ExAllocatePoolWithTag(NonPagedPool, IrpSp->Parameters.QueryDirectory.FileName->Length + sizeof(WCHAR), ALLOC_TAG);
// RtlCopyMemory(us, IrpSp->Parameters.QueryDirectory.FileName->Buffer, IrpSp->Parameters.QueryDirectory.FileName->Length);
// us[IrpSp->Parameters.QueryDirectory.FileName->Length / sizeof(WCHAR)] = 0;
//
// if (!find_file_in_dir(fcb->Vcb, us, fcb->subvolume, fcb->inode, &filesubvol, &fileinode)) {
// ExFreePool(us);
// return STATUS_NO_MORE_FILES;
// }
//
// ExFreePool(us);
// }
ccb->query_dir_offset = newoffset;
buf = map_user_buffer(Irp);
if (Irp->MdlAddress && !buf) {
@ -679,8 +786,89 @@ static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
length = IrpSp->Parameters.QueryDirectory.Length;
// if (specific_file) {
if (has_wildcard) {
if (specific_file) {
BOOL found = FALSE;
root* found_subvol;
UINT64 found_inode, found_index;
UINT8 found_type;
UNICODE_STRING us;
LIST_ENTRY* le;
Status = RtlUpcaseUnicodeString(&us, &ccb->query_string, TRUE);
if (!NT_SUCCESS(Status)) {
ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
goto end;
}
ExAcquireResourceSharedLite(&fileref->nonpaged->children_lock, TRUE);
le = fileref->children.Flink;
while (le != &fileref->children) {
file_ref* fr2 = CONTAINING_RECORD(le, file_ref, list_entry);
if (!fr2->deleted && fr2->filepart_uc.Length == us.Length &&
RtlCompareMemory(fr2->filepart_uc.Buffer, us.Buffer, us.Length) == us.Length) {
found = TRUE;
if (fr2->fcb->subvol == fcb->subvol) {
de.key.obj_id = fr2->fcb->inode;
de.key.obj_type = TYPE_INODE_ITEM;
de.key.offset = 0;
} else {
de.key.obj_id = fr2->fcb->subvol->id;
de.key.obj_type = TYPE_ROOT_ITEM;
de.key.offset = 0;
}
de.name = ExAllocatePoolWithTag(PagedPool, fr2->utf8.Length, ALLOC_TAG);
if (!de.name) {
ERR("out of memory\n");
Status = STATUS_INSUFFICIENT_RESOURCES;
goto end;
}
RtlCopyMemory(de.name, fr2->utf8.Buffer, fr2->utf8.Length);
de.name_alloc = TRUE;
de.namelen = fr2->utf8.Length;
de.type = fr2->fcb->type;
de.dir_entry_type = DirEntryType_File;
break;
}
le = le->Flink;
}
ExReleaseResourceLite(&fileref->nonpaged->children_lock);
if (us.Buffer)
ExFreePool(us.Buffer);
if (!found) {
Status = find_file_in_dir(fcb->Vcb, &ccb->query_string, fileref, &found_subvol, &found_inode, &found_type, &found_index, &utf8);
if (!NT_SUCCESS(Status)) {
Status = STATUS_NO_SUCH_FILE;
goto end;
}
if (found_subvol == fcb->subvol) {
de.key.obj_id = found_inode;
de.key.obj_type = TYPE_INODE_ITEM;
de.key.offset = 0;
} else {
de.key.obj_id = found_subvol->id;
de.key.obj_type = TYPE_ROOT_ITEM;
de.key.offset = 0;
}
de.name = utf8.Buffer;
de.name_alloc = FALSE;
de.namelen = utf8.Length;
de.type = found_type;
de.dir_entry_type = DirEntryType_File;
}
} else if (has_wildcard) {
WCHAR* uni_fn;
ULONG stringlen;
UNICODE_STRING di_uni_fn;
@ -709,10 +897,16 @@ 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, fileref, &ccb->query_dir_offset, &de, &tp);
if (de.name_alloc)
ExFreePool(de.name);
newoffset = ccb->query_dir_offset;
Status = next_dir_entry(fileref, &newoffset, &de);
ExFreePool(uni_fn);
if (NT_SUCCESS(Status)) {
ccb->query_dir_offset = newoffset;
Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, de.name, de.namelen);
if (!NT_SUCCESS(Status)) {
ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
@ -752,6 +946,9 @@ static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
Status = query_dir_item(fcb, fileref, buf, &length, Irp, &de, fcb->subvol);
if (de.name_alloc)
ExFreePool(de.name);
count = 0;
if (NT_SUCCESS(Status) && !(IrpSp->Flags & SL_RETURN_SINGLE_ENTRY) && !specific_file) {
lastitem = (UINT8*)buf;
@ -778,7 +975,8 @@ static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
WCHAR* uni_fn = NULL;
UNICODE_STRING di_uni_fn;
Status = next_dir_entry(fcb, fileref, &ccb->query_dir_offset, &de, &tp);
newoffset = ccb->query_dir_offset;
Status = next_dir_entry(fileref, &newoffset, &de);
if (NT_SUCCESS(Status)) {
if (has_wildcard) {
ULONG stringlen;
@ -786,6 +984,7 @@ 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 (de.name_alloc) ExFreePool(de.name);
goto end;
}
@ -793,6 +992,7 @@ static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
if (!uni_fn) {
ERR("out of memory\n");
Status = STATUS_INSUFFICIENT_RESOURCES;
if (de.name_alloc) ExFreePool(de.name);
goto end;
}
@ -801,6 +1001,7 @@ static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
if (!NT_SUCCESS(Status)) {
ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
ExFreePool(uni_fn);
if (de.name_alloc) ExFreePool(de.name);
goto end;
}
@ -821,19 +1022,24 @@ static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
ULONG* lastoffset = (ULONG*)lastitem;
*lastoffset = (ULONG)(curitem - lastitem);
ccb->query_dir_offset = newoffset;
lastitem = curitem;
} else {
if (uni_fn) ExFreePool(uni_fn);
if (de.name_alloc) ExFreePool(de.name);
break;
}
}
} else
ccb->query_dir_offset = newoffset;
if (uni_fn) {
ExFreePool(uni_fn);
uni_fn = NULL;
}
if (de.name_alloc)
ExFreePool(de.name);
} else {
if (Status == STATUS_NO_MORE_FILES)
Status = STATUS_SUCCESS;
@ -848,10 +1054,12 @@ static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
Irp->IoStatus.Information = IrpSp->Parameters.QueryDirectory.Length - length;
end:
release_tree_lock(fcb->Vcb, FALSE);
ExReleaseResourceLite(&fcb->Vcb->tree_lock);
// TRACE("query directory performed %u reads\n", (UINT32)(num_reads-num_reads_orig));
TRACE("returning %08x\n", Status);
if (utf8.Buffer)
ExFreePool(utf8.Buffer);
return Status;
}
@ -863,16 +1071,26 @@ static NTSTATUS STDCALL notify_change_directory(device_extension* Vcb, PIRP Irp)
ccb* ccb = FileObject->FsContext2;
file_ref* fileref = ccb->fileref;
NTSTATUS Status;
// WCHAR fn[MAX_PATH];
TRACE("IRP_MN_NOTIFY_CHANGE_DIRECTORY\n");
if (!ccb) {
ERR("ccb was NULL\n");
return STATUS_INVALID_PARAMETER;
}
if (!fileref) {
ERR("no fileref\n");
return STATUS_INVALID_PARAMETER;
}
acquire_tree_lock(fcb->Vcb, FALSE);
if (!(ccb->access & FILE_LIST_DIRECTORY)) {
WARN("insufficient privileges\n");
return STATUS_ACCESS_DENIED;
}
ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, TRUE);
ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
if (fcb->type != BTRFS_TYPE_DIRECTORY) {
Status = STATUS_INVALID_PARAMETER;
@ -882,14 +1100,24 @@ static NTSTATUS STDCALL notify_change_directory(device_extension* Vcb, PIRP Irp)
// FIXME - raise exception if FCB marked for deletion?
TRACE("%S\n", file_desc(FileObject));
if (ccb->filename.Length == 0) {
Status = fileref_get_filename(fileref, &ccb->filename, NULL);
if (!NT_SUCCESS(Status)) {
ERR("fileref_get_filename returned %08x\n", Status);
goto end;
}
}
FsRtlNotifyFullChangeDirectory(Vcb->NotifySync, &Vcb->DirNotifyList, FileObject->FsContext2, (PSTRING)&fileref->full_filename,
IrpSp->Flags & SL_WATCH_TREE, FALSE, IrpSp->Parameters.NotifyDirectory.CompletionFilter, Irp, NULL, NULL);
FsRtlNotifyFilterChangeDirectory(Vcb->NotifySync, &Vcb->DirNotifyList, FileObject->FsContext2, (PSTRING)&ccb->filename,
IrpSp->Flags & SL_WATCH_TREE, FALSE, IrpSp->Parameters.NotifyDirectory.CompletionFilter, Irp,
NULL, NULL, NULL);
Status = STATUS_PENDING;
end:
release_tree_lock(fcb->Vcb, FALSE);
ExReleaseResourceLite(fcb->Header.Resource);
ExReleaseResourceLite(&fcb->Vcb->tree_lock);
return Status;
}
@ -899,6 +1127,7 @@ NTSTATUS STDCALL drv_directory_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP I
NTSTATUS Status;
ULONG func;
BOOL top_level;
device_extension* Vcb = DeviceObject->DeviceExtension;
TRACE("directory control\n");
@ -906,6 +1135,11 @@ NTSTATUS STDCALL drv_directory_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP I
top_level = is_top_level(Irp);
if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
Status = part0_passthrough(DeviceObject, Irp);
goto exit;
}
IrpSp = IoGetCurrentIrpStackLocation(Irp);
Irp->IoStatus.Information = 0;
@ -914,7 +1148,7 @@ NTSTATUS STDCALL drv_directory_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP I
switch (func) {
case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
Status = notify_change_directory(DeviceObject->DeviceExtension, Irp);
Status = notify_change_directory(Vcb, Irp);
break;
case IRP_MN_QUERY_DIRECTORY:
@ -927,16 +1161,18 @@ NTSTATUS STDCALL drv_directory_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP I
Irp->IoStatus.Status = Status;
break;
}
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 );
}
if (Status == STATUS_PENDING)
goto exit;
Irp->IoStatus.Status = Status;
// if (Irp->UserIosb)
// *Irp->UserIosb = Irp->IoStatus;
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
exit:
if (top_level)
IoSetTopLevelIrp(NULL);

View file

@ -25,7 +25,9 @@ static __inline ULONG get_extent_data_len(UINT8 type) {
case TYPE_EXTENT_DATA_REF:
return sizeof(EXTENT_DATA_REF);
// FIXME - TYPE_EXTENT_REF_V0
case TYPE_EXTENT_REF_V0:
return sizeof(EXTENT_REF_V0);
// FIXME - TYPE_SHARED_BLOCK_REF
case TYPE_SHARED_DATA_REF:
@ -47,7 +49,12 @@ static __inline UINT64 get_extent_data_refcount(UINT8 type, void* data) {
return edr->count;
}
// FIXME - TYPE_EXTENT_REF_V0
case TYPE_EXTENT_REF_V0:
{
EXTENT_REF_V0* erv0 = (EXTENT_REF_V0*)data;
return erv0->count;
}
// FIXME - TYPE_SHARED_BLOCK_REF
case TYPE_SHARED_DATA_REF:
@ -61,16 +68,20 @@ static __inline UINT64 get_extent_data_refcount(UINT8 type, void* data) {
}
}
static UINT64 get_extent_data_ref_hash(EXTENT_DATA_REF* edr) {
static UINT64 get_extent_data_ref_hash2(UINT64 root, UINT64 objid, UINT64 offset) {
UINT32 high_crc = 0xffffffff, low_crc = 0xffffffff;
high_crc = calc_crc32c(high_crc, (UINT8*)&edr->root, sizeof(UINT64));
low_crc = calc_crc32c(low_crc, (UINT8*)&edr->objid, sizeof(UINT64));
low_crc = calc_crc32c(low_crc, (UINT8*)&edr->offset, sizeof(UINT64));
high_crc = calc_crc32c(high_crc, (UINT8*)&root, sizeof(UINT64));
low_crc = calc_crc32c(low_crc, (UINT8*)&objid, sizeof(UINT64));
low_crc = calc_crc32c(low_crc, (UINT8*)&offset, sizeof(UINT64));
return ((UINT64)high_crc << 31) ^ (UINT64)low_crc;
}
static __inline UINT64 get_extent_data_ref_hash(EXTENT_DATA_REF* edr) {
return get_extent_data_ref_hash2(edr->root, edr->objid, edr->offset);
}
static UINT64 get_extent_hash(UINT8 type, void* data) {
if (type == TYPE_EXTENT_DATA_REF) {
return get_extent_data_ref_hash((EXTENT_DATA_REF*)data);
@ -153,17 +164,40 @@ static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address,
} else if (tp.item->key.offset != size) {
ERR("extent %llx exists, but with size %llx rather than %llx expected\n", tp.item->key.obj_id, tp.item->key.offset, size);
return STATUS_INTERNAL_ERROR;
} else if (tp.item->size == sizeof(EXTENT_ITEM_V0)) {
}
if (tp.item->size == sizeof(EXTENT_ITEM_V0)) {
EXTENT_ITEM_V0* eiv0 = (EXTENT_ITEM_V0*)tp.item->data;
TRACE("converting old-style extent at (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
Status = convert_old_data_extent(Vcb, address, size, rollback);
if (!NT_SUCCESS(Status)) {
ERR("convert_old_data_extent returned %08x\n", Status);
return Status;
ei = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_ITEM), ALLOC_TAG);
if (!ei) {
ERR("out of memory\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
return increase_extent_refcount(Vcb, address, size, type, data, firstitem, level, rollback);
} else if (tp.item->size < sizeof(EXTENT_ITEM)) {
ei->refcount = eiv0->refcount;
ei->generation = Vcb->superblock.generation;
ei->flags = EXTENT_ITEM_DATA;
delete_tree_item(Vcb, &tp, rollback);
if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, ei, sizeof(EXTENT_ITEM), NULL, rollback)) {
ERR("insert_tree_item failed\n");
ExFreePool(ei);
return STATUS_INTERNAL_ERROR;
}
Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
}
}
if (tp.item->size < sizeof(EXTENT_ITEM)) {
ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_ITEM));
return STATUS_INTERNAL_ERROR;
}
@ -204,18 +238,6 @@ static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address,
return STATUS_INTERNAL_ERROR;
}
if (secttype == TYPE_SHARED_DATA_REF) {
TRACE("found shared data extent at %llx, converting\n", tp.item->key.obj_id);
Status = convert_shared_data_extent(Vcb, address, size, rollback);
if (!NT_SUCCESS(Status)) {
ERR("convert_shared_data_extent returned %08x\n", Status);
return Status;
}
return increase_extent_refcount(Vcb, address, size, type, data, firstitem, level, rollback);
}
// If inline extent already present, increase refcount and return
if (secttype == type) {
@ -286,8 +308,6 @@ static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address,
if (secttype > type)
break;
len--;
if (secttype == type) {
UINT64 sectoff = get_extent_hash(secttype, ptr + 1);
@ -295,7 +315,7 @@ static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address,
break;
}
len -= sectlen;
len -= sectlen + sizeof(UINT8);
ptr += sizeof(UINT8) + sectlen;
}
@ -306,7 +326,7 @@ static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address,
newei->refcount += get_extent_data_refcount(type, data);
if (len > 0)
RtlCopyMemory((UINT8*)newei + (ptr - tp.item->data) + sizeof(UINT8) + datalen, ptr, len + 1);
RtlCopyMemory((UINT8*)newei + (ptr - tp.item->data) + sizeof(UINT8) + datalen, ptr, len);
ptr = (ptr - tp.item->data) + (UINT8*)newei;
@ -409,10 +429,10 @@ static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address,
return STATUS_SUCCESS;
}
NTSTATUS increase_extent_refcount_data(device_extension* Vcb, UINT64 address, UINT64 size, root* subvol, UINT64 inode, UINT64 offset, UINT32 refcount, LIST_ENTRY* rollback) {
NTSTATUS increase_extent_refcount_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 inode, UINT64 offset, UINT32 refcount, LIST_ENTRY* rollback) {
EXTENT_DATA_REF edr;
edr.root = subvol->id;
edr.root = root;
edr.objid = inode;
edr.offset = offset;
edr.count = refcount;
@ -426,57 +446,16 @@ void decrease_chunk_usage(chunk* c, UINT64 delta) {
TRACE("decreasing size of chunk %llx by %llx\n", c->offset, delta);
}
static NTSTATUS remove_extent(device_extension* Vcb, UINT64 address, UINT64 size, LIST_ENTRY* changed_sector_list) {
chunk* c;
LIST_ENTRY* le;
if (changed_sector_list) {
changed_sector* sc = ExAllocatePoolWithTag(PagedPool, sizeof(changed_sector), ALLOC_TAG);
if (!sc) {
ERR("out of memory\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
sc->ol.key = address;
sc->checksums = NULL;
sc->length = size / Vcb->superblock.sector_size;
sc->deleted = TRUE;
insert_into_ordered_list(changed_sector_list, &sc->ol);
}
c = NULL;
le = Vcb->chunks.Flink;
while (le != &Vcb->chunks) {
c = CONTAINING_RECORD(le, chunk, list_entry);
if (address >= c->offset && address + size < c->offset + c->chunk_item->size)
break;
le = le->Flink;
}
if (le == &Vcb->chunks) c = NULL;
if (c) {
decrease_chunk_usage(c, size);
add_to_space_list(c, address, size, SPACE_TYPE_DELETING);
}
return STATUS_SUCCESS;
}
static NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address, UINT64 size, UINT8 type, void* data, KEY* firstitem,
UINT8 level, LIST_ENTRY* changed_sector_list, LIST_ENTRY* rollback) {
UINT8 level, UINT64 parent, LIST_ENTRY* rollback) {
KEY searchkey;
NTSTATUS Status;
traverse_ptr tp, tp2;
EXTENT_ITEM* ei;
ULONG len;
UINT64 inline_rc, offset;
UINT64 inline_rc;
UINT8* ptr;
UINT32 rc = get_extent_data_refcount(type, data);
UINT32 rc = data ? get_extent_data_refcount(type, data) : 1;
ULONG datalen = get_extent_data_len(type);
// FIXME - handle trees
@ -502,15 +481,34 @@ static NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address,
}
if (tp.item->size == sizeof(EXTENT_ITEM_V0)) {
EXTENT_ITEM_V0* eiv0 = (EXTENT_ITEM_V0*)tp.item->data;
TRACE("converting old-style extent at (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
Status = convert_old_data_extent(Vcb, address, size, rollback);
if (!NT_SUCCESS(Status)) {
ERR("convert_old_data_extent returned %08x\n", Status);
return Status;
ei = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_ITEM), ALLOC_TAG);
if (!ei) {
ERR("out of memory\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
return decrease_extent_refcount(Vcb, address, size, type, data, firstitem, level, changed_sector_list, rollback);
ei->refcount = eiv0->refcount;
ei->generation = Vcb->superblock.generation;
ei->flags = EXTENT_ITEM_DATA;
delete_tree_item(Vcb, &tp, rollback);
if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, ei, sizeof(EXTENT_ITEM), &tp, rollback)) {
ERR("insert_tree_item failed\n");
ExFreePool(ei);
return STATUS_INTERNAL_ERROR;
}
Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
}
}
if (tp.item->size < sizeof(EXTENT_ITEM)) {
@ -559,18 +557,6 @@ static NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address,
return STATUS_INTERNAL_ERROR;
}
if (secttype == TYPE_SHARED_DATA_REF) {
TRACE("found shared data extent at %llx, converting\n", tp.item->key.obj_id);
Status = convert_shared_data_extent(Vcb, address, size, rollback);
if (!NT_SUCCESS(Status)) {
ERR("convert_shared_data_extent returned %08x\n", Status);
return Status;
}
return decrease_extent_refcount(Vcb, address, size, type, data, firstitem, level, changed_sector_list, rollback);
}
if (secttype == type) {
if (type == TYPE_EXTENT_DATA_REF) {
EXTENT_DATA_REF* sectedr = (EXTENT_DATA_REF*)(ptr + sizeof(UINT8));
@ -580,12 +566,6 @@ static NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address,
if (sectedr->root == edr->root && sectedr->objid == edr->objid && sectedr->offset == edr->offset) {
if (ei->refcount == edr->count) {
Status = remove_extent(Vcb, address, size, changed_sector_list);
if (!NT_SUCCESS(Status)) {
ERR("remove_extent returned %08x\n", Status);
return Status;
}
delete_tree_item(Vcb, &tp, rollback);
return STATUS_SUCCESS;
}
@ -631,9 +611,45 @@ static NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address,
return STATUS_SUCCESS;
}
// } else if (type == TYPE_TREE_BLOCK_REF) {
// ERR("trying to increase refcount of tree extent\n");
// return STATUS_INTERNAL_ERROR;
} else if (type == TYPE_SHARED_DATA_REF) {
SHARED_DATA_REF* sectsdr = (SHARED_DATA_REF*)(ptr + sizeof(UINT8));
SHARED_DATA_REF* sdr = (SHARED_DATA_REF*)data;
ULONG neweilen;
EXTENT_ITEM* newei;
if (sectsdr->offset == sdr->offset) {
// We ignore sdr->count, and assume that we want to remove the whole bit
if (ei->refcount == sectsdr->count) {
delete_tree_item(Vcb, &tp, rollback);
return STATUS_SUCCESS;
}
neweilen = tp.item->size - sizeof(UINT8) - sectlen;
newei = ExAllocatePoolWithTag(PagedPool, neweilen, ALLOC_TAG);
if (!newei) {
ERR("out of memory\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlCopyMemory(newei, ei, ptr - tp.item->data);
if (len > sectlen)
RtlCopyMemory((UINT8*)newei + (ptr - tp.item->data), ptr + sectlen + sizeof(UINT8), len - sectlen);
newei->generation = Vcb->superblock.generation;
newei->refcount -= rc;
delete_tree_item(Vcb, &tp, rollback);
if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, neweilen, NULL, rollback)) {
ERR("insert_tree_item failed\n");
return STATUS_INTERNAL_ERROR;
}
return STATUS_SUCCESS;
}
} else {
ERR("unhandled extent type %x\n", type);
return STATUS_INTERNAL_ERROR;
@ -650,11 +666,9 @@ static NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address,
return STATUS_INTERNAL_ERROR;
}
offset = get_extent_hash(type, data);
searchkey.obj_id = address;
searchkey.obj_type = type;
searchkey.offset = offset;
searchkey.offset = (type == TYPE_SHARED_DATA_REF || type == TYPE_EXTENT_REF_V0) ? parent : get_extent_hash(type, data);
Status = find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE);
if (!NT_SUCCESS(Status)) {
@ -679,12 +693,6 @@ static NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address,
if (sectedr->root == edr->root && sectedr->objid == edr->objid && sectedr->offset == edr->offset) {
if (ei->refcount == edr->count) {
Status = remove_extent(Vcb, address, size, changed_sector_list);
if (!NT_SUCCESS(Status)) {
ERR("remove_extent returned %08x\n", Status);
return Status;
}
delete_tree_item(Vcb, &tp, rollback);
delete_tree_item(Vcb, &tp2, rollback);
return STATUS_SUCCESS;
@ -738,25 +746,105 @@ static NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address,
ERR("error - hash collision?\n");
return STATUS_INTERNAL_ERROR;
}
// } else if (type == TYPE_TREE_BLOCK_REF) {
// ERR("trying to increase refcount of tree extent\n");
// return STATUS_INTERNAL_ERROR;
} else if (type == TYPE_SHARED_DATA_REF) {
SHARED_DATA_REF* sectsdr = (SHARED_DATA_REF*)tp2.item->data;
SHARED_DATA_REF* sdr = (SHARED_DATA_REF*)data;
EXTENT_ITEM* newei;
if (sectsdr->offset == sdr->offset) {
// As above, we assume that we want to remove the whole shared data ref
if (ei->refcount == sectsdr->count) {
delete_tree_item(Vcb, &tp, rollback);
delete_tree_item(Vcb, &tp2, rollback);
return STATUS_SUCCESS;
}
delete_tree_item(Vcb, &tp2, rollback);
newei = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
if (!newei) {
ERR("out of memory\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlCopyMemory(newei, tp.item->data, tp.item->size);
newei->generation = Vcb->superblock.generation;
newei->refcount -= rc;
delete_tree_item(Vcb, &tp, rollback);
if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, rollback)) {
ERR("insert_tree_item failed\n");
return STATUS_INTERNAL_ERROR;
}
return STATUS_SUCCESS;
} else {
ERR("error - collision?\n");
return STATUS_INTERNAL_ERROR;
}
} else if (type == TYPE_EXTENT_REF_V0) {
EXTENT_REF_V0* erv0 = (EXTENT_REF_V0*)tp2.item->data;
EXTENT_ITEM* newei;
if (ei->refcount == erv0->count) {
delete_tree_item(Vcb, &tp, rollback);
delete_tree_item(Vcb, &tp2, rollback);
return STATUS_SUCCESS;
}
delete_tree_item(Vcb, &tp2, rollback);
newei = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
if (!newei) {
ERR("out of memory\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlCopyMemory(newei, tp.item->data, tp.item->size);
newei->generation = Vcb->superblock.generation;
newei->refcount -= rc;
delete_tree_item(Vcb, &tp, rollback);
if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, rollback)) {
ERR("insert_tree_item failed\n");
return STATUS_INTERNAL_ERROR;
}
return STATUS_SUCCESS;
} else {
ERR("unhandled extent type %x\n", type);
return STATUS_INTERNAL_ERROR;
}
}
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) {
NTSTATUS decrease_extent_refcount_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 inode,
UINT64 offset, UINT32 refcount, LIST_ENTRY* rollback) {
EXTENT_DATA_REF edr;
edr.root = subvol->id;
edr.root = root;
edr.objid = inode;
edr.offset = offset;
edr.count = refcount;
return decrease_extent_refcount(Vcb, address, size, TYPE_EXTENT_DATA_REF, &edr, NULL, 0, changed_sector_list, rollback);
return decrease_extent_refcount(Vcb, address, size, TYPE_EXTENT_DATA_REF, &edr, NULL, 0, 0, rollback);
}
NTSTATUS decrease_extent_refcount_shared_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 treeaddr, UINT64 parent, LIST_ENTRY* rollback) {
SHARED_DATA_REF sdr;
sdr.offset = treeaddr;
sdr.count = 1;
return decrease_extent_refcount(Vcb, address, size, TYPE_SHARED_DATA_REF, &sdr, NULL, 0, parent, rollback);
}
NTSTATUS decrease_extent_refcount_old(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 treeaddr, LIST_ENTRY* rollback) {
return decrease_extent_refcount(Vcb, address, size, TYPE_EXTENT_REF_V0, NULL, NULL, 0, treeaddr, rollback);
}
typedef struct {
@ -954,9 +1042,9 @@ static NTSTATUS populate_extent_refs_from_tree(device_extension* Vcb, UINT64 tre
return STATUS_INSUFFICIENT_RESOURCES;
}
Status = read_tree(Vcb, tree_address, buf);
Status = read_data(Vcb, tree_address, Vcb->superblock.node_size, NULL, TRUE, buf, NULL, NULL);
if (!NT_SUCCESS(Status)) {
ERR("read_tree returned %08x\n", Status);
ERR("read_data returned %08x\n", Status);
ExFreePool(buf);
return Status;
}
@ -993,177 +1081,6 @@ static NTSTATUS populate_extent_refs_from_tree(device_extension* Vcb, UINT64 tre
return STATUS_SUCCESS;
}
NTSTATUS convert_shared_data_extent(device_extension* Vcb, UINT64 address, UINT64 size, LIST_ENTRY* rollback) {
KEY searchkey;
traverse_ptr tp;
LIST_ENTRY extent_refs;
LIST_ENTRY *le, *next_le;
EXTENT_ITEM* ei;
UINT64 eiflags, inline_rc;
UINT8* siptr;
ULONG len;
NTSTATUS Status;
searchkey.obj_id = address;
searchkey.obj_type = TYPE_EXTENT_ITEM;
searchkey.offset = size;
Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
}
if (keycmp(&tp.item->key, &searchkey)) {
WARN("extent item not found for address %llx, size %llx\n", address, size);
return STATUS_SUCCESS;
}
if (tp.item->size < sizeof(EXTENT_ITEM)) {
ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_ITEM));
return STATUS_INTERNAL_ERROR;
}
ei = (EXTENT_ITEM*)tp.item->data;
len = tp.item->size - sizeof(EXTENT_ITEM);
eiflags = ei->flags;
InitializeListHead(&extent_refs);
inline_rc = 0;
siptr = (UINT8*)&ei[1];
do {
extent_ref* er;
ULONG extlen;
extlen = get_extent_data_len(*siptr);
if (extlen == 0) {
ERR("unrecognized extent subitem %x\n", *siptr);
free_extent_refs(&extent_refs);
return STATUS_INTERNAL_ERROR;
}
if (extlen > len - 1) {
ERR("extent %llx was truncated\n", address);
free_extent_refs(&extent_refs);
return STATUS_INTERNAL_ERROR;
}
er = ExAllocatePoolWithTag(PagedPool, sizeof(extent_ref), ALLOC_TAG);
if (!er) {
ERR("out of memory\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
er->type = *siptr;
er->data = ExAllocatePoolWithTag(PagedPool, extlen, ALLOC_TAG);
if (!er->data) {
ERR("out of memory\n");
ExFreePool(er);
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlCopyMemory(er->data, siptr+1, extlen);
er->allocated = TRUE;
InsertTailList(&extent_refs, &er->list_entry);
siptr += extlen;
len -= extlen + 1;
inline_rc += get_extent_data_refcount(er->type, er->data);
} while (len > 0);
delete_tree_item(Vcb, &tp, rollback);
if (inline_rc < ei->refcount) {
BOOL b;
traverse_ptr next_tp;
do {
b = find_next_item(Vcb, &tp, &next_tp, FALSE);
if (tp.item->key.obj_id == address) {
ULONG extlen;
extlen = get_extent_data_len(tp.item->key.obj_type);
if (extlen != 0 && tp.item->size >= extlen) {
extent_ref* er = ExAllocatePoolWithTag(PagedPool, sizeof(extent_ref), ALLOC_TAG);
if (!er) {
ERR("out of memory\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
er->type = tp.item->key.obj_type;
er->data = ExAllocatePoolWithTag(PagedPool, extlen, ALLOC_TAG);
if (!er->data) {
ERR("out of memory\n");
ExFreePool(er);
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlCopyMemory(er->data, siptr+1, extlen);
er->allocated = TRUE;
InsertTailList(&extent_refs, &er->list_entry);
delete_tree_item(Vcb, &tp, rollback);
}
}
if (b) {
tp = next_tp;
if (tp.item->key.obj_id > address)
break;
}
} while (b);
}
le = extent_refs.Flink;
while (le != &extent_refs) {
extent_ref* er = CONTAINING_RECORD(le, extent_ref, list_entry);
next_le = le->Flink;
if (er->type == TYPE_SHARED_DATA_REF) {
SHARED_DATA_REF* sdr = er->data;
Status = populate_extent_refs_from_tree(Vcb, sdr->offset, address, &extent_refs);
if (!NT_SUCCESS(Status)) {
ERR("populate_extent_refs_from_tree returned %08x\n", Status);
free_extent_refs(&extent_refs);
return Status;
}
RemoveEntryList(&er->list_entry);
if (er->allocated)
ExFreePool(er->data);
ExFreePool(er);
}
// FIXME - also do for SHARED_BLOCK_REF?
le = next_le;
}
Status = construct_extent_item(Vcb, address, size, eiflags, &extent_refs, rollback);
if (!NT_SUCCESS(Status)) {
ERR("construct_extent_item returned %08x\n", Status);
free_extent_refs(&extent_refs);
return Status;
}
free_extent_refs(&extent_refs);
return STATUS_SUCCESS;
}
NTSTATUS convert_old_data_extent(device_extension* Vcb, UINT64 address, UINT64 size, LIST_ENTRY* rollback) {
KEY searchkey;
traverse_ptr tp, next_tp;
@ -1237,3 +1154,241 @@ NTSTATUS convert_old_data_extent(device_extension* Vcb, UINT64 address, UINT64 s
return STATUS_SUCCESS;
}
UINT64 find_extent_data_refcount(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 objid, UINT64 offset) {
NTSTATUS Status;
KEY searchkey;
traverse_ptr tp;
EXTENT_DATA_REF* edr;
BOOL old = FALSE;
searchkey.obj_id = address;
searchkey.obj_type = TYPE_EXTENT_ITEM;
searchkey.offset = 0xffffffffffffffff;
Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return 0;
}
if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
ERR("could not find address %llx in extent tree\n", address);
return 0;
}
if (tp.item->key.offset != size) {
ERR("extent %llx had size %llx, not %llx as expected\n", address, tp.item->key.offset, size);
return 0;
}
if (tp.item->size >= sizeof(EXTENT_ITEM)) {
EXTENT_ITEM* ei = (EXTENT_ITEM*)tp.item->data;
UINT32 len = tp.item->size - sizeof(EXTENT_ITEM);
UINT8* ptr = (UINT8*)&ei[1];
while (len > 0) {
UINT8 secttype = *ptr;
ULONG sectlen = get_extent_data_len(secttype);
UINT64 sectcount = get_extent_data_refcount(secttype, ptr + sizeof(UINT8));
len--;
if (sectlen > len) {
ERR("(%llx,%x,%llx): %x bytes left, expecting at least %x\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, len, sectlen);
return 0;
}
if (sectlen == 0) {
ERR("(%llx,%x,%llx): unrecognized extent type %x\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, secttype);
return 0;
}
if (secttype == TYPE_EXTENT_DATA_REF) {
EXTENT_DATA_REF* sectedr = (EXTENT_DATA_REF*)(ptr + sizeof(UINT8));
if (sectedr->root == root && sectedr->objid == objid && sectedr->offset == offset)
return sectcount;
} else if (secttype == TYPE_SHARED_DATA_REF) {
SHARED_DATA_REF* sectsdr = (SHARED_DATA_REF*)(ptr + sizeof(UINT8));
BOOL found = FALSE;
LIST_ENTRY* le;
le = Vcb->shared_extents.Flink;
while (le != &Vcb->shared_extents) {
shared_data* sd = CONTAINING_RECORD(le, shared_data, list_entry);
if (sd->address == sectsdr->offset) {
LIST_ENTRY* le2 = sd->entries.Flink;
while (le2 != &sd->entries) {
shared_data_entry* sde = CONTAINING_RECORD(le2, shared_data_entry, list_entry);
if (sde->edr.root == root && sde->edr.objid == objid && sde->edr.offset == offset)
return sde->edr.count;
le2 = le2->Flink;
}
found = TRUE;
break;
}
le = le->Flink;
}
if (!found)
WARN("shared data extents not loaded for tree at %llx\n", sectsdr->offset);
}
len -= sectlen;
ptr += sizeof(UINT8) + sectlen;
}
} else if (tp.item->size == sizeof(EXTENT_ITEM_V0))
old = TRUE;
searchkey.obj_id = address;
searchkey.obj_type = TYPE_EXTENT_DATA_REF;
searchkey.offset = get_extent_data_ref_hash2(root, objid, offset);
Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return 0;
}
if (!keycmp(&searchkey, &tp.item->key)) {
if (tp.item->size < sizeof(EXTENT_DATA_REF))
ERR("(%llx,%x,%llx) has size %u, not %u as expected\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA_REF));
else {
edr = (EXTENT_DATA_REF*)tp.item->data;
return edr->count;
}
}
if (old) {
BOOL b;
searchkey.obj_id = address;
searchkey.obj_type = TYPE_EXTENT_REF_V0;
searchkey.offset = 0;
Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return 0;
}
do {
traverse_ptr next_tp;
b = find_next_item(Vcb, &tp, &next_tp, FALSE);
if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
if (tp.item->size >= sizeof(EXTENT_REF_V0)) {
EXTENT_REF_V0* erv0 = (EXTENT_REF_V0*)tp.item->data;
if (erv0->root == root && erv0->objid == objid) {
LIST_ENTRY* le;
BOOL found = FALSE;
le = Vcb->shared_extents.Flink;
while (le != &Vcb->shared_extents) {
shared_data* sd = CONTAINING_RECORD(le, shared_data, list_entry);
if (sd->address == tp.item->key.offset) {
LIST_ENTRY* le2 = sd->entries.Flink;
while (le2 != &sd->entries) {
shared_data_entry* sde = CONTAINING_RECORD(le2, shared_data_entry, list_entry);
if (sde->edr.root == root && sde->edr.objid == objid && sde->edr.offset == offset)
return sde->edr.count;
le2 = le2->Flink;
}
found = TRUE;
break;
}
le = le->Flink;
}
if (!found)
WARN("shared data extents not loaded for tree at %llx\n", tp.item->key.offset);
}
} else {
ERR("(%llx,%x,%llx) was %x bytes, not %x as expected\n", tp.item->key.obj_id, tp.item->key.obj_type,
tp.item->key.offset, tp.item->size, sizeof(EXTENT_REF_V0));
}
}
if (b) {
tp = next_tp;
if (tp.item->key.obj_id > searchkey.obj_id || (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type > searchkey.obj_type))
break;
}
} while (b);
} else {
BOOL b;
searchkey.obj_id = address;
searchkey.obj_type = TYPE_SHARED_DATA_REF;
searchkey.offset = 0;
Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return 0;
}
do {
traverse_ptr next_tp;
b = find_next_item(Vcb, &tp, &next_tp, FALSE);
if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
if (tp.item->size >= sizeof(SHARED_DATA_REF)) {
SHARED_DATA_REF* sdr = (SHARED_DATA_REF*)tp.item->data;
LIST_ENTRY* le;
BOOL found = FALSE;
le = Vcb->shared_extents.Flink;
while (le != &Vcb->shared_extents) {
shared_data* sd = CONTAINING_RECORD(le, shared_data, list_entry);
if (sd->address == sdr->offset) {
LIST_ENTRY* le2 = sd->entries.Flink;
while (le2 != &sd->entries) {
shared_data_entry* sde = CONTAINING_RECORD(le2, shared_data_entry, list_entry);
if (sde->edr.root == root && sde->edr.objid == objid && sde->edr.offset == offset)
return sde->edr.count;
le2 = le2->Flink;
}
found = TRUE;
break;
}
le = le->Flink;
}
if (!found)
WARN("shared data extents not loaded for tree at %llx\n", sdr->offset);
} else {
ERR("(%llx,%x,%llx) was %x bytes, not %x as expected\n", tp.item->key.obj_id, tp.item->key.obj_type,
tp.item->key.offset, tp.item->size, sizeof(SHARED_DATA_REF));
}
}
if (b) {
tp = next_tp;
if (tp.item->key.obj_id > searchkey.obj_id || (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type > searchkey.obj_type))
break;
}
} while (b);
}
return 0;
}

View file

@ -15,7 +15,6 @@
* You should have received a copy of the GNU Lesser General Public Licence
* along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */
#include <ntifs.h>
#include "btrfs_drv.h"
FAST_IO_DISPATCH FastIoDispatch;
@ -28,20 +27,128 @@ static void STDCALL release_file_for_create_section(PFILE_OBJECT FileObject) {
TRACE("STUB: release_file_for_create_section\n");
}
static BOOLEAN STDCALL fast_query_basic_info(PFILE_OBJECT FileObject, BOOLEAN wait, PFILE_BASIC_INFORMATION buf,
PIO_STATUS_BLOCK IoStatus, PDEVICE_OBJECT DeviceObject) {
static BOOLEAN STDCALL fast_query_basic_info(PFILE_OBJECT FileObject, BOOLEAN wait, PFILE_BASIC_INFORMATION fbi,
PIO_STATUS_BLOCK IoStatus, PDEVICE_OBJECT DeviceObject) {
fcb* fcb;
ccb* ccb;
TRACE("STUB: fast_query_basic_info\n");
TRACE("(%p, %u, %p, %p, %p)\n", FileObject, wait, fbi, IoStatus, DeviceObject);
return FALSE;
if (!FileObject)
return FALSE;
fcb = FileObject->FsContext;
if (!fcb)
return FALSE;
ccb = FileObject->FsContext2;
if (!ccb)
return FALSE;
if (!(ccb->access & (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES)))
return FALSE;
if (fcb->ads) {
if (!ccb || !ccb->fileref || !ccb->fileref->parent || !ccb->fileref->parent->fcb)
return FALSE;
fcb = ccb->fileref->parent->fcb;
}
FsRtlEnterFileSystem();
if (!ExAcquireResourceSharedLite(fcb->Header.Resource, wait)) {
FsRtlExitFileSystem();
return FALSE;
}
fbi->CreationTime.QuadPart = unix_time_to_win(&fcb->inode_item.otime);
fbi->LastAccessTime.QuadPart = unix_time_to_win(&fcb->inode_item.st_atime);
fbi->LastWriteTime.QuadPart = unix_time_to_win(&fcb->inode_item.st_mtime);
fbi->ChangeTime.QuadPart = 0;
fbi->FileAttributes = fcb->atts;
IoStatus->Status = STATUS_SUCCESS;
IoStatus->Information = sizeof(FILE_BASIC_INFORMATION);
ExReleaseResourceLite(fcb->Header.Resource);
FsRtlExitFileSystem();
return TRUE;
}
static BOOLEAN STDCALL fast_query_standard_info(PFILE_OBJECT FileObject, BOOLEAN wait, PFILE_STANDARD_INFORMATION buf,
PIO_STATUS_BLOCK IoStatus, PDEVICE_OBJECT DeviceObject) {
static BOOLEAN STDCALL fast_query_standard_info(PFILE_OBJECT FileObject, BOOLEAN wait, PFILE_STANDARD_INFORMATION fsi,
PIO_STATUS_BLOCK IoStatus, PDEVICE_OBJECT DeviceObject) {
fcb* fcb;
ccb* ccb;
BOOL ads;
ULONG adssize;
TRACE("STUB: fast_query_standard_info\n");
TRACE("(%p, %u, %p, %p, %p)\n", FileObject, wait, fsi, IoStatus, DeviceObject);
return FALSE;
if (!FileObject)
return FALSE;
fcb = FileObject->FsContext;
ccb = FileObject->FsContext2;
if (!fcb)
return FALSE;
FsRtlEnterFileSystem();
if (!ExAcquireResourceSharedLite(fcb->Header.Resource, wait)) {
FsRtlExitFileSystem();
return FALSE;
}
ads = fcb->ads;
if (ads) {
struct _fcb* fcb2;
if (!ccb || !ccb->fileref || !ccb->fileref->parent || !ccb->fileref->parent->fcb) {
ExReleaseResourceLite(fcb->Header.Resource);
FsRtlExitFileSystem();
return FALSE;
}
adssize = fcb->adsdata.Length;
fcb2 = ccb->fileref->parent->fcb;
ExReleaseResourceLite(fcb->Header.Resource);
fcb = fcb2;
if (!ExAcquireResourceSharedLite(fcb->Header.Resource, wait)) {
FsRtlExitFileSystem();
return FALSE;
}
fsi->AllocationSize.QuadPart = fsi->EndOfFile.QuadPart = adssize;
fsi->NumberOfLinks = fcb->inode_item.st_nlink;
fsi->Directory = S_ISDIR(fcb->inode_item.st_mode);
} else {
fsi->AllocationSize.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size);
fsi->EndOfFile.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : fcb->inode_item.st_size;
fsi->NumberOfLinks = fcb->inode_item.st_nlink;
fsi->Directory = S_ISDIR(fcb->inode_item.st_mode);
}
fsi->DeletePending = ccb->fileref ? ccb->fileref->delete_on_close : FALSE;
IoStatus->Status = STATUS_SUCCESS;
IoStatus->Information = sizeof(FILE_STANDARD_INFORMATION);
ExReleaseResourceLite(fcb->Header.Resource);
FsRtlExitFileSystem();
return TRUE;
}
static BOOLEAN STDCALL fast_io_query_open(PIRP Irp, PFILE_NETWORK_OPEN_INFORMATION NetworkInformation, PDEVICE_OBJECT DeviceObject) {

File diff suppressed because it is too large Load diff

View file

@ -26,16 +26,16 @@ static void do_flush(device_extension* Vcb) {
FsRtlEnterFileSystem();
acquire_tree_lock(Vcb, TRUE);
ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
if (Vcb->write_trees > 0)
if (Vcb->need_write)
do_write(Vcb, &rollback);
free_trees(Vcb);
clear_rollback(&rollback);
release_tree_lock(Vcb, TRUE);
ExReleaseResourceLite(&Vcb->tree_lock);
FsRtlExitFileSystem();
}
@ -44,28 +44,30 @@ void STDCALL flush_thread(void* context) {
DEVICE_OBJECT* devobj = context;
device_extension* Vcb = devobj->DeviceExtension;
LARGE_INTEGER due_time;
KTIMER flush_thread_timer;
ObReferenceObject(devobj);
KeInitializeTimer(&flush_thread_timer);
KeInitializeTimer(&Vcb->flush_thread_timer);
due_time.QuadPart = -INTERVAL * 10000;
KeSetTimer(&flush_thread_timer, due_time, NULL);
KeSetTimer(&Vcb->flush_thread_timer, due_time, NULL);
while (TRUE) {
KeWaitForSingleObject(&flush_thread_timer, Executive, KernelMode, FALSE, NULL);
KeWaitForSingleObject(&Vcb->flush_thread_timer, Executive, KernelMode, FALSE, NULL);
if (!(devobj->Vpb->Flags & VPB_MOUNTED))
if (!(devobj->Vpb->Flags & VPB_MOUNTED) || Vcb->removing)
break;
do_flush(Vcb);
KeSetTimer(&flush_thread_timer, due_time, NULL);
KeSetTimer(&Vcb->flush_thread_timer, due_time, NULL);
}
ObDereferenceObject(devobj);
KeCancelTimer(&flush_thread_timer);
KeCancelTimer(&Vcb->flush_thread_timer);
KeSetEvent(&Vcb->flush_thread_finished, 0, FALSE);
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

@ -174,14 +174,14 @@ static NTSTATUS pnp_query_remove_device(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
InitializeListHead(&rollback);
acquire_tree_lock(Vcb, TRUE);
ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
if (Vcb->write_trees > 0)
if (Vcb->need_write)
do_write(Vcb, &rollback);
clear_rollback(&rollback);
release_tree_lock(Vcb, TRUE);
ExReleaseResourceLite(&Vcb->tree_lock);
Status = STATUS_SUCCESS;
end:
@ -200,8 +200,13 @@ static NTSTATUS pnp_remove_device(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
}
if (DeviceObject->Vpb->Flags & VPB_MOUNTED) {
Status = FsRtlNotifyVolumeEvent(Vcb->root_file, FSRTL_VOLUME_DISMOUNT);
if (!NT_SUCCESS(Status)) {
WARN("FsRtlNotifyVolumeEvent returned %08x\n", Status);
}
uninit(Vcb, FALSE);
DeviceObject->Vpb->Flags &= ~VPB_MOUNTED;
Vcb->Vpb->Flags &= ~VPB_MOUNTED;
}
return STATUS_SUCCESS;
@ -229,6 +234,11 @@ NTSTATUS STDCALL drv_pnp(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
top_level = is_top_level(Irp);
if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
Status = part0_passthrough(DeviceObject, Irp);
goto end;
}
Status = STATUS_NOT_IMPLEMENTED;
switch (IrpSp->MinorFunction) {

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,667 @@
#include "btrfs_drv.h"
extern UNICODE_STRING log_device, log_file, registry_path;
static WCHAR option_mounted[] = L"Mounted";
static WCHAR option_ignore[] = L"Ignore";
#define hex_digit(c) ((c) >= 0 && (c) <= 9) ? ((c) + '0') : ((c) - 10 + 'a')
NTSTATUS registry_load_volume_options(BTRFS_UUID* uuid, mount_options* options) {
UNICODE_STRING path, ignoreus;
OBJECT_ATTRIBUTES oa;
NTSTATUS Status;
ULONG i, j, kvfilen, index, retlen;
KEY_VALUE_FULL_INFORMATION* kvfi = NULL;
HANDLE h;
path.Length = path.MaximumLength = registry_path.Length + (37 * sizeof(WCHAR));
path.Buffer = ExAllocatePoolWithTag(PagedPool, path.Length, ALLOC_TAG);
if (!path.Buffer) {
ERR("out of memory\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlCopyMemory(path.Buffer, registry_path.Buffer, registry_path.Length);
i = registry_path.Length / sizeof(WCHAR);
path.Buffer[i] = '\\';
i++;
for (j = 0; j < 16; j++) {
path.Buffer[i] = hex_digit((uuid->uuid[j] & 0xF0) >> 4);
path.Buffer[i+1] = hex_digit(uuid->uuid[j] & 0xF);
i += 2;
if (j == 3 || j == 5 || j == 7 || j == 9) {
path.Buffer[i] = '-';
i++;
}
}
kvfilen = sizeof(KEY_VALUE_FULL_INFORMATION) - sizeof(WCHAR) + (255 * sizeof(WCHAR));
kvfi = ExAllocatePoolWithTag(PagedPool, kvfilen, ALLOC_TAG);
if (!kvfi) {
ERR("out of memory\n");
Status = STATUS_INSUFFICIENT_RESOURCES;
goto end;
}
InitializeObjectAttributes(&oa, &path, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
Status = ZwOpenKey(&h, KEY_QUERY_VALUE, &oa);
if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
Status = STATUS_SUCCESS;
goto end;
} else if (!NT_SUCCESS(Status)) {
ERR("ZwOpenKey returned %08x\n", Status);
goto end;
}
index = 0;
ignoreus.Buffer = option_ignore;
ignoreus.Length = ignoreus.MaximumLength = wcslen(option_ignore) * sizeof(WCHAR);
do {
Status = ZwEnumerateValueKey(h, index, KeyValueFullInformation, kvfi, kvfilen, &retlen);
index++;
if (NT_SUCCESS(Status)) {
UNICODE_STRING us;
us.Length = us.MaximumLength = kvfi->NameLength;
us.Buffer = kvfi->Name;
if (FsRtlAreNamesEqual(&ignoreus, &us, TRUE, NULL) && kvfi->DataOffset > 0 && kvfi->DataLength > 0 && kvfi->Type == REG_DWORD) {
DWORD* val = (DWORD*)((UINT8*)kvfi + kvfi->DataOffset);
options->ignore = *val != 0 ? TRUE : FALSE;
}
} else if (Status != STATUS_NO_MORE_ENTRIES) {
ERR("ZwEnumerateValueKey returned %08x\n", Status);
goto end2;
}
} while (NT_SUCCESS(Status));
Status = STATUS_SUCCESS;
end2:
ZwClose(h);
end:
ExFreePool(path.Buffer);
if (kvfi)
ExFreePool(kvfi);
return Status;
}
NTSTATUS registry_mark_volume_mounted(BTRFS_UUID* uuid) {
UNICODE_STRING path, mountedus;
ULONG i, j;
NTSTATUS Status;
OBJECT_ATTRIBUTES oa;
HANDLE h;
DWORD data;
path.Length = path.MaximumLength = registry_path.Length + (37 * sizeof(WCHAR));
path.Buffer = ExAllocatePoolWithTag(PagedPool, path.Length, ALLOC_TAG);
if (!path.Buffer) {
ERR("out of memory\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlCopyMemory(path.Buffer, registry_path.Buffer, registry_path.Length);
i = registry_path.Length / sizeof(WCHAR);
path.Buffer[i] = '\\';
i++;
for (j = 0; j < 16; j++) {
path.Buffer[i] = hex_digit((uuid->uuid[j] & 0xF0) >> 4);
path.Buffer[i+1] = hex_digit(uuid->uuid[j] & 0xF);
i += 2;
if (j == 3 || j == 5 || j == 7 || j == 9) {
path.Buffer[i] = '-';
i++;
}
}
InitializeObjectAttributes(&oa, &path, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
Status = ZwCreateKey(&h, KEY_SET_VALUE, &oa, 0, NULL, REG_OPTION_NON_VOLATILE, NULL);
if (!NT_SUCCESS(Status)) {
ERR("ZwCreateKey returned %08x\n", Status);
goto end;
}
mountedus.Buffer = option_mounted;
mountedus.Length = mountedus.MaximumLength = wcslen(option_mounted) * sizeof(WCHAR);
data = 1;
Status = ZwSetValueKey(h, &mountedus, 0, REG_DWORD, &data, sizeof(DWORD));
if (!NT_SUCCESS(Status)) {
ERR("ZwSetValueKey returned %08x\n", Status);
goto end2;
}
Status = STATUS_SUCCESS;
end2:
ZwClose(h);
end:
ExFreePool(path.Buffer);
return Status;
}
static NTSTATUS registry_mark_volume_unmounted_path(PUNICODE_STRING path) {
HANDLE h;
OBJECT_ATTRIBUTES oa;
NTSTATUS Status;
ULONG index, kvbilen = sizeof(KEY_VALUE_BASIC_INFORMATION) - sizeof(WCHAR) + (255 * sizeof(WCHAR)), retlen;
KEY_VALUE_BASIC_INFORMATION* kvbi;
BOOL has_options = FALSE;
UNICODE_STRING mountedus;
// If a volume key has any options in it, we set Mounted to 0 and return. Otherwise,
// we delete the whole thing.
kvbi = ExAllocatePoolWithTag(PagedPool, kvbilen, ALLOC_TAG);
if (!kvbi) {
ERR("out of memory\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
InitializeObjectAttributes(&oa, path, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
Status = ZwOpenKey(&h, KEY_QUERY_VALUE | KEY_SET_VALUE | DELETE, &oa);
if (!NT_SUCCESS(Status)) {
ERR("ZwOpenKey returned %08x\n", Status);
goto end;
}
index = 0;
mountedus.Buffer = option_mounted;
mountedus.Length = mountedus.MaximumLength = wcslen(option_mounted) * sizeof(WCHAR);
do {
Status = ZwEnumerateValueKey(h, index, KeyValueBasicInformation, kvbi, kvbilen, &retlen);
index++;
if (NT_SUCCESS(Status)) {
UNICODE_STRING us;
us.Length = us.MaximumLength = kvbi->NameLength;
us.Buffer = kvbi->Name;
if (!FsRtlAreNamesEqual(&mountedus, &us, TRUE, NULL)) {
has_options = TRUE;
break;
}
} else if (Status != STATUS_NO_MORE_ENTRIES) {
ERR("ZwEnumerateValueKey returned %08x\n", Status);
goto end2;
}
} while (NT_SUCCESS(Status));
if (has_options) {
DWORD data = 0;
Status = ZwSetValueKey(h, &mountedus, 0, REG_DWORD, &data, sizeof(DWORD));
if (!NT_SUCCESS(Status)) {
ERR("ZwSetValueKey returned %08x\n", Status);
goto end2;
}
} else {
Status = ZwDeleteKey(h);
if (!NT_SUCCESS(Status)) {
ERR("ZwDeleteKey returned %08x\n", Status);
goto end2;
}
}
Status = STATUS_SUCCESS;
end2:
ZwClose(h);
end:
ExFreePool(kvbi);
return Status;
}
NTSTATUS registry_mark_volume_unmounted(BTRFS_UUID* uuid) {
UNICODE_STRING path;
NTSTATUS Status;
ULONG i, j;
path.Length = path.MaximumLength = registry_path.Length + (37 * sizeof(WCHAR));
path.Buffer = ExAllocatePoolWithTag(PagedPool, path.Length, ALLOC_TAG);
if (!path.Buffer) {
ERR("out of memory\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlCopyMemory(path.Buffer, registry_path.Buffer, registry_path.Length);
i = registry_path.Length / sizeof(WCHAR);
path.Buffer[i] = '\\';
i++;
for (j = 0; j < 16; j++) {
path.Buffer[i] = hex_digit((uuid->uuid[j] & 0xF0) >> 4);
path.Buffer[i+1] = hex_digit(uuid->uuid[j] & 0xF);
i += 2;
if (j == 3 || j == 5 || j == 7 || j == 9) {
path.Buffer[i] = '-';
i++;
}
}
Status = registry_mark_volume_unmounted_path(&path);
if (!NT_SUCCESS(Status)) {
ERR("registry_mark_volume_unmounted_path returned %08x\n", Status);
goto end;
}
Status = STATUS_SUCCESS;
end:
ExFreePool(path.Buffer);
return Status;
}
#define is_hex(c) ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))
static BOOL is_uuid(ULONG namelen, WCHAR* name) {
ULONG i;
if (namelen != 36 * sizeof(WCHAR))
return FALSE;
for (i = 0; i < 36; i++) {
if (i == 8 || i == 13 || i == 18 || i == 23) {
if (name[i] != '-')
return FALSE;
} else if (!is_hex(name[i]))
return FALSE;
}
return TRUE;
}
typedef struct {
UNICODE_STRING name;
LIST_ENTRY list_entry;
} key_name;
static void reset_subkeys(HANDLE h, PUNICODE_STRING reg_path) {
NTSTATUS Status;
KEY_BASIC_INFORMATION* kbi;
ULONG kbilen = sizeof(KEY_BASIC_INFORMATION) - sizeof(WCHAR) + (255 * sizeof(WCHAR)), retlen, index = 0;
LIST_ENTRY key_names, *le;
InitializeListHead(&key_names);
kbi = ExAllocatePoolWithTag(PagedPool, kbilen, ALLOC_TAG);
if (!kbi) {
ERR("out of memory\n");
return;
}
do {
Status = ZwEnumerateKey(h, index, KeyBasicInformation, kbi, kbilen, &retlen);
index++;
if (NT_SUCCESS(Status)) {
key_name* kn;
ERR("key: %.*S\n", kbi->NameLength / sizeof(WCHAR), kbi->Name);
if (is_uuid(kbi->NameLength, kbi->Name)) {
kn = ExAllocatePoolWithTag(PagedPool, sizeof(key_name), ALLOC_TAG);
if (!kn) {
ERR("out of memory\n");
goto end;
}
kn->name.Length = kn->name.MaximumLength = kbi->NameLength;
kn->name.Buffer = ExAllocatePoolWithTag(PagedPool, kn->name.Length, ALLOC_TAG);
if (!kn->name.Buffer) {
ERR("out of memory\n");
ExFreePool(kn);
goto end;
}
RtlCopyMemory(kn->name.Buffer, kbi->Name, kbi->NameLength);
InsertTailList(&key_names, &kn->list_entry);
}
} else if (Status != STATUS_NO_MORE_ENTRIES)
ERR("ZwEnumerateKey returned %08x\n", Status);
} while (NT_SUCCESS(Status));
le = key_names.Flink;
while (le != &key_names) {
key_name* kn = CONTAINING_RECORD(le, key_name, list_entry);
UNICODE_STRING path;
path.Length = path.MaximumLength = reg_path->Length + sizeof(WCHAR) + kn->name.Length;
path.Buffer = ExAllocatePoolWithTag(PagedPool, path.Length, ALLOC_TAG);
if (!path.Buffer) {
ERR("out of memory\n");
goto end;
}
RtlCopyMemory(path.Buffer, reg_path->Buffer, reg_path->Length);
path.Buffer[reg_path->Length / sizeof(WCHAR)] = '\\';
RtlCopyMemory(&path.Buffer[(reg_path->Length / sizeof(WCHAR)) + 1], kn->name.Buffer, kn->name.Length);
Status = registry_mark_volume_unmounted_path(&path);
if (!NT_SUCCESS(Status))
WARN("registry_mark_volume_unmounted_path returned %08x\n", Status);
ExFreePool(path.Buffer);
le = le->Flink;
}
end:
while (!IsListEmpty(&key_names)) {
key_name* kn;
le = RemoveHeadList(&key_names);
kn = CONTAINING_RECORD(le, key_name, list_entry);
if (kn->name.Buffer)
ExFreePool(kn->name.Buffer);
ExFreePool(kn);
}
ExFreePool(kbi);
}
static void read_mappings(PUNICODE_STRING regpath) {
WCHAR* path;
UNICODE_STRING us;
HANDLE h;
OBJECT_ATTRIBUTES oa;
ULONG dispos;
NTSTATUS Status;
ULONG kvfilen, retlen, i;
KEY_VALUE_FULL_INFORMATION* kvfi;
const WCHAR mappings[] = L"\\Mappings";
path = ExAllocatePoolWithTag(PagedPool, regpath->Length + (wcslen(mappings) * sizeof(WCHAR)), ALLOC_TAG);
if (!path) {
ERR("out of memory\n");
return;
}
RtlCopyMemory(path, regpath->Buffer, regpath->Length);
RtlCopyMemory((UINT8*)path + regpath->Length, mappings, wcslen(mappings) * sizeof(WCHAR));
us.Buffer = path;
us.Length = us.MaximumLength = regpath->Length + ((USHORT)wcslen(mappings) * sizeof(WCHAR));
InitializeObjectAttributes(&oa, &us, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
// FIXME - keep open and do notify for changes
Status = ZwCreateKey(&h, KEY_QUERY_VALUE, &oa, 0, NULL, REG_OPTION_NON_VOLATILE, &dispos);
if (!NT_SUCCESS(Status)) {
ERR("ZwCreateKey returned %08x\n", Status);
ExFreePool(path);
return;
}
if (dispos == REG_OPENED_EXISTING_KEY) {
kvfilen = sizeof(KEY_VALUE_FULL_INFORMATION) + 256;
kvfi = ExAllocatePoolWithTag(PagedPool, kvfilen, ALLOC_TAG);
if (!kvfi) {
ERR("out of memory\n");
ExFreePool(path);
ZwClose(h);
return;
}
i = 0;
do {
Status = ZwEnumerateValueKey(h, i, KeyValueFullInformation, kvfi, kvfilen, &retlen);
if (NT_SUCCESS(Status) && kvfi->DataLength > 0) {
UINT32 val = 0;
RtlCopyMemory(&val, (UINT8*)kvfi + kvfi->DataOffset, min(kvfi->DataLength, sizeof(UINT32)));
TRACE("entry %u = %.*S = %u\n", i, kvfi->NameLength / sizeof(WCHAR), kvfi->Name, val);
add_user_mapping(kvfi->Name, kvfi->NameLength / sizeof(WCHAR), val);
}
i = i + 1;
} while (Status != STATUS_NO_MORE_ENTRIES);
}
ZwClose(h);
ExFreePool(path);
}
void STDCALL read_registry(PUNICODE_STRING regpath) {
#ifndef __REACTOS__
UNICODE_STRING us;
#endif
OBJECT_ATTRIBUTES oa;
NTSTATUS Status;
HANDLE h;
ULONG dispos;
#ifndef __REACTOS__
ULONG kvfilen;
KEY_VALUE_FULL_INFORMATION* kvfi;
#endif
#ifndef __REACTOS__
static WCHAR def_log_file[] = L"\\??\\C:\\btrfs.log";
#endif
read_mappings(regpath);
InitializeObjectAttributes(&oa, regpath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
Status = ZwCreateKey(&h, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &oa, 0, NULL, REG_OPTION_NON_VOLATILE, &dispos);
if (!NT_SUCCESS(Status)) {
ERR("ZwCreateKey returned %08x\n", Status);
return;
}
reset_subkeys(h, regpath);
#ifdef _DEBUG
RtlInitUnicodeString(&us, L"DebugLogLevel");
kvfi = NULL;
kvfilen = 0;
Status = ZwQueryValueKey(h, &us, KeyValueFullInformation, kvfi, kvfilen, &kvfilen);
if ((Status == STATUS_BUFFER_TOO_SMALL || Status == STATUS_BUFFER_OVERFLOW) && kvfilen > 0) {
kvfi = ExAllocatePoolWithTag(PagedPool, kvfilen, ALLOC_TAG);
if (!kvfi) {
ERR("out of memory\n");
ZwClose(h);
return;
}
Status = ZwQueryValueKey(h, &us, KeyValueFullInformation, kvfi, kvfilen, &kvfilen);
if (NT_SUCCESS(Status)) {
if (kvfi->Type == REG_DWORD && kvfi->DataLength >= sizeof(UINT32)) {
RtlCopyMemory(&debug_log_level, ((UINT8*)kvfi) + kvfi->DataOffset, sizeof(UINT32));
} else {
Status = ZwDeleteValueKey(h, &us);
if (!NT_SUCCESS(Status)) {
ERR("ZwDeleteValueKey returned %08x\n", Status);
}
Status = ZwSetValueKey(h, &us, 0, REG_DWORD, &debug_log_level, sizeof(debug_log_level));
if (!NT_SUCCESS(Status)) {
ERR("ZwSetValueKey reutrned %08x\n", Status);
}
}
}
ExFreePool(kvfi);
} else if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
Status = ZwSetValueKey(h, &us, 0, REG_DWORD, &debug_log_level, sizeof(debug_log_level));
if (!NT_SUCCESS(Status)) {
ERR("ZwSetValueKey reutrned %08x\n", Status);
}
} else {
ERR("ZwQueryValueKey returned %08x\n", Status);
}
RtlInitUnicodeString(&us, L"LogDevice");
kvfi = NULL;
kvfilen = 0;
Status = ZwQueryValueKey(h, &us, KeyValueFullInformation, kvfi, kvfilen, &kvfilen);
if ((Status == STATUS_BUFFER_TOO_SMALL || Status == STATUS_BUFFER_OVERFLOW) && kvfilen > 0) {
kvfi = ExAllocatePoolWithTag(PagedPool, kvfilen, ALLOC_TAG);
if (!kvfi) {
ERR("out of memory\n");
ZwClose(h);
return;
}
Status = ZwQueryValueKey(h, &us, KeyValueFullInformation, kvfi, kvfilen, &kvfilen);
if (NT_SUCCESS(Status)) {
if ((kvfi->Type == REG_SZ || kvfi->Type == REG_EXPAND_SZ) && kvfi->DataLength >= sizeof(WCHAR)) {
log_device.Length = log_device.MaximumLength = kvfi->DataLength;
log_device.Buffer = ExAllocatePoolWithTag(PagedPool, kvfi->DataLength, ALLOC_TAG);
if (!log_device.Buffer) {
ERR("out of memory\n");
ExFreePool(kvfi);
ZwClose(h);
return;
}
RtlCopyMemory(log_device.Buffer, ((UINT8*)kvfi) + kvfi->DataOffset, kvfi->DataLength);
if (log_device.Buffer[(log_device.Length / sizeof(WCHAR)) - 1] == 0)
log_device.Length -= sizeof(WCHAR);
} else {
ERR("LogDevice was type %u, length %u\n", kvfi->Type, kvfi->DataLength);
Status = ZwDeleteValueKey(h, &us);
if (!NT_SUCCESS(Status)) {
ERR("ZwDeleteValueKey returned %08x\n", Status);
}
}
}
ExFreePool(kvfi);
} else if (Status != STATUS_OBJECT_NAME_NOT_FOUND) {
ERR("ZwQueryValueKey returned %08x\n", Status);
}
RtlInitUnicodeString(&us, L"LogFile");
kvfi = NULL;
kvfilen = 0;
Status = ZwQueryValueKey(h, &us, KeyValueFullInformation, kvfi, kvfilen, &kvfilen);
if ((Status == STATUS_BUFFER_TOO_SMALL || Status == STATUS_BUFFER_OVERFLOW) && kvfilen > 0) {
kvfi = ExAllocatePoolWithTag(PagedPool, kvfilen, ALLOC_TAG);
if (!kvfi) {
ERR("out of memory\n");
ZwClose(h);
return;
}
Status = ZwQueryValueKey(h, &us, KeyValueFullInformation, kvfi, kvfilen, &kvfilen);
if (NT_SUCCESS(Status)) {
if ((kvfi->Type == REG_SZ || kvfi->Type == REG_EXPAND_SZ) && kvfi->DataLength >= sizeof(WCHAR)) {
log_file.Length = log_file.MaximumLength = kvfi->DataLength;
log_file.Buffer = ExAllocatePoolWithTag(PagedPool, kvfi->DataLength, ALLOC_TAG);
if (!log_file.Buffer) {
ERR("out of memory\n");
ExFreePool(kvfi);
ZwClose(h);
return;
}
RtlCopyMemory(log_file.Buffer, ((UINT8*)kvfi) + kvfi->DataOffset, kvfi->DataLength);
if (log_file.Buffer[(log_file.Length / sizeof(WCHAR)) - 1] == 0)
log_file.Length -= sizeof(WCHAR);
} else {
ERR("LogFile was type %u, length %u\n", kvfi->Type, kvfi->DataLength);
Status = ZwDeleteValueKey(h, &us);
if (!NT_SUCCESS(Status)) {
ERR("ZwDeleteValueKey returned %08x\n", Status);
}
}
}
ExFreePool(kvfi);
} else if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
Status = ZwSetValueKey(h, &us, 0, REG_SZ, def_log_file, (wcslen(def_log_file) + 1) * sizeof(WCHAR));
if (!NT_SUCCESS(Status)) {
ERR("ZwSetValueKey returned %08x\n", Status);
}
} else {
ERR("ZwQueryValueKey returned %08x\n", Status);
}
if (log_file.Length == 0) {
log_file.Length = log_file.MaximumLength = wcslen(def_log_file) * sizeof(WCHAR);
log_file.Buffer = ExAllocatePoolWithTag(PagedPool, log_file.MaximumLength, ALLOC_TAG);
if (!log_file.Buffer) {
ERR("out of memory\n");
ZwClose(h);
return;
}
RtlCopyMemory(log_file.Buffer, def_log_file, log_file.Length);
}
#endif
ZwClose(h);
}

View file

@ -30,7 +30,8 @@ NTSTATUS get_reparse_point(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject,
TRACE("(%p, %p, %p, %x, %p)\n", DeviceObject, FileObject, buffer, buflen, retlen);
acquire_tree_lock(fcb->Vcb, FALSE);
ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, TRUE);
ExAcquireResourceSharedLite(fcb->Header.Resource, TRUE);
if (fcb->type == BTRFS_TYPE_SYMLINK) {
data = ExAllocatePoolWithTag(PagedPool, fcb->inode_item.st_size, ALLOC_TAG);
@ -41,7 +42,7 @@ NTSTATUS get_reparse_point(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject,
}
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);
Status = read_file(fcb, (UINT8*)data, 0, fcb->inode_item.st_size, NULL, NULL);
if (!NT_SUCCESS(Status)) {
ERR("read_file returned %08x\n", Status);
@ -101,38 +102,22 @@ NTSTATUS get_reparse_point(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject,
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);
Status = read_file(fcb, buffer, 0, buflen, retlen, NULL);
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);
if (!fcb->reparse_xattr.Buffer || fcb->reparse_xattr.Length < sizeof(ULONG)) {
Status = STATUS_NOT_A_REPARSE_POINT;
goto end;
}
if (buflen > 0) {
*retlen = min(buflen, datalen);
RtlCopyMemory(buffer, data, *retlen);
*retlen = min(buflen, fcb->reparse_xattr.Length);
RtlCopyMemory(buffer, fcb->reparse_xattr.Buffer, *retlen);
} else
*retlen = 0;
ExFreePool(data);
} else
Status = STATUS_NOT_A_REPARSE_POINT;
} else {
@ -140,115 +125,19 @@ NTSTATUS get_reparse_point(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject,
}
end:
release_tree_lock(fcb->Vcb, FALSE);
ExReleaseResourceLite(fcb->Header.Resource);
ExReleaseResourceLite(&fcb->Vcb->tree_lock);
return Status;
}
static NTSTATUS change_file_type(device_extension* Vcb, UINT64 inode, root* subvol, UINT64 parinode, UINT64 index, PANSI_STRING utf8, UINT8 type, LIST_ENTRY* rollback) {
KEY searchkey;
UINT32 crc32;
traverse_ptr tp;
NTSTATUS Status;
crc32 = calc_crc32c(0xfffffffe, (UINT8*)utf8->Buffer, (ULONG)utf8->Length);
searchkey.obj_id = parinode;
searchkey.obj_type = TYPE_DIR_ITEM;
searchkey.offset = crc32;
Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
}
if (!keycmp(&tp.item->key, &searchkey)) {
if (tp.item->size < sizeof(DIR_ITEM)) {
ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM));
} else {
DIR_ITEM *di = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG), *di2;
BOOL found = FALSE;
ULONG len = tp.item->size;
if (!di) {
ERR("out of memory\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlCopyMemory(di, tp.item->data, tp.item->size);
di2 = di;
do {
if (len < sizeof(DIR_ITEM) || len < sizeof(DIR_ITEM) - 1 + di2->m + di2->n) {
ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
break;
}
if (di2->n == utf8->Length && RtlCompareMemory(di2->name, utf8->Buffer, utf8->Length) == utf8->Length) {
di2->type = type;
found = TRUE;
break;
}
if (len > sizeof(DIR_ITEM) - sizeof(char) + di2->m + di2->n) {
len -= sizeof(DIR_ITEM) - sizeof(char) + di2->m + di2->n;
di2 = (DIR_ITEM*)&di2->name[di2->m + di2->n];
} else
break;
} while (len > 0);
if (found) {
delete_tree_item(Vcb, &tp, rollback);
insert_tree_item(Vcb, subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, di, tp.item->size, NULL, rollback);
} else
ExFreePool(di);
}
} else {
WARN("search for DIR_ITEM by crc32 failed\n");
}
searchkey.obj_id = parinode;
searchkey.obj_type = TYPE_DIR_INDEX;
searchkey.offset = index;
Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
}
if (!keycmp(&tp.item->key, &searchkey)) {
if (tp.item->size < sizeof(DIR_ITEM)) {
ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM));
} else {
DIR_ITEM* di = (DIR_ITEM*)tp.item->data;
DIR_ITEM* di2 = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
if (!di2) {
ERR("out of memory\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlCopyMemory(di2, di, tp.item->size);
di2->type = type;
delete_tree_item(Vcb, &tp, rollback);
insert_tree_item(Vcb, subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, di2, tp.item->size, NULL, rollback);
}
}
return STATUS_SUCCESS;
}
static NTSTATUS set_symlink(PIRP Irp, fcb* fcb, REPARSE_DATA_BUFFER* rdb, ULONG buflen, LIST_ENTRY* rollback) {
static NTSTATUS set_symlink(PIRP Irp, file_ref* fileref, REPARSE_DATA_BUFFER* rdb, ULONG buflen, LIST_ENTRY* rollback) {
NTSTATUS Status;
ULONG minlen;
UNICODE_STRING subname;
ANSI_STRING target;
KEY searchkey;
traverse_ptr tp, next_tp;
BOOL b;
LARGE_INTEGER offset;
LARGE_INTEGER offset, time;
BTRFS_TIME now;
USHORT i;
minlen = offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) + sizeof(WCHAR);
@ -260,125 +149,13 @@ static NTSTATUS set_symlink(PIRP Irp, fcb* fcb, REPARSE_DATA_BUFFER* rdb, ULONG
subname.Buffer = &rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)];
subname.MaximumLength = subname.Length = rdb->SymbolicLinkReparseBuffer.SubstituteNameLength;
ERR("substitute name = %.*S\n", subname.Length / sizeof(WCHAR), subname.Buffer);
TRACE("substitute name = %.*S\n", subname.Length / sizeof(WCHAR), subname.Buffer);
fcb->type = BTRFS_TYPE_SYMLINK;
fileref->fcb->type = BTRFS_TYPE_SYMLINK;
searchkey.obj_id = fcb->inode;
searchkey.obj_type = TYPE_INODE_REF;
searchkey.offset = 0;
fileref->fcb->inode_item.st_mode |= __S_IFLNK;
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_SYMLINK, 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_SYMLINK, 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);
}
fcb->inode_item.st_mode |= __S_IFLNK;
Status = truncate_file(fcb, 0, rollback);
Status = truncate_file(fileref->fcb, 0, rollback);
if (!NT_SUCCESS(Status)) {
ERR("truncate_file returned %08x\n", Status);
return Status;
@ -386,7 +163,7 @@ static NTSTATUS set_symlink(PIRP Irp, fcb* fcb, REPARSE_DATA_BUFFER* rdb, ULONG
Status = RtlUnicodeToUTF8N(NULL, 0, (PULONG)&target.Length, subname.Buffer, subname.Length);
if (!NT_SUCCESS(Status)) {
ERR("RtlUnicodeToUTF8N 3 failed with error %08x\n", Status);
ERR("RtlUnicodeToUTF8N 1 failed with error %08x\n", Status);
return Status;
}
@ -399,7 +176,7 @@ static NTSTATUS set_symlink(PIRP Irp, fcb* fcb, REPARSE_DATA_BUFFER* rdb, ULONG
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);
ERR("RtlUnicodeToUTF8N 2 failed with error %08x\n", Status);
ExFreePool(target.Buffer);
return Status;
}
@ -410,159 +187,23 @@ static NTSTATUS set_symlink(PIRP Irp, fcb* fcb, REPARSE_DATA_BUFFER* rdb, ULONG
}
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(fileref->fcb->Vcb, Irp, offset, target.Buffer, (ULONG*)&target.Length, FALSE, TRUE,
TRUE, FALSE, 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);
fileref->fcb->inode_item.transid = fileref->fcb->Vcb->superblock.generation;
fileref->fcb->inode_item.sequence++;
fileref->fcb->inode_item.st_ctime = now;
fileref->fcb->inode_item.st_mtime = 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;
fileref->fcb->subvol->root_item.ctransid = fileref->fcb->Vcb->superblock.generation;
fileref->fcb->subvol->root_item.ctime = now;
mark_fcb_dirty(fileref->fcb);
mark_fileref_dirty(fileref);
return Status;
}
@ -575,6 +216,8 @@ NTSTATUS set_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
DWORD buflen = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
NTSTATUS Status = STATUS_SUCCESS;
fcb* fcb;
ccb* ccb;
file_ref* fileref;
ULONG tag;
LIST_ENTRY rollback;
@ -591,10 +234,24 @@ NTSTATUS set_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
}
fcb = FileObject->FsContext;
ccb = FileObject->FsContext2;
if (!ccb) {
ERR("ccb was NULL\n");
return STATUS_INVALID_PARAMETER;
}
fileref = ccb->fileref;
if (!fileref) {
ERR("fileref was NULL\n");
return STATUS_INVALID_PARAMETER;
}
TRACE("%S\n", file_desc(FileObject));
acquire_tree_lock(fcb->Vcb, TRUE);
ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, TRUE);
ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
if (fcb->type == BTRFS_TYPE_SYMLINK) {
WARN("tried to set a reparse point on an existing symlink\n");
@ -616,17 +273,32 @@ NTSTATUS set_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
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);
Status = set_symlink(Irp, fileref, rdb, buflen, &rollback);
fcb->atts |= FILE_ATTRIBUTE_REPARSE_POINT;
} else {
LARGE_INTEGER offset;
char val[64];
LARGE_INTEGER offset, time;
BTRFS_TIME now;
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);
ANSI_STRING buf;
buf.Buffer = ExAllocatePoolWithTag(PagedPool, buflen, ALLOC_TAG);
if (!buf.Buffer) {
ERR("out of memory\n");
Status = STATUS_INSUFFICIENT_RESOURCES;
goto end;
}
buf.Length = buf.MaximumLength = buflen;
if (fcb->reparse_xattr.Buffer)
ExFreePool(fcb->reparse_xattr.Buffer);
fcb->reparse_xattr = buf;
RtlCopyMemory(fcb->reparse_xattr.Buffer, buffer, buflen);
fcb->reparse_xattr_changed = TRUE;
Status = STATUS_SUCCESS;
} else { // otherwise, store as file data
Status = truncate_file(fcb, 0, &rollback);
if (!NT_SUCCESS(Status)) {
@ -636,26 +308,30 @@ NTSTATUS set_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
offset.QuadPart = 0;
Status = write_file2(fcb->Vcb, Irp, offset, buffer, &buflen, Irp->Flags & IRP_PAGING_IO, Irp->Flags & IRP_NOCACHE, &rollback);
Status = write_file2(fcb->Vcb, Irp, offset, buffer, &buflen, FALSE, TRUE, TRUE, FALSE, &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;
}
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;
fcb->atts |= FILE_ATTRIBUTE_REPARSE_POINT;
fcb->atts_changed = TRUE;
fcb->subvol->root_item.ctransid = fcb->Vcb->superblock.generation;
fcb->subvol->root_item.ctime = now;
mark_fcb_dirty(fcb);
}
if (NT_SUCCESS(Status))
Status = consider_write(fcb->Vcb);
send_notification_fcb(fileref, FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_ATTRIBUTES, FILE_ACTION_MODIFIED);
end:
if (NT_SUCCESS(Status))
@ -663,7 +339,8 @@ end:
else
do_rollback(fcb->Vcb, &rollback);
release_tree_lock(fcb->Vcb, TRUE);
ExReleaseResourceLite(fcb->Header.Resource);
ExReleaseResourceLite(&fcb->Vcb->tree_lock);
return Status;
}
@ -679,7 +356,6 @@ NTSTATUS delete_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
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);
@ -695,25 +371,29 @@ NTSTATUS delete_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
ccb = FileObject->FsContext2;
fileref = ccb ? ccb->fileref : NULL;
ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, TRUE);
ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
TRACE("%S\n", file_desc(FileObject));
if (!fileref) {
ERR("fileref was NULL\n");
return STATUS_INVALID_PARAMETER;
Status = STATUS_INVALID_PARAMETER;
goto end;
}
if (buflen < offsetof(REPARSE_DATA_BUFFER, GenericReparseBuffer.DataBuffer)) {
ERR("buffer was too short\n");
return STATUS_INVALID_PARAMETER;
Status = STATUS_INVALID_PARAMETER;
goto end;
}
if (rdb->ReparseDataLength > 0) {
WARN("rdb->ReparseDataLength was not zero\n");
return STATUS_INVALID_PARAMETER;
Status = STATUS_INVALID_PARAMETER;
goto end;
}
acquire_tree_lock(fcb->Vcb, TRUE);
if (fcb->ads) {
WARN("tried to delete reparse point on ADS\n");
Status = STATUS_INVALID_PARAMETER;
@ -721,21 +401,35 @@ NTSTATUS delete_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
}
if (fcb->type == BTRFS_TYPE_SYMLINK) {
LARGE_INTEGER time;
BTRFS_TIME now;
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;
}
KeQuerySystemTime(&time);
win_time_to_unix(time, &now);
fileref->fcb->type = BTRFS_TYPE_FILE;
fileref->fcb->inode_item.st_mode &= ~__S_IFLNK;
fileref->fcb->inode_item.st_mode |= __S_IFREG;
fileref->fcb->inode_item.transid = fileref->fcb->Vcb->superblock.generation;
fileref->fcb->inode_item.sequence++;
fileref->fcb->inode_item.st_ctime = now;
fileref->fcb->inode_item.st_mtime = now;
fileref->fcb->atts &= ~FILE_ATTRIBUTE_REPARSE_POINT;
mark_fileref_dirty(fileref);
mark_fcb_dirty(fileref->fcb);
fileref->fcb->subvol->root_item.ctransid = fcb->Vcb->superblock.generation;
fileref->fcb->subvol->root_item.ctime = now;
} 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?
@ -746,24 +440,9 @@ NTSTATUS delete_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
}
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);
fcb->atts_changed = TRUE;
KeQuerySystemTime(&time);
win_time_to_unix(time, &now);
fcb->inode_item.transid = fcb->Vcb->superblock.generation;
@ -771,55 +450,35 @@ NTSTATUS delete_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
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;
}
mark_fcb_dirty(fcb);
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;
fcb->atts_changed = TRUE;
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 (fcb->reparse_xattr.Buffer) {
ExFreePool(fcb->reparse_xattr.Buffer);
fcb->reparse_xattr.Buffer = NULL;
}
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);
fcb->reparse_xattr_changed = TRUE;
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;
}
mark_fcb_dirty(fcb);
fcb->subvol->root_item.ctransid = fcb->Vcb->superblock.generation;
fcb->subvol->root_item.ctime = now;
@ -831,16 +490,16 @@ NTSTATUS delete_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
Status = STATUS_SUCCESS;
if (NT_SUCCESS(Status))
Status = consider_write(fcb->Vcb);
send_notification_fcb(fileref, FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_ATTRIBUTES, FILE_ACTION_MODIFIED);
end:
if (NT_SUCCESS(Status))
clear_rollback(&rollback);
else
do_rollback(fcb->Vcb, &rollback);
release_tree_lock(fcb->Vcb, TRUE);
ExReleaseResourceLite(fcb->Header.Resource);
ExReleaseResourceLite(&fcb->Vcb->tree_lock);
return Status;
}

View file

@ -53,6 +53,63 @@ VOID WINAPI IopNotifyPlugPlayNotification(
static const WCHAR devpath[] = {'\\','D','e','v','i','c','e',0};
static NTSTATUS create_part0(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT DeviceObject, PUNICODE_STRING pardir, PUNICODE_STRING nameus,
BTRFS_UUID* uuid) {
PDEVICE_OBJECT newdevobj;
UNICODE_STRING name;
NTSTATUS Status;
part0_device_extension* p0de;
static const WCHAR btrfs_partition[] = L"\\BtrfsPartition";
name.Length = name.MaximumLength = pardir->Length + (wcslen(btrfs_partition) * sizeof(WCHAR));
name.Buffer = ExAllocatePoolWithTag(PagedPool, name.Length, ALLOC_TAG);
if (!name.Buffer) {
ERR("out of memory\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlCopyMemory(name.Buffer, pardir->Buffer, pardir->Length);
RtlCopyMemory(&name.Buffer[pardir->Length / sizeof(WCHAR)], btrfs_partition, wcslen(btrfs_partition) * sizeof(WCHAR));
Status = IoCreateDevice(DriverObject, sizeof(part0_device_extension), &name, FILE_DEVICE_DISK, FILE_DEVICE_SECURE_OPEN, FALSE, &newdevobj);
if (!NT_SUCCESS(Status)) {
ERR("IoCreateDevice returned %08x\n", Status);
ExFreePool(name.Buffer);
return Status;
}
p0de = newdevobj->DeviceExtension;
p0de->type = VCB_TYPE_PARTITION0;
p0de->devobj = DeviceObject;
RtlCopyMemory(&p0de->uuid, uuid, sizeof(BTRFS_UUID));
p0de->name.Length = name.Length;
p0de->name.MaximumLength = name.MaximumLength;
p0de->name.Buffer = ExAllocatePoolWithTag(PagedPool, p0de->name.MaximumLength, ALLOC_TAG);
if (!p0de->name.Buffer) {
ERR("out of memory\b");
ExFreePool(name.Buffer);
ExFreePool(p0de->name.Buffer);
IoDeleteDevice(newdevobj);
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlCopyMemory(p0de->name.Buffer, name.Buffer, name.Length);
ObReferenceObject(DeviceObject);
newdevobj->StackSize = DeviceObject->StackSize + 1;
newdevobj->Flags |= DO_DIRECT_IO;
newdevobj->Flags &= ~DO_DEVICE_INITIALIZING;
*nameus = name;
return STATUS_SUCCESS;
}
static void STDCALL add_volume(PDEVICE_OBJECT mountmgr, PUNICODE_STRING us) {
ULONG tnsize;
MOUNTMGR_TARGET_NAME* tn;
@ -81,7 +138,8 @@ static void STDCALL add_volume(PDEVICE_OBJECT mountmgr, PUNICODE_STRING us) {
mountmgr, tn, tnsize,
NULL, 0, FALSE, &Event, &IoStatusBlock);
if (!Irp) {
ERR("IoBuildDeviceIoControlRequest failed\n");
ERR("%.*S: IoBuildDeviceIoControlRequest 1 failed\n", us->Length / sizeof(WCHAR), us->Buffer);
ExFreePool(tn);
return;
}
@ -91,8 +149,10 @@ static void STDCALL add_volume(PDEVICE_OBJECT mountmgr, PUNICODE_STRING us) {
Status = IoStatusBlock.Status;
}
if (!NT_SUCCESS(Status))
ERR("IoCallDriver (1) returned %08x\n", Status);
if (!NT_SUCCESS(Status)) {
ERR("%.*S: IoCallDriver 1 returned %08x\n", us->Length / sizeof(WCHAR), us->Buffer, Status);
return;
}
ExFreePool(tn);
@ -113,7 +173,7 @@ static void STDCALL add_volume(PDEVICE_OBJECT mountmgr, PUNICODE_STRING us) {
mountmgr, mmdlt, mmdltsize,
&mmdli, sizeof(MOUNTMGR_DRIVE_LETTER_INFORMATION), FALSE, &Event, &IoStatusBlock);
if (!Irp) {
ERR("IoBuildDeviceIoControlRequest failed\n");
ERR("%.*S: IoBuildDeviceIoControlRequest 2 failed\n", us->Length / sizeof(WCHAR), us->Buffer);
return;
}
@ -123,15 +183,15 @@ static void STDCALL add_volume(PDEVICE_OBJECT mountmgr, PUNICODE_STRING us) {
Status = IoStatusBlock.Status;
}
if (!NT_SUCCESS(Status))
ERR("IoCallDriver (2) returned %08x\n", Status);
else
if (!NT_SUCCESS(Status)) {
ERR("%.*S: IoCallDriver 2 returned %08x\n", us->Length / sizeof(WCHAR), us->Buffer, Status);
} else
TRACE("DriveLetterWasAssigned = %u, CurrentDriveLetter = %c\n", mmdli.DriveLetterWasAssigned, mmdli.CurrentDriveLetter);
ExFreePool(mmdlt);
}
static void STDCALL test_vol(PDEVICE_OBJECT mountmgr, PUNICODE_STRING us, LIST_ENTRY* volumes) {
static void STDCALL test_vol(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT mountmgr, PUNICODE_STRING pardir, PUNICODE_STRING us, BOOL part0, LIST_ENTRY* volumes) {
KEVENT Event;
PIRP Irp;
IO_STATUS_BLOCK IoStatusBlock;
@ -143,10 +203,11 @@ static void STDCALL test_vol(PDEVICE_OBJECT mountmgr, PUNICODE_STRING us, LIST_E
UINT8* data;
UNICODE_STRING us2;
BOOL added_entry = FALSE;
UINT32 sector_size;
TRACE("%.*S\n", us->Length / sizeof(WCHAR), us->Buffer);
us2.Length = ((wcslen(devpath) + 1) * sizeof(WCHAR)) + us->Length;
us2.Length = pardir->Length + sizeof(WCHAR) + us->Length;
us2.MaximumLength = us2.Length;
us2.Buffer = ExAllocatePoolWithTag(PagedPool, us2.Length, ALLOC_TAG);
if (!us2.Buffer) {
@ -154,9 +215,9 @@ static void STDCALL test_vol(PDEVICE_OBJECT mountmgr, PUNICODE_STRING us, LIST_E
return;
}
RtlCopyMemory(us2.Buffer, devpath, wcslen(devpath) * sizeof(WCHAR));
us2.Buffer[wcslen(devpath)] = '\\';
RtlCopyMemory(&us2.Buffer[wcslen(devpath)+1], us->Buffer, us->Length);
RtlCopyMemory(us2.Buffer, pardir->Buffer, pardir->Length);
us2.Buffer[pardir->Length / sizeof(WCHAR)] = '\\';
RtlCopyMemory(&us2.Buffer[(pardir->Length / sizeof(WCHAR))+1], us->Buffer, us->Length);
TRACE("%.*S\n", us2.Length / sizeof(WCHAR), us2.Buffer);
@ -165,12 +226,40 @@ static void STDCALL test_vol(PDEVICE_OBJECT mountmgr, PUNICODE_STRING us, LIST_E
ERR("IoGetDeviceObjectPointer returned %08x\n", Status);
goto exit;
}
sector_size = DeviceObject->SectorSize;
if (sector_size == 0) {
DISK_GEOMETRY geometry;
IO_STATUS_BLOCK iosb;
Status = dev_ioctl(DeviceObject, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0,
&geometry, sizeof(DISK_GEOMETRY), TRUE, &iosb);
if (!NT_SUCCESS(Status)) {
ERR("%.*S had a sector size of 0, and IOCTL_DISK_GET_DRIVE_GEOMETRY returned %08x\n",
us2.Length / sizeof(WCHAR), us2.Buffer, Status);
goto exit;
}
if (iosb.Information < sizeof(DISK_GEOMETRY)) {
ERR("%.*S: IOCTL_DISK_GET_DRIVE_GEOMETRY returned %u bytes, expected %u\n",
us2.Length / sizeof(WCHAR), us2.Buffer, iosb.Information, sizeof(DISK_GEOMETRY));
}
sector_size = geometry.BytesPerSector;
if (sector_size == 0) {
ERR("%.*S had a sector size of 0\n", us2.Length / sizeof(WCHAR), us2.Buffer);
goto exit;
}
}
KeInitializeEvent(&Event, NotificationEvent, FALSE);
Offset.QuadPart = superblock_addrs[0];
toread = sector_align(sizeof(superblock), DeviceObject->SectorSize);
toread = sector_align(sizeof(superblock), sector_size);
data = ExAllocatePoolWithTag(NonPagedPool, toread, ALLOC_TAG);
if (!data) {
ERR("out of memory\n");
@ -192,22 +281,85 @@ static void STDCALL test_vol(PDEVICE_OBJECT mountmgr, PUNICODE_STRING us, LIST_E
}
if (NT_SUCCESS(Status) && IoStatusBlock.Information > 0 && ((superblock*)data)->magic == BTRFS_MAGIC) {
int i;
GET_LENGTH_INFORMATION gli;
superblock* sb = (superblock*)data;
volume* v = ExAllocatePoolWithTag(PagedPool, sizeof(volume), ALLOC_TAG);
if (!v) {
ERR("out of memory\n");
goto deref;
}
v->devobj = DeviceObject;
Status = dev_ioctl(DeviceObject, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0,
&gli, sizeof(gli), TRUE, NULL);
if (!NT_SUCCESS(Status)) {
ERR("error reading length information: %08x\n", Status);
goto deref;
}
if (part0) {
UNICODE_STRING us3;
Status = create_part0(DriverObject, DeviceObject, pardir, &us3, &sb->dev_item.device_uuid);
if (!NT_SUCCESS(Status)) {
ERR("create_part0 returned %08x\n", Status);
goto deref;
}
ExFreePool(us2.Buffer);
us2 = us3;
}
RtlCopyMemory(&v->fsuuid, &sb->uuid, sizeof(BTRFS_UUID));
RtlCopyMemory(&v->devuuid, &sb->dev_item.device_uuid, sizeof(BTRFS_UUID));
v->devnum = sb->dev_item.dev_id;
v->devpath = us2;
v->processed = FALSE;
v->length = gli.Length.QuadPart;
v->gen1 = sb->generation;
v->gen2 = 0;
InsertTailList(volumes, &v->list_entry);
i = 1;
while (superblock_addrs[i] != 0 && superblock_addrs[i] + toread <= v->length) {
KeInitializeEvent(&Event, NotificationEvent, FALSE);
Offset.QuadPart = superblock_addrs[i];
Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ, DeviceObject, data, toread, &Offset, &Event, &IoStatusBlock);
if (!Irp) {
ERR("IoBuildSynchronousFsdRequest failed\n");
goto deref;
}
Status = IoCallDriver(DeviceObject, Irp);
if (Status == STATUS_PENDING) {
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
Status = IoStatusBlock.Status;
}
if (NT_SUCCESS(Status)) {
UINT32 crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&sb->uuid, (ULONG)sizeof(superblock) - sizeof(sb->checksum));
if (crc32 != *((UINT32*)sb->checksum))
WARN("superblock %u CRC error\n", i);
else if (sb->generation > v->gen1) {
v->gen2 = v->gen1;
v->gen1 = sb->generation;
}
} else {
ERR("got error %08x while reading superblock %u\n", Status, i);
}
i++;
}
TRACE("volume found\n");
TRACE("gen1 = %llx, gen2 = %llx\n", v->gen1, v->gen2);
TRACE("FS uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
v->fsuuid.uuid[0], v->fsuuid.uuid[1], v->fsuuid.uuid[2], v->fsuuid.uuid[3], v->fsuuid.uuid[4], v->fsuuid.uuid[5], v->fsuuid.uuid[6], v->fsuuid.uuid[7],
v->fsuuid.uuid[8], v->fsuuid.uuid[9], v->fsuuid.uuid[10], v->fsuuid.uuid[11], v->fsuuid.uuid[12], v->fsuuid.uuid[13], v->fsuuid.uuid[14], v->fsuuid.uuid[15]);
@ -228,7 +380,192 @@ exit:
ExFreePool(us2.Buffer);
}
void STDCALL look_for_vols(LIST_ENTRY* volumes) {
static NTSTATUS look_in_harddisk_dir(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT mountmgr, PUNICODE_STRING name, LIST_ENTRY* volumes) {
UNICODE_STRING path;
OBJECT_ATTRIBUTES attr;
NTSTATUS Status;
HANDLE h;
OBJECT_DIRECTORY_INFORMATION* odi;
ULONG odisize, context;
BOOL restart, has_part0 = FALSE, has_parts = FALSE;
static const WCHAR partition[] = L"Partition";
static WCHAR partition0[] = L"Partition0";
path.Buffer = ExAllocatePoolWithTag(PagedPool, ((wcslen(devpath) + 1) * sizeof(WCHAR)) + name->Length, ALLOC_TAG);
if (!path.Buffer) {
ERR("out of memory\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlCopyMemory(path.Buffer, devpath, wcslen(devpath) * sizeof(WCHAR));
path.Buffer[wcslen(devpath)] = '\\';
RtlCopyMemory(&path.Buffer[wcslen(devpath) + 1], name->Buffer, name->Length);
path.Length = path.MaximumLength = ((wcslen(devpath) + 1) * sizeof(WCHAR)) + name->Length;
attr.Length = sizeof(attr);
attr.RootDirectory = 0;
attr.Attributes = OBJ_CASE_INSENSITIVE;
attr.ObjectName = &path;
attr.SecurityDescriptor = NULL;
attr.SecurityQualityOfService = NULL;
Status = ZwOpenDirectoryObject(&h, DIRECTORY_TRAVERSE, &attr);
if (!NT_SUCCESS(Status)) {
ERR("ZwOpenDirectoryObject returned %08x\n", Status);
goto end;
}
odisize = sizeof(OBJECT_DIRECTORY_INFORMATION) * 16;
odi = ExAllocatePoolWithTag(PagedPool, odisize, ALLOC_TAG);
if (!odi) {
ERR("out of memory\n");
Status = STATUS_INSUFFICIENT_RESOURCES;
ZwClose(h);
goto end;
}
restart = TRUE;
do {
Status = ZwQueryDirectoryObject(h, odi, odisize, FALSE, restart, &context, NULL/*&retlen*/);
restart = FALSE;
if (!NT_SUCCESS(Status)) {
if (Status != STATUS_NO_MORE_ENTRIES)
ERR("ZwQueryDirectoryObject returned %08x\n", Status);
} else {
OBJECT_DIRECTORY_INFORMATION* odi2 = odi;
while (odi2->Name.Buffer) {
TRACE("%.*S, %.*S\n", odi2->TypeName.Length / sizeof(WCHAR), odi2->TypeName.Buffer, odi2->Name.Length / sizeof(WCHAR), odi2->Name.Buffer);
if (odi2->Name.Length > wcslen(partition) * sizeof(WCHAR) &&
RtlCompareMemory(odi2->Name.Buffer, partition, wcslen(partition) * sizeof(WCHAR)) == wcslen(partition) * sizeof(WCHAR)) {
if (odi2->Name.Length == (wcslen(partition) + 1) * sizeof(WCHAR) && odi2->Name.Buffer[(odi2->Name.Length / sizeof(WCHAR)) - 1] == '0') {
// Partition0 refers to the whole disk
has_part0 = TRUE;
} else {
has_parts = TRUE;
test_vol(DriverObject, mountmgr, &path, &odi2->Name, FALSE, volumes);
}
}
odi2 = &odi2[1];
}
}
} while (NT_SUCCESS(Status));
// If disk had no partitions, test the whole disk
if (!has_parts && has_part0) {
UNICODE_STRING part0us;
part0us.Buffer = partition0;
part0us.Length = part0us.MaximumLength = wcslen(partition0) * sizeof(WCHAR);
test_vol(DriverObject, mountmgr, &path, &part0us, TRUE, volumes);
}
ZwClose(h);
ExFreePool(odi);
Status = STATUS_SUCCESS;
end:
ExFreePool(path.Buffer);
return Status;
}
static void remove_drive_letter(PDEVICE_OBJECT mountmgr, volume* v) {
NTSTATUS Status;
KEVENT Event;
PIRP Irp;
MOUNTMGR_MOUNT_POINT* mmp;
ULONG mmpsize;
MOUNTMGR_MOUNT_POINTS mmps1, *mmps2;
IO_STATUS_BLOCK IoStatusBlock;
mmpsize = sizeof(MOUNTMGR_MOUNT_POINT) + v->devpath.Length;
mmp = ExAllocatePoolWithTag(PagedPool, mmpsize, ALLOC_TAG);
if (!mmp) {
ERR("out of memory\n");
return;
}
RtlZeroMemory(mmp, mmpsize);
mmp->DeviceNameOffset = sizeof(MOUNTMGR_MOUNT_POINT);
mmp->DeviceNameLength = v->devpath.Length;
RtlCopyMemory(&mmp[1], v->devpath.Buffer, v->devpath.Length);
KeInitializeEvent(&Event, NotificationEvent, FALSE);
Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTMGR_DELETE_POINTS,
mountmgr, mmp, mmpsize,
&mmps1, sizeof(MOUNTMGR_MOUNT_POINTS), FALSE, &Event, &IoStatusBlock);
if (!Irp) {
ERR("%.*S: IoBuildDeviceIoControlRequest 1 failed\n", v->devpath.Length / sizeof(WCHAR), v->devpath.Buffer);
ExFreePool(mmp);
return;
}
Status = IoCallDriver(mountmgr, Irp);
if (Status == STATUS_PENDING) {
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
Status = IoStatusBlock.Status;
}
if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW) {
ERR("%.*S: IoCallDriver 1 returned %08x\n", v->devpath.Length / sizeof(WCHAR), v->devpath.Buffer, Status);
ExFreePool(mmp);
return;
}
if (Status != STATUS_BUFFER_OVERFLOW || mmps1.Size == 0) {
ExFreePool(mmp);
return;
}
mmps2 = ExAllocatePoolWithTag(PagedPool, mmps1.Size, ALLOC_TAG);
if (!mmps2) {
ERR("out of memory\n");
ExFreePool(mmp);
return;
}
KeInitializeEvent(&Event, NotificationEvent, FALSE);
Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTMGR_DELETE_POINTS,
mountmgr, mmp, mmpsize,
mmps2, mmps1.Size, FALSE, &Event, &IoStatusBlock);
if (!Irp) {
ERR("%.*S: IoBuildDeviceIoControlRequest 2 failed\n", v->devpath.Length / sizeof(WCHAR), v->devpath.Buffer);
ExFreePool(mmps2);
ExFreePool(mmp);
return;
}
Status = IoCallDriver(mountmgr, Irp);
if (Status == STATUS_PENDING) {
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
Status = IoStatusBlock.Status;
}
if (!NT_SUCCESS(Status)) {
ERR("%.*S: IoCallDriver 2 returned %08x\n", v->devpath.Length / sizeof(WCHAR), v->devpath.Buffer, Status);
ExFreePool(mmps2);
ExFreePool(mmp);
return;
}
ExFreePool(mmps2);
ExFreePool(mmp);
}
void STDCALL look_for_vols(PDRIVER_OBJECT DriverObject, LIST_ENTRY* volumes) {
PFILE_OBJECT FileObject;
PDEVICE_OBJECT mountmgr;
OBJECT_ATTRIBUTES attr;
@ -241,8 +578,8 @@ void STDCALL look_for_vols(LIST_ENTRY* volumes) {
NTSTATUS Status;
LIST_ENTRY* le;
static const WCHAR hdv[] = {'H','a','r','d','d','i','s','k','V','o','l','u','m','e',0};
static const WCHAR device[] = {'D','e','v','i','c','e',0};
static const WCHAR directory[] = L"Directory";
static const WCHAR harddisk[] = L"Harddisk";
RtlInitUnicodeString(&mmdevpath, MOUNTMGR_DEVICE_NAME);
Status = IoGetDeviceObjectPointer(&mmdevpath, FILE_READ_ATTRIBUTES, &FileObject, &mountmgr);
@ -271,6 +608,7 @@ void STDCALL look_for_vols(LIST_ENTRY* volumes) {
odi = ExAllocatePoolWithTag(PagedPool, odisize, ALLOC_TAG);
if (!odi) {
ERR("out of memory\n");
ZwClose(h);
return;
}
@ -286,21 +624,21 @@ void STDCALL look_for_vols(LIST_ENTRY* volumes) {
OBJECT_DIRECTORY_INFORMATION* odi2 = odi;
while (odi2->Name.Buffer) {
if (odi2->TypeName.Length == wcslen(device) * sizeof(WCHAR) &&
RtlCompareMemory(odi2->TypeName.Buffer, device, wcslen(device) * sizeof(WCHAR)) == wcslen(device) * sizeof(WCHAR) &&
odi2->Name.Length > wcslen(hdv) * sizeof(WCHAR) &&
RtlCompareMemory(odi2->Name.Buffer, hdv, wcslen(hdv) * sizeof(WCHAR)) == wcslen(hdv) * sizeof(WCHAR)) {
test_vol(mountmgr, &odi2->Name, volumes);
if (odi2->TypeName.Length == wcslen(directory) * sizeof(WCHAR) &&
RtlCompareMemory(odi2->TypeName.Buffer, directory, odi2->TypeName.Length) == odi2->TypeName.Length &&
odi2->Name.Length > wcslen(harddisk) * sizeof(WCHAR) &&
RtlCompareMemory(odi2->Name.Buffer, harddisk, wcslen(harddisk) * sizeof(WCHAR)) == wcslen(harddisk) * sizeof(WCHAR)) {
look_in_harddisk_dir(DriverObject, mountmgr, &odi2->Name, volumes);
}
odi2 = &odi2[1];
}
}
} while (NT_SUCCESS(Status));
ExFreePool(odi);
ZwClose(h);
// FIXME - if Windows has already added the second device of a filesystem itself, delete it
le = volumes->Flink;
while (le != volumes) {
volume* v = CONTAINING_RECORD(le, volume, list_entry);
@ -315,8 +653,11 @@ void STDCALL look_for_vols(LIST_ENTRY* volumes) {
if (RtlCompareMemory(&v2->fsuuid, &v->fsuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
v2->processed = TRUE;
if (v2->devnum < mountvol->devnum)
if (v2->devnum < mountvol->devnum) {
remove_drive_letter(mountmgr, mountvol);
mountvol = v2;
} else if (v2->devnum > mountvol->devnum)
remove_drive_letter(mountmgr, v2);
}
le2 = le2->Flink;

View file

@ -17,6 +17,9 @@
#include "btrfs_drv.h"
#define SEF_DACL_AUTO_INHERIT 0x01
#define SEF_SACL_AUTO_INHERIT 0x02
typedef struct {
UCHAR revision;
UCHAR elements;
@ -382,103 +385,6 @@ static ACL* load_default_acl() {
return acl;
}
static ACL* inherit_acl(SECURITY_DESCRIPTOR* parsd, BOOL file) {
ULONG size;
NTSTATUS Status;
ACL *paracl, *acl;
BOOLEAN parhasdacl, pardefaulted;
ACE_HEADER *ah, *parah;
UINT32 i;
USHORT num_aces;
// FIXME - replace this with SeAssignSecurity
Status = RtlGetDaclSecurityDescriptor(parsd, &parhasdacl, &paracl, &pardefaulted);
if (!NT_SUCCESS(Status)) {
ERR("RtlGetDaclSecurityDescriptor returned %08x\n", Status);
return NULL;
}
// FIXME - handle parhasdacl == FALSE
// OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE
num_aces = 0;
size = sizeof(ACL);
ah = (ACE_HEADER*)&paracl[1];
for (i = 0; i < paracl->AceCount; i++) {
if (!file && ah->AceFlags & CONTAINER_INHERIT_ACE) {
num_aces++;
size += ah->AceSize;
if (ah->AceFlags & INHERIT_ONLY_ACE) {
num_aces++;
size += ah->AceSize;
}
}
if (ah->AceFlags & OBJECT_INHERIT_ACE && (file || !(ah->AceFlags & CONTAINER_INHERIT_ACE))) {
num_aces++;
size += ah->AceSize;
}
ah = (ACE_HEADER*)((UINT8*)ah + ah->AceSize);
}
acl = ExAllocatePoolWithTag(PagedPool, size, ALLOC_TAG);
if (!acl) {
ERR("out of memory\n");
return NULL;
}
acl->AclRevision = ACL_REVISION;
acl->Sbz1 = 0;
acl->AclSize = size;
acl->AceCount = num_aces;
acl->Sbz2 = 0;
ah = (ACE_HEADER*)&acl[1];
parah = (ACE_HEADER*)&paracl[1];
for (i = 0; i < paracl->AceCount; i++) {
if (!file && parah->AceFlags & CONTAINER_INHERIT_ACE) {
if (parah->AceFlags & INHERIT_ONLY_ACE) {
RtlCopyMemory(ah, parah, parah->AceSize);
ah->AceFlags &= ~INHERIT_ONLY_ACE;
ah->AceFlags |= INHERITED_ACE;
ah->AceFlags &= ~(OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE);
ah = (ACE_HEADER*)((UINT8*)ah + ah->AceSize);
}
RtlCopyMemory(ah, parah, parah->AceSize);
ah->AceFlags |= INHERITED_ACE;
if (ah->AceFlags & NO_PROPAGATE_INHERIT_ACE)
ah->AceFlags &= ~(OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE);
ah = (ACE_HEADER*)((UINT8*)ah + ah->AceSize);
}
if (parah->AceFlags & OBJECT_INHERIT_ACE && (file || !(parah->AceFlags & CONTAINER_INHERIT_ACE))) {
RtlCopyMemory(ah, parah, parah->AceSize);
ah->AceFlags |= INHERITED_ACE;
if (file)
ah->AceFlags &= ~INHERIT_ONLY_ACE;
else
ah->AceFlags |= INHERIT_ONLY_ACE;
if (ah->AceFlags & NO_PROPAGATE_INHERIT_ACE || file)
ah->AceFlags &= ~(OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE);
ah = (ACE_HEADER*)((UINT8*)ah + ah->AceSize);
}
parah = (ACE_HEADER*)((UINT8*)parah + parah->AceSize);
}
return acl;
}
// static void STDCALL sid_to_string(PSID sid, char* s) {
// sid_header* sh = (sid_header*)sid;
// LARGE_INTEGER authnum;
@ -532,7 +438,7 @@ static BOOL get_sd_from_xattr(fcb* fcb) {
ERR("RtlSelfRelativeToAbsoluteSD 1 returned %08x\n", Status);
}
ERR("sdsize = %u, daclsize = %u, saclsize = %u, ownersize = %u, groupsize = %u\n", sdsize, daclsize, saclsize, ownersize, groupsize);
TRACE("sdsize = %u, daclsize = %u, saclsize = %u, ownersize = %u, groupsize = %u\n", sdsize, daclsize, saclsize, ownersize, groupsize);
newsd2 = sdsize == 0 ? NULL : ExAllocatePoolWithTag(PagedPool, sdsize, ALLOC_TAG);
dacl = daclsize == 0 ? NULL : ExAllocatePoolWithTag(PagedPool, daclsize, ALLOC_TAG);
@ -540,7 +446,7 @@ static BOOL get_sd_from_xattr(fcb* fcb) {
owner = ownersize == 0 ? NULL : ExAllocatePoolWithTag(PagedPool, ownersize, ALLOC_TAG);
group = groupsize == 0 ? NULL : ExAllocatePoolWithTag(PagedPool, groupsize, ALLOC_TAG);
if ((sdsize > 0 && !newsd2) || (daclsize > 0 && !dacl) || (saclsize > 0 && !sacl) || (ownersize > 0 && !owner) || (groupsize > 0 || !group)) {
if ((sdsize > 0 && !newsd2) || (daclsize > 0 && !dacl) || (saclsize > 0 && !sacl) || (ownersize > 0 && !owner) || (groupsize > 0 && !group)) {
ERR("out of memory\n");
if (newsd2) ExFreePool(newsd2);
if (dacl) ExFreePool(dacl);
@ -646,16 +552,13 @@ static BOOL get_sd_from_xattr(fcb* fcb) {
return TRUE;
}
void fcb_get_sd(fcb* fcb, struct _fcb* parent) {
static void get_top_level_sd(fcb* fcb) {
NTSTATUS Status;
SECURITY_DESCRIPTOR sd;
ULONG buflen;
ACL* acl = NULL;
PSID usersid = NULL, groupsid = NULL;
if (get_sd_from_xattr(fcb))
goto end;
Status = RtlCreateSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
if (!NT_SUCCESS(Status)) {
@ -694,14 +597,10 @@ void fcb_get_sd(fcb* fcb, struct _fcb* parent) {
}
// }
if (!parent)
acl = load_default_acl();
else
acl = inherit_acl(parent->sd, fcb->type != BTRFS_TYPE_DIRECTORY);
acl = load_default_acl();
if (!acl) {
ERR("out of memory\n");
Status = STATUS_INSUFFICIENT_RESOURCES;
goto end;
}
@ -755,6 +654,47 @@ end:
ExFreePool(groupsid);
}
void fcb_get_sd(fcb* fcb, struct _fcb* parent) {
NTSTATUS Status;
PSID usersid = NULL, groupsid = NULL;
SECURITY_SUBJECT_CONTEXT subjcont;
if (get_sd_from_xattr(fcb))
return;
if (!parent) {
get_top_level_sd(fcb);
return;
}
SeCaptureSubjectContext(&subjcont);
Status = SeAssignSecurityEx(parent->sd, NULL, (void**)&fcb->sd, NULL, fcb->type == BTRFS_TYPE_DIRECTORY, SEF_DACL_AUTO_INHERIT,
&subjcont, IoGetFileObjectGenericMapping(), PagedPool);
if (!NT_SUCCESS(Status)) {
ERR("SeAssignSecurityEx returned %08x\n", Status);
}
uid_to_sid(fcb->inode_item.st_uid, &usersid);
if (!usersid) {
ERR("out of memory\n");
return;
}
RtlSetOwnerSecurityDescriptor(&fcb->sd, usersid, FALSE);
gid_to_sid(fcb->inode_item.st_gid, &groupsid);
if (!groupsid) {
ERR("out of memory\n");
return;
}
RtlSetGroupSecurityDescriptor(&fcb->sd, groupsid, FALSE);
ExFreePool(usersid);
ExFreePool(groupsid);
}
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;
@ -787,8 +727,11 @@ NTSTATUS STDCALL drv_query_security(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
NTSTATUS Status;
SECURITY_DESCRIPTOR* sd;
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
device_extension* Vcb = DeviceObject->DeviceExtension;
ULONG buflen;
BOOL top_level;
PFILE_OBJECT FileObject = IrpSp->FileObject;
ccb* ccb = FileObject ? FileObject->FsContext2 : NULL;
TRACE("query security\n");
@ -796,6 +739,23 @@ NTSTATUS STDCALL drv_query_security(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
top_level = is_top_level(Irp);
if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
Status = part0_passthrough(DeviceObject, Irp);
goto exit;
}
if (!ccb) {
ERR("no ccb\n");
Status = STATUS_INVALID_PARAMETER;
goto end;
}
if (Irp->RequestorMode == UserMode && !(ccb->access & READ_CONTROL)) {
WARN("insufficient permissions\n");
Status = STATUS_ACCESS_DENIED;
goto end;
}
Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
@ -820,12 +780,13 @@ NTSTATUS STDCALL drv_query_security(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
if (Irp->MdlAddress && !sd) {
ERR("MmGetSystemAddressForMdlSafe returned NULL\n");
return STATUS_INSUFFICIENT_RESOURCES;
Status = STATUS_INSUFFICIENT_RESOURCES;
goto end;
}
buflen = IrpSp->Parameters.QuerySecurity.Length;
Status = get_file_security(DeviceObject->DeviceExtension, IrpSp->FileObject, sd, &buflen, IrpSp->Parameters.QuerySecurity.SecurityInformation);
Status = get_file_security(Vcb, IrpSp->FileObject, sd, &buflen, IrpSp->Parameters.QuerySecurity.SecurityInformation);
if (NT_SUCCESS(Status))
Irp->IoStatus.Information = IrpSp->Parameters.QuerySecurity.Length;
@ -835,12 +796,14 @@ NTSTATUS STDCALL drv_query_security(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
} else
Irp->IoStatus.Information = 0;
end:
TRACE("Irp->IoStatus.Information = %u\n", Irp->IoStatus.Information);
Irp->IoStatus.Status = Status;
IoCompleteRequest( Irp, IO_NO_INCREMENT );
IoCompleteRequest(Irp, IO_NO_INCREMENT);
exit:
if (top_level)
IoSetTopLevelIrp(NULL);
@ -857,31 +820,26 @@ static NTSTATUS STDCALL set_file_security(device_extension* Vcb, PFILE_OBJECT Fi
ccb* ccb = FileObject->FsContext2;
file_ref* fileref = ccb ? ccb->fileref : NULL;
SECURITY_DESCRIPTOR* oldsd;
INODE_ITEM* ii;
KEY searchkey;
traverse_ptr tp;
LARGE_INTEGER time;
BTRFS_TIME now;
LIST_ENTRY rollback;
TRACE("(%p, %p, %p, %x)\n", Vcb, FileObject, sd, flags);
InitializeListHead(&rollback);
if (Vcb->readonly)
return STATUS_MEDIA_WRITE_PROTECTED;
acquire_tree_lock(Vcb, TRUE);
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;
Status = STATUS_INTERNAL_ERROR;
goto end;
}
}
ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
if (fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY) {
Status = STATUS_ACCESS_DENIED;
goto end;
@ -898,24 +856,6 @@ static NTSTATUS STDCALL set_file_security(device_extension* Vcb, PFILE_OBJECT Fi
ExFreePool(oldsd);
searchkey.obj_id = fcb->inode;
searchkey.obj_type = TYPE_INODE_ITEM;
searchkey.offset = 0;
Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
goto end;
}
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;
goto end;
}
delete_tree_item(Vcb, &tp, &rollback);
KeQuerySystemTime(&time);
win_time_to_unix(time, &now);
@ -937,48 +877,28 @@ static NTSTATUS STDCALL set_file_security(device_extension* Vcb, PFILE_OBJECT Fi
fcb->inode_item.st_uid = sid_to_uid(owner);
}
ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
if (!ii) {
ERR("out of memory\n");
Status = STATUS_INSUFFICIENT_RESOURCES;
goto end;
}
RtlCopyMemory(ii, &fcb->inode_item, sizeof(INODE_ITEM));
if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, &rollback)) {
ERR("error - failed to insert INODE_ITEM\n");
Status = STATUS_INTERNAL_ERROR;
ExFreePool(ii);
goto end;
}
Status = set_xattr(Vcb, fcb->subvol, fcb->inode, EA_NTACL, EA_NTACL_HASH, (UINT8*)fcb->sd, RtlLengthSecurityDescriptor(fcb->sd), &rollback);
if (!NT_SUCCESS(Status)) {
ERR("set_xattr returned %08x\n", Status);
ExFreePool(ii);
goto end;
}
fcb->sd_dirty = TRUE;
fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
fcb->subvol->root_item.ctime = now;
Status = consider_write(Vcb);
mark_fcb_dirty(fcb);
send_notification_fcb(fileref, FILE_NOTIFY_CHANGE_SECURITY, FILE_ACTION_MODIFIED);
end:
if (NT_SUCCESS(Status))
clear_rollback(&rollback);
else
do_rollback(Vcb, &rollback);
ExReleaseResourceLite(fcb->Header.Resource);
release_tree_lock(Vcb, TRUE);
return Status;
}
NTSTATUS STDCALL drv_set_security(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
NTSTATUS Status;
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
PFILE_OBJECT FileObject = IrpSp->FileObject;
ccb* ccb = FileObject ? FileObject->FsContext2 : NULL;
device_extension* Vcb = DeviceObject->DeviceExtension;
ULONG access_req = 0;
BOOL top_level;
TRACE("set security\n");
@ -987,34 +907,58 @@ NTSTATUS STDCALL drv_set_security(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
top_level = is_top_level(Irp);
if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
Status = part0_passthrough(DeviceObject, Irp);
goto exit;
}
if (!ccb) {
ERR("no ccb\n");
Status = STATUS_INVALID_PARAMETER;
goto end;
}
Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
if (IrpSp->Parameters.QuerySecurity.SecurityInformation & OWNER_SECURITY_INFORMATION)
if (IrpSp->Parameters.QuerySecurity.SecurityInformation & OWNER_SECURITY_INFORMATION) {
TRACE("OWNER_SECURITY_INFORMATION\n");
access_req |= WRITE_OWNER;
}
if (IrpSp->Parameters.QuerySecurity.SecurityInformation & GROUP_SECURITY_INFORMATION)
if (IrpSp->Parameters.QuerySecurity.SecurityInformation & GROUP_SECURITY_INFORMATION) {
TRACE("GROUP_SECURITY_INFORMATION\n");
access_req |= WRITE_OWNER;
}
if (IrpSp->Parameters.QuerySecurity.SecurityInformation & DACL_SECURITY_INFORMATION)
if (IrpSp->Parameters.QuerySecurity.SecurityInformation & DACL_SECURITY_INFORMATION) {
TRACE("DACL_SECURITY_INFORMATION\n");
access_req |= WRITE_DAC;
}
if (IrpSp->Parameters.QuerySecurity.SecurityInformation & SACL_SECURITY_INFORMATION)
if (IrpSp->Parameters.QuerySecurity.SecurityInformation & SACL_SECURITY_INFORMATION) {
TRACE("SACL_SECURITY_INFORMATION\n");
access_req |= ACCESS_SYSTEM_SECURITY;
}
Status = set_file_security(DeviceObject->DeviceExtension, IrpSp->FileObject, IrpSp->Parameters.SetSecurity.SecurityDescriptor,
if ((ccb->access & access_req) != access_req) {
Status = STATUS_ACCESS_DENIED;
WARN("insufficient privileges\n");
goto end;
}
Status = set_file_security(DeviceObject->DeviceExtension, FileObject, IrpSp->Parameters.SetSecurity.SecurityDescriptor,
IrpSp->Parameters.SetSecurity.SecurityInformation);
end:
Irp->IoStatus.Status = Status;
IoCompleteRequest( Irp, IO_NO_INCREMENT );
IoCompleteRequest(Irp, IO_NO_INCREMENT);
if (top_level)
IoSetTopLevelIrp(NULL);
TRACE("returning %08x\n", Status);
exit:
if (top_level)
IoSetTopLevelIrp(NULL);
@ -1023,16 +967,16 @@ NTSTATUS STDCALL drv_set_security(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
return Status;
}
NTSTATUS fcb_get_new_sd(fcb* fcb, file_ref* fileref, ACCESS_STATE* as) {
NTSTATUS fcb_get_new_sd(fcb* fcb, file_ref* parfileref, ACCESS_STATE* as) {
NTSTATUS Status;
PSID owner;
BOOLEAN defaulted;
Status = SeAssignSecurity((fileref && fileref->parent) ? fileref->parent->fcb->sd : NULL, as->SecurityDescriptor, (void**)&fcb->sd, fcb->type == BTRFS_TYPE_DIRECTORY,
&as->SubjectSecurityContext, IoGetFileObjectGenericMapping(), PagedPool);
Status = SeAssignSecurityEx(parfileref ? parfileref->fcb->sd : NULL, as->SecurityDescriptor, (void**)&fcb->sd, NULL, fcb->type == BTRFS_TYPE_DIRECTORY,
SEF_SACL_AUTO_INHERIT, &as->SubjectSecurityContext, IoGetFileObjectGenericMapping(), PagedPool);
if (!NT_SUCCESS(Status)) {
ERR("SeAssignSecurity returned %08x\n", Status);
ERR("SeAssignSecurityEx returned %08x\n", Status);
return Status;
}

View file

@ -19,355 +19,20 @@
// #define DEBUG_TREE_LOCKS
enum read_tree_status {
ReadTreeStatus_Pending,
ReadTreeStatus_Success,
ReadTreeStatus_Cancelling,
ReadTreeStatus_Cancelled,
ReadTreeStatus_Error,
ReadTreeStatus_CRCError,
ReadTreeStatus_MissingDevice
};
struct read_tree_context;
typedef struct {
struct read_tree_context* context;
UINT8* buf;
PIRP Irp;
IO_STATUS_BLOCK iosb;
enum read_tree_status status;
} read_tree_stripe;
typedef struct {
KEVENT Event;
NTSTATUS Status;
chunk* c;
// UINT8* buf;
UINT32 buflen;
UINT64 num_stripes;
LONG stripes_left;
UINT64 type;
read_tree_stripe* stripes;
} read_tree_context;
enum rollback_type {
ROLLBACK_INSERT_ITEM,
ROLLBACK_DELETE_ITEM
};
typedef struct {
enum rollback_type type;
void* ptr;
LIST_ENTRY list_entry;
} rollback_item;
static NTSTATUS STDCALL read_tree_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
read_tree_stripe* stripe = conptr;
read_tree_context* context = (read_tree_context*)stripe->context;
UINT64 i;
if (stripe->status == ReadTreeStatus_Cancelling) {
stripe->status = ReadTreeStatus_Cancelled;
goto end;
}
stripe->iosb = Irp->IoStatus;
if (NT_SUCCESS(Irp->IoStatus.Status)) {
tree_header* th = (tree_header*)stripe->buf;
UINT32 crc32;
crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&th->fs_uuid, context->buflen - sizeof(th->csum));
if (crc32 == *((UINT32*)th->csum)) {
stripe->status = ReadTreeStatus_Success;
for (i = 0; i < context->num_stripes; i++) {
if (context->stripes[i].status == ReadTreeStatus_Pending) {
context->stripes[i].status = ReadTreeStatus_Cancelling;
IoCancelIrp(context->stripes[i].Irp);
}
}
goto end;
} else
stripe->status = ReadTreeStatus_CRCError;
} else {
stripe->status = ReadTreeStatus_Error;
}
end:
if (InterlockedDecrement(&context->stripes_left) == 0)
KeSetEvent(&context->Event, 0, FALSE);
// return STATUS_SUCCESS;
return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS STDCALL read_tree(device_extension* Vcb, UINT64 addr, UINT8* buf) {
CHUNK_ITEM* ci;
CHUNK_ITEM_STRIPE* cis;
read_tree_context* context;
UINT64 i/*, type*/, offset;
NTSTATUS Status;
device** devices;
// FIXME - make this work with RAID
if (Vcb->log_to_phys_loaded) {
chunk* c = get_chunk_from_address(Vcb, addr);
if (!c) {
ERR("get_chunk_from_address failed\n");
return STATUS_INTERNAL_ERROR;
}
ci = c->chunk_item;
offset = c->offset;
devices = c->devices;
} else {
LIST_ENTRY* le = Vcb->sys_chunks.Flink;
ci = NULL;
while (le != &Vcb->sys_chunks) {
sys_chunk* sc = CONTAINING_RECORD(le, sys_chunk, list_entry);
if (sc->key.obj_id == 0x100 && sc->key.obj_type == TYPE_CHUNK_ITEM && sc->key.offset <= addr) {
CHUNK_ITEM* chunk_item = sc->data;
if ((addr - sc->key.offset) < chunk_item->size && chunk_item->num_stripes > 0) {
ci = chunk_item;
offset = sc->key.offset;
cis = (CHUNK_ITEM_STRIPE*)&chunk_item[1];
devices = ExAllocatePoolWithTag(PagedPool, sizeof(device*) * ci->num_stripes, ALLOC_TAG);
if (!devices) {
ERR("out of memory\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
for (i = 0; i < ci->num_stripes; i++) {
devices[i] = find_device_from_uuid(Vcb, &cis[i].dev_uuid);
}
break;
}
}
le = le->Flink;
}
if (!ci) {
ERR("could not find chunk for %llx in bootstrap\n", addr);
return STATUS_INTERNAL_ERROR;
}
}
// if (ci->type & BLOCK_FLAG_DUPLICATE) {
// type = BLOCK_FLAG_DUPLICATE;
// } else if (ci->type & BLOCK_FLAG_RAID0) {
// FIXME("RAID0 not yet supported\n");
// return STATUS_NOT_IMPLEMENTED;
// } else if (ci->type & BLOCK_FLAG_RAID1) {
// FIXME("RAID1 not yet supported\n");
// return STATUS_NOT_IMPLEMENTED;
// } else if (ci->type & BLOCK_FLAG_RAID10) {
// FIXME("RAID10 not yet supported\n");
// return STATUS_NOT_IMPLEMENTED;
// } else if (ci->type & BLOCK_FLAG_RAID5) {
// FIXME("RAID5 not yet supported\n");
// return STATUS_NOT_IMPLEMENTED;
// } else if (ci->type & BLOCK_FLAG_RAID6) {
// FIXME("RAID6 not yet supported\n");
// return STATUS_NOT_IMPLEMENTED;
// } else { // SINGLE
// type = 0;
// }
cis = (CHUNK_ITEM_STRIPE*)&ci[1];
context = ExAllocatePoolWithTag(NonPagedPool, sizeof(read_tree_context), ALLOC_TAG);
if (!context) {
ERR("out of memory\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlZeroMemory(context, sizeof(read_tree_context));
KeInitializeEvent(&context->Event, NotificationEvent, FALSE);
context->stripes = ExAllocatePoolWithTag(NonPagedPool, sizeof(read_tree_stripe) * ci->num_stripes, ALLOC_TAG);
if (!context->stripes) {
ERR("out of memory\n");
ExFreePool(context);
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlZeroMemory(context->stripes, sizeof(read_tree_stripe) * ci->num_stripes);
context->buflen = Vcb->superblock.node_size;
context->num_stripes = ci->num_stripes;
context->stripes_left = context->num_stripes;
// context->type = type;
// FIXME - for RAID, check beforehand whether there's enough devices to satisfy request
for (i = 0; i < ci->num_stripes; i++) {
PIO_STACK_LOCATION IrpSp;
if (!devices[i]) {
context->stripes[i].status = ReadTreeStatus_MissingDevice;
context->stripes[i].buf = NULL;
context->stripes_left--;
} else {
context->stripes[i].context = (struct read_tree_context*)context;
context->stripes[i].buf = ExAllocatePoolWithTag(NonPagedPool, Vcb->superblock.node_size, ALLOC_TAG);
if (!context->stripes[i].buf) {
ERR("out of memory\n");
Status = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
context->stripes[i].Irp = IoAllocateIrp(devices[i]->devobj->StackSize, FALSE);
if (!context->stripes[i].Irp) {
ERR("IoAllocateIrp failed\n");
Status = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
IrpSp = IoGetNextIrpStackLocation(context->stripes[i].Irp);
IrpSp->MajorFunction = IRP_MJ_READ;
if (devices[i]->devobj->Flags & DO_BUFFERED_IO) {
FIXME("FIXME - buffered IO\n");
} else if (devices[i]->devobj->Flags & DO_DIRECT_IO) {
context->stripes[i].Irp->MdlAddress = IoAllocateMdl(context->stripes[i].buf, Vcb->superblock.node_size, FALSE, FALSE, NULL);
if (!context->stripes[i].Irp->MdlAddress) {
ERR("IoAllocateMdl failed\n");
Status = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
MmProbeAndLockPages(context->stripes[i].Irp->MdlAddress, KernelMode, IoWriteAccess);
} else {
context->stripes[i].Irp->UserBuffer = context->stripes[i].buf;
}
IrpSp->Parameters.Read.Length = Vcb->superblock.node_size;
IrpSp->Parameters.Read.ByteOffset.QuadPart = addr - offset + cis[i].offset;
context->stripes[i].Irp->UserIosb = &context->stripes[i].iosb;
IoSetCompletionRoutine(context->stripes[i].Irp, read_tree_completion, &context->stripes[i], TRUE, TRUE, TRUE);
context->stripes[i].status = ReadTreeStatus_Pending;
}
}
for (i = 0; i < ci->num_stripes; i++) {
if (context->stripes[i].status != ReadTreeStatus_MissingDevice) {
IoCallDriver(devices[i]->devobj, context->stripes[i].Irp);
}
}
KeWaitForSingleObject(&context->Event, Executive, KernelMode, FALSE, NULL);
// FIXME - if checksum error, write good data over bad
// check if any of the 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++) {
if (context->stripes[i].status == ReadTreeStatus_Success) {
RtlCopyMemory(buf, context->stripes[i].buf, Vcb->superblock.node_size);
Status = STATUS_SUCCESS;
goto exit;
}
}
// if not, see if we got a checksum error
for (i = 0; i < ci->num_stripes; i++) {
if (context->stripes[i].status == ReadTreeStatus_CRCError) {
#ifdef _DEBUG
tree_header* th = (tree_header*)context->stripes[i].buf;
UINT32 crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&th->fs_uuid, context->buflen - sizeof(th->csum));
// UINT64 j;
WARN("stripe %llu had a checksum error\n", i);
WARN("crc32 was %08x, expected %08x\n", crc32, *((UINT32*)th->csum));
#endif
// for (j = 0; j < ci->num_stripes; j++) {
// WARN("stripe %llu: device = %p, status = %u\n", j, c->devices[j], context->stripes[j].status);
// }
// int3;
Status = STATUS_IMAGE_CHECKSUM_MISMATCH;
goto exit;
}
}
// failing that, return the first error we encountered
for (i = 0; i < ci->num_stripes; i++) {
if (context->stripes[i].status == ReadTreeStatus_Error) {
Status = context->stripes[i].iosb.Status;
goto exit;
}
}
// if we somehow get here, return STATUS_INTERNAL_ERROR
Status = STATUS_INTERNAL_ERROR;
// for (i = 0; i < ci->num_stripes; i++) {
// ERR("%llx: status = %u, NTSTATUS = %08x\n", i, context->stripes[i].status, context->stripes[i].iosb.Status);
// }
exit:
for (i = 0; i < ci->num_stripes; i++) {
if (context->stripes[i].Irp) {
if (devices[i]->devobj->Flags & DO_DIRECT_IO) {
MmUnlockPages(context->stripes[i].Irp->MdlAddress);
IoFreeMdl(context->stripes[i].Irp->MdlAddress);
}
IoFreeIrp(context->stripes[i].Irp);
}
if (context->stripes[i].buf)
ExFreePool(context->stripes[i].buf);
}
ExFreePool(context->stripes);
ExFreePool(context);
if (!Vcb->log_to_phys_loaded)
ExFreePool(devices);
return Status;
}
NTSTATUS STDCALL _load_tree(device_extension* Vcb, UINT64 addr, root* r, tree** pt, const char* func, const char* file, unsigned int line) {
NTSTATUS STDCALL _load_tree(device_extension* Vcb, UINT64 addr, root* r, tree** pt, tree* parent, const char* func, const char* file, unsigned int line) {
UINT8* buf;
NTSTATUS Status;
tree_header* th;
tree* t;
tree_data* td;
chunk* c;
shared_data* sd;
TRACE("(%p, %llx)\n", Vcb, addr);
@ -377,9 +42,9 @@ NTSTATUS STDCALL _load_tree(device_extension* Vcb, UINT64 addr, root* r, tree**
return STATUS_INSUFFICIENT_RESOURCES;
}
Status = read_tree(Vcb, addr, buf);
Status = read_data(Vcb, addr, Vcb->superblock.node_size, NULL, TRUE, buf, &c, NULL);
if (!NT_SUCCESS(Status)) {
ERR("read_tree returned 0x%08x\n", Status);
ERR("read_data returned 0x%08x\n", Status);
ExFreePool(buf);
return Status;
}
@ -407,8 +72,6 @@ NTSTATUS STDCALL _load_tree(device_extension* Vcb, UINT64 addr, root* r, tree**
t->has_new_address = FALSE;
t->write = FALSE;
c = get_chunk_from_address(Vcb, addr);
if (c)
t->flags = c->chunk_item->type;
else
@ -419,10 +82,31 @@ NTSTATUS STDCALL _load_tree(device_extension* Vcb, UINT64 addr, root* r, tree**
// t->items = ExAllocatePoolWithTag(PagedPool, num_items * sizeof(tree_data), ALLOC_TAG);
InitializeListHead(&t->itemlist);
if (t->header.flags & HEADER_FLAG_SHARED_BACKREF || !(t->header.flags & HEADER_FLAG_MIXED_BACKREF)) {
sd = ExAllocatePoolWithTag(NonPagedPool, sizeof(shared_data), ALLOC_TAG);
if (!sd) {
ERR("out of memory\n");
ExFreePool(buf);
return STATUS_INSUFFICIENT_RESOURCES;
}
sd->address = addr;
sd->parent = parent ? parent->header.address : addr;
InitializeListHead(&sd->entries);
ExInterlockedInsertTailList(&Vcb->shared_extents, &sd->list_entry, &Vcb->shared_extents_lock);
}
if (t->header.level == 0) { // leaf node
leaf_node* ln = (leaf_node*)(buf + sizeof(tree_header));
unsigned int i;
if ((t->header.num_items * sizeof(leaf_node)) + sizeof(tree_header) > Vcb->superblock.node_size) {
ERR("tree at %llx has more items than expected (%x)\n", t->header.num_items);
ExFreePool(buf);
return STATUS_INSUFFICIENT_RESOURCES;
}
for (i = 0; i < t->header.num_items; i++) {
td = ExAllocatePoolWithTag(PagedPool, sizeof(tree_data), ALLOC_TAG);
if (!td) {
@ -446,6 +130,55 @@ NTSTATUS STDCALL _load_tree(device_extension* Vcb, UINT64 addr, root* r, tree**
} else
td->data = NULL;
if ((t->header.flags & HEADER_FLAG_SHARED_BACKREF || !(t->header.flags & HEADER_FLAG_MIXED_BACKREF)) &&
ln[i].key.obj_type == TYPE_EXTENT_DATA && ln[i].size >= sizeof(EXTENT_DATA)) {
EXTENT_DATA* ed = (EXTENT_DATA*)td->data;
if ((ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) && ln[i].size >= sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) {
EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data;
if (ed2->size != 0) {
LIST_ENTRY* le;
BOOL found = FALSE;
TRACE("shared extent %llx,%llx\n", ed2->address, ed2->size);
le = sd->entries.Flink;
while (le != &sd->entries) {
shared_data_entry* sde = CONTAINING_RECORD(le, shared_data_entry, list_entry);
if (sde->address == ed2->address && sde->size == ed2->size && sde->edr.root == t->header.tree_id &&
sde->edr.objid == ln[i].key.obj_id && sde->edr.offset == ln[i].key.offset - ed2->offset) {
sde->edr.count++;
found = TRUE;
break;
}
le = le->Flink;
}
if (!found) {
shared_data_entry* sde = ExAllocatePoolWithTag(PagedPool, sizeof(shared_data_entry), ALLOC_TAG);
if (!sde) {
ERR("out of memory\n");
ExFreePool(buf);
return STATUS_INSUFFICIENT_RESOURCES;
}
sde->address = ed2->address;
sde->size = ed2->size;
sde->edr.root = t->header.tree_id;
sde->edr.objid = ln[i].key.obj_id;
sde->edr.offset = ln[i].key.offset - ed2->offset;
sde->edr.count = 1;
InsertTailList(&sd->entries, &sde->list_entry);
}
}
}
}
td->size = ln[i].size;
td->ignore = FALSE;
td->inserted = FALSE;
@ -460,6 +193,12 @@ NTSTATUS STDCALL _load_tree(device_extension* Vcb, UINT64 addr, root* r, tree**
internal_node* in = (internal_node*)(buf + sizeof(tree_header));
unsigned int i;
if ((t->header.num_items * sizeof(internal_node)) + sizeof(tree_header) > Vcb->superblock.node_size) {
ERR("tree at %llx has more items than expected (%x)\n", t->header.num_items);
ExFreePool(buf);
return STATUS_INSUFFICIENT_RESOURCES;
}
for (i = 0; i < t->header.num_items; i++) {
td = ExAllocatePoolWithTag(PagedPool, sizeof(tree_data), ALLOC_TAG);
if (!td) {
@ -474,7 +213,6 @@ NTSTATUS STDCALL _load_tree(device_extension* Vcb, UINT64 addr, root* r, tree**
td->treeholder.address = in[i].address;
td->treeholder.generation = in[i].generation;
td->treeholder.tree = NULL;
init_tree_holder(&td->treeholder);
// td->treeholder.nonpaged->status = tree_holder_unloaded;
td->ignore = FALSE;
td->inserted = FALSE;
@ -587,7 +325,7 @@ NTSTATUS STDCALL _do_load_tree(device_extension* Vcb, tree_holder* th, root* r,
if (!th->tree) {
NTSTATUS Status;
Status = _load_tree(Vcb, th->address, r, &th->tree, func, file, line);
Status = _load_tree(Vcb, th->address, r, &th->tree, t, func, file, line);
if (!NT_SUCCESS(Status)) {
ERR("load_tree returned %08x\n", Status);
ExReleaseResourceLite(&r->nonpaged->load_tree_lock);
@ -964,17 +702,20 @@ void free_trees_root(device_extension* Vcb, root* r) {
LIST_ENTRY* nextle = le->Flink;
tree* t = CONTAINING_RECORD(le, tree, list_entry);
if (t->root == r && t->header.level == level) {
BOOL top = !t->paritem;
empty = FALSE;
free_tree2(t, funcname, __FILE__, __LINE__);
if (top && r->treeholder.tree == t)
r->treeholder.tree = NULL;
if (IsListEmpty(&Vcb->trees))
return;
if (t->root == r) {
if (t->header.level == level) {
BOOL top = !t->paritem;
empty = FALSE;
free_tree2(t, funcname, __FILE__, __LINE__);
if (top && r->treeholder.tree == t)
r->treeholder.tree = NULL;
if (IsListEmpty(&Vcb->trees))
return;
} else if (t->header.level > level)
empty = FALSE;
}
le = nextle;
@ -986,22 +727,59 @@ void free_trees_root(device_extension* Vcb, root* r) {
}
void STDCALL free_trees(device_extension* Vcb) {
tree* t;
root* r;
while (!IsListEmpty(&Vcb->trees)) {
t = CONTAINING_RECORD(Vcb->trees.Flink, tree, list_entry);
r = t->root;
LIST_ENTRY* le;
UINT8 level;
for (level = 0; level <= 255; level++) {
BOOL empty = TRUE;
ExAcquireResourceExclusiveLite(&r->nonpaged->load_tree_lock, TRUE);
le = Vcb->trees.Flink;
free_trees_root(Vcb, r);
while (le != &Vcb->trees) {
LIST_ENTRY* nextle = le->Flink;
tree* t = CONTAINING_RECORD(le, tree, list_entry);
root* r = t->root;
if (t->header.level == level) {
BOOL top = !t->paritem;
empty = FALSE;
free_tree2(t, funcname, __FILE__, __LINE__);
if (top && r->treeholder.tree == t)
r->treeholder.tree = NULL;
if (IsListEmpty(&Vcb->trees))
goto free_shared;
} else if (t->header.level > level)
empty = FALSE;
le = nextle;
}
ExReleaseResourceLite(&r->nonpaged->load_tree_lock);
if (empty)
break;
}
free_shared:
while (!IsListEmpty(&Vcb->shared_extents)) {
shared_data* sd;
le = RemoveHeadList(&Vcb->shared_extents);
sd = CONTAINING_RECORD(le, shared_data, list_entry);
while (!IsListEmpty(&sd->entries)) {
LIST_ENTRY* le2 = RemoveHeadList(&sd->entries);
shared_data_entry* sde = CONTAINING_RECORD(le2, shared_data_entry, list_entry);
ExFreePool(sde);
}
ExFreePool(sd);
}
}
static void add_rollback(LIST_ENTRY* rollback, enum rollback_type type, void* ptr) {
void add_rollback(LIST_ENTRY* rollback, enum rollback_type type, void* ptr) {
rollback_item* ri;
ri = ExAllocatePoolWithTag(PagedPool, sizeof(rollback_item), ALLOC_TAG);
@ -1031,6 +809,13 @@ BOOL STDCALL insert_tree_item(device_extension* Vcb, root* r, UINT64 obj_id, UIN
TRACE("(%p, %p, %llx, %x, %llx, %p, %x, %p, %p)\n", Vcb, r, obj_id, obj_type, offset, data, size, ptp, rollback);
#ifdef DEBUG_PARANOID
if (!ExIsResourceAcquiredExclusiveLite(&Vcb->tree_lock)) {
ERR("ERROR - tree_lock not held exclusively\n");
int3;
}
#endif
searchkey.obj_id = obj_id;
searchkey.obj_type = obj_type;
searchkey.offset = offset;
@ -1123,7 +908,7 @@ BOOL STDCALL insert_tree_item(device_extension* Vcb, root* r, UINT64 obj_id, UIN
if (!tp.tree->write) {
tp.tree->write = TRUE;
Vcb->write_trees++;
Vcb->need_write = TRUE;
}
if (ptp)
@ -1185,6 +970,11 @@ void STDCALL delete_tree_item(device_extension* Vcb, traverse_ptr* tp, LIST_ENTR
TRACE("deleting item %llx,%x,%llx (ignore = %s)\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset, tp->item->ignore ? "TRUE" : "FALSE");
#ifdef DEBUG_PARANOID
if (!ExIsResourceAcquiredExclusiveLite(&Vcb->tree_lock)) {
ERR("ERROR - tree_lock not held exclusively\n");
int3;
}
if (tp->item->ignore) {
ERR("trying to delete already-deleted item %llx,%x,%llx\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset);
int3;
@ -1195,7 +985,7 @@ void STDCALL delete_tree_item(device_extension* Vcb, traverse_ptr* tp, LIST_ENTR
if (!tp->tree->write) {
tp->tree->write = TRUE;
Vcb->write_trees++;
Vcb->need_write = TRUE;
}
tp->tree->header.num_items--;
@ -1237,6 +1027,9 @@ void clear_rollback(LIST_ENTRY* rollback) {
case ROLLBACK_DELETE_ITEM:
ExFreePool(ri->ptr);
break;
default:
break;
}
ExFreePool(ri);
@ -1247,7 +1040,7 @@ void do_rollback(device_extension* Vcb, LIST_ENTRY* rollback) {
rollback_item* ri;
while (!IsListEmpty(rollback)) {
LIST_ENTRY* le = RemoveHeadList(rollback);
LIST_ENTRY* le = RemoveTailList(rollback);
ri = CONTAINING_RECORD(le, rollback_item, list_entry);
switch (ri->type) {
@ -1286,6 +1079,22 @@ void do_rollback(device_extension* Vcb, LIST_ENTRY* rollback) {
ExFreePool(tp);
break;
}
case ROLLBACK_INSERT_EXTENT:
{
extent* ext = ri->ptr;
ext->ignore = TRUE;
break;
}
case ROLLBACK_DELETE_EXTENT:
{
extent* ext = ri->ptr;
ext->ignore = FALSE;
break;
}
}
ExFreePool(ri);

View file

@ -0,0 +1,108 @@
#include "btrfs_drv.h"
void do_read_job(PIRP Irp) {
NTSTATUS Status;
ULONG bytes_read;
BOOL top_level = is_top_level(Irp);
Irp->IoStatus.Information = 0;
Status = do_read(Irp, TRUE, &bytes_read);
Irp->IoStatus.Status = Status;
// // 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);
IoCompleteRequest(Irp, IO_NO_INCREMENT);
if (top_level)
IoSetTopLevelIrp(NULL);
}
void do_write_job(device_extension* Vcb, PIRP Irp) {
BOOL top_level = is_top_level(Irp);
NTSTATUS Status;
_SEH2_TRY {
Status = write_file(Vcb, Irp, TRUE, TRUE);
} _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
Status = _SEH2_GetExceptionCode();
} _SEH2_END;
Irp->IoStatus.Status = Status;
TRACE("wrote %u bytes\n", Irp->IoStatus.Information);
IoCompleteRequest(Irp, IO_NO_INCREMENT);
if (top_level)
IoSetTopLevelIrp(NULL);
TRACE("returning %08x\n", Status);
}
static void do_job(drv_thread* thread, LIST_ENTRY* le) {
thread_job* tj = CONTAINING_RECORD(le, thread_job, list_entry);
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(tj->Irp);
if (IrpSp->MajorFunction == IRP_MJ_READ) {
do_read_job(tj->Irp);
} else if (IrpSp->MajorFunction == IRP_MJ_WRITE) {
do_write_job(thread->DeviceObject->DeviceExtension, tj->Irp);
} else {
ERR("unsupported major function %x\n", IrpSp->MajorFunction);
tj->Irp->IoStatus.Status = STATUS_INTERNAL_ERROR;
tj->Irp->IoStatus.Information = 0;
IoCompleteRequest(tj->Irp, IO_NO_INCREMENT);
}
ExFreePool(tj);
}
void STDCALL worker_thread(void* context) {
drv_thread* thread = context;
KIRQL irql;
ObReferenceObject(thread->DeviceObject);
while (TRUE) {
KeWaitForSingleObject(&thread->event, Executive, KernelMode, FALSE, NULL);
FsRtlEnterFileSystem();
while (TRUE) {
LIST_ENTRY* le;
KeAcquireSpinLock(&thread->spin_lock, &irql);
if (IsListEmpty(&thread->jobs)) {
KeReleaseSpinLock(&thread->spin_lock, irql);
break;
}
le = thread->jobs.Flink;
RemoveEntryList(le);
KeReleaseSpinLock(&thread->spin_lock, irql);
do_job(thread, le);
}
FsRtlExitFileSystem();
if (thread->quit)
break;
}
ObDereferenceObject(thread->DeviceObject);
KeSetEvent(&thread->finished, 0, FALSE);
PsTerminateSystemThread(STATUS_SUCCESS);
}

File diff suppressed because it is too large Load diff