1 /* Copyright (c) Mark Harmstone 2016
3 * This file is part of WinBtrfs.
5 * WinBtrfs is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public Licence as published by
7 * the Free Software Foundation, either version 3 of the Licence, or
8 * (at your option) any later version.
10 * WinBtrfs is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public Licence for more details.
15 * You should have received a copy of the GNU Lesser General Public Licence
16 * along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */
18 #include "btrfs_drv.h"
20 NTSTATUS
get_reparse_point(PDEVICE_OBJECT DeviceObject
, PFILE_OBJECT FileObject
, void* buffer
, DWORD buflen
, DWORD
* retlen
) {
21 USHORT subnamelen
, printnamelen
, i
;
24 REPARSE_DATA_BUFFER
* rdb
= buffer
;
25 fcb
* fcb
= FileObject
->FsContext
;
29 // FIXME - check permissions
31 TRACE("(%p, %p, %p, %x, %p)\n", DeviceObject
, FileObject
, buffer
, buflen
, retlen
);
33 acquire_tree_lock(fcb
->Vcb
, FALSE
);
35 if (fcb
->type
== BTRFS_TYPE_SYMLINK
) {
36 data
= ExAllocatePoolWithTag(PagedPool
, fcb
->inode_item
.st_size
, ALLOC_TAG
);
38 ERR("out of memory\n");
39 Status
= STATUS_INSUFFICIENT_RESOURCES
;
43 TRACE("data = %p, size = %x\n", data
, fcb
->inode_item
.st_size
);
44 Status
= read_file(DeviceObject
->DeviceExtension
, fcb
->subvol
, fcb
->inode
, (UINT8
*)data
, 0, fcb
->inode_item
.st_size
, NULL
);
46 if (!NT_SUCCESS(Status
)) {
47 ERR("read_file returned %08x\n", Status
);
52 Status
= RtlUTF8ToUnicodeN(NULL
, 0, &stringlen
, data
, fcb
->inode_item
.st_size
);
53 if (!NT_SUCCESS(Status
)) {
54 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status
);
59 subnamelen
= stringlen
;
60 printnamelen
= stringlen
;
62 reqlen
= offsetof(REPARSE_DATA_BUFFER
, SymbolicLinkReparseBuffer
.PathBuffer
) + subnamelen
+ printnamelen
;
64 if (buflen
< reqlen
) {
65 Status
= STATUS_BUFFER_OVERFLOW
;
69 rdb
->ReparseTag
= IO_REPARSE_TAG_SYMLINK
;
70 rdb
->ReparseDataLength
= reqlen
- offsetof(REPARSE_DATA_BUFFER
, SymbolicLinkReparseBuffer
);
73 rdb
->SymbolicLinkReparseBuffer
.SubstituteNameOffset
= 0;
74 rdb
->SymbolicLinkReparseBuffer
.SubstituteNameLength
= subnamelen
;
75 rdb
->SymbolicLinkReparseBuffer
.PrintNameOffset
= subnamelen
;
76 rdb
->SymbolicLinkReparseBuffer
.PrintNameLength
= printnamelen
;
77 rdb
->SymbolicLinkReparseBuffer
.Flags
= SYMLINK_FLAG_RELATIVE
;
79 Status
= RtlUTF8ToUnicodeN(&rdb
->SymbolicLinkReparseBuffer
.PathBuffer
[rdb
->SymbolicLinkReparseBuffer
.SubstituteNameOffset
/ sizeof(WCHAR
)],
80 stringlen
, &stringlen
, data
, fcb
->inode_item
.st_size
);
82 if (!NT_SUCCESS(Status
)) {
83 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status
);
88 for (i
= 0; i
< stringlen
/ sizeof(WCHAR
); i
++) {
89 if (rdb
->SymbolicLinkReparseBuffer
.PathBuffer
[(rdb
->SymbolicLinkReparseBuffer
.SubstituteNameOffset
/ sizeof(WCHAR
)) + i
] == '/')
90 rdb
->SymbolicLinkReparseBuffer
.PathBuffer
[(rdb
->SymbolicLinkReparseBuffer
.SubstituteNameOffset
/ sizeof(WCHAR
)) + i
] = '\\';
93 RtlCopyMemory(&rdb
->SymbolicLinkReparseBuffer
.PathBuffer
[rdb
->SymbolicLinkReparseBuffer
.PrintNameOffset
/ sizeof(WCHAR
)],
94 &rdb
->SymbolicLinkReparseBuffer
.PathBuffer
[rdb
->SymbolicLinkReparseBuffer
.SubstituteNameOffset
/ sizeof(WCHAR
)],
95 rdb
->SymbolicLinkReparseBuffer
.SubstituteNameLength
);
101 Status
= STATUS_SUCCESS
;
102 } else if (fcb
->atts
& FILE_ATTRIBUTE_REPARSE_POINT
) {
103 if (fcb
->type
== BTRFS_TYPE_FILE
) {
104 Status
= read_file(DeviceObject
->DeviceExtension
, fcb
->subvol
, fcb
->inode
, buffer
, 0, buflen
, retlen
);
106 if (!NT_SUCCESS(Status
)) {
107 ERR("read_file returned %08x\n", Status
);
109 } else if (fcb
->type
== BTRFS_TYPE_DIRECTORY
) {
113 if (!get_xattr(fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, EA_REPARSE
, EA_REPARSE_HASH
, &data
, &datalen
)) {
114 Status
= STATUS_NOT_A_REPARSE_POINT
;
119 Status
= STATUS_NOT_A_REPARSE_POINT
;
123 if (datalen
< sizeof(ULONG
)) {
125 Status
= STATUS_NOT_A_REPARSE_POINT
;
130 *retlen
= min(buflen
, datalen
);
131 RtlCopyMemory(buffer
, data
, *retlen
);
137 Status
= STATUS_NOT_A_REPARSE_POINT
;
139 Status
= STATUS_NOT_A_REPARSE_POINT
;
143 release_tree_lock(fcb
->Vcb
, FALSE
);
148 static NTSTATUS
change_file_type(device_extension
* Vcb
, UINT64 inode
, root
* subvol
, UINT64 parinode
, UINT64 index
, PANSI_STRING utf8
, UINT8 type
, LIST_ENTRY
* rollback
) {
154 crc32
= calc_crc32c(0xfffffffe, (UINT8
*)utf8
->Buffer
, (ULONG
)utf8
->Length
);
156 searchkey
.obj_id
= parinode
;
157 searchkey
.obj_type
= TYPE_DIR_ITEM
;
158 searchkey
.offset
= crc32
;
160 Status
= find_item(Vcb
, subvol
, &tp
, &searchkey
, FALSE
);
161 if (!NT_SUCCESS(Status
)) {
162 ERR("error - find_item returned %08x\n", Status
);
166 if (!keycmp(&tp
.item
->key
, &searchkey
)) {
167 if (tp
.item
->size
< sizeof(DIR_ITEM
)) {
168 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
));
170 DIR_ITEM
*di
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
, ALLOC_TAG
), *di2
;
172 ULONG len
= tp
.item
->size
;
175 ERR("out of memory\n");
176 return STATUS_INSUFFICIENT_RESOURCES
;
179 RtlCopyMemory(di
, tp
.item
->data
, tp
.item
->size
);
183 if (len
< sizeof(DIR_ITEM
) || len
< sizeof(DIR_ITEM
) - 1 + di2
->m
+ di2
->n
) {
184 ERR("(%llx,%x,%llx) was truncated\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
188 if (di2
->n
== utf8
->Length
&& RtlCompareMemory(di2
->name
, utf8
->Buffer
, utf8
->Length
) == utf8
->Length
) {
194 if (len
> sizeof(DIR_ITEM
) - sizeof(char) + di2
->m
+ di2
->n
) {
195 len
-= sizeof(DIR_ITEM
) - sizeof(char) + di2
->m
+ di2
->n
;
196 di2
= (DIR_ITEM
*)&di2
->name
[di2
->m
+ di2
->n
];
202 delete_tree_item(Vcb
, &tp
, rollback
);
203 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
);
208 WARN("search for DIR_ITEM by crc32 failed\n");
211 searchkey
.obj_id
= parinode
;
212 searchkey
.obj_type
= TYPE_DIR_INDEX
;
213 searchkey
.offset
= index
;
215 Status
= find_item(Vcb
, subvol
, &tp
, &searchkey
, FALSE
);
216 if (!NT_SUCCESS(Status
)) {
217 ERR("error - find_item returned %08x\n", Status
);
221 if (!keycmp(&tp
.item
->key
, &searchkey
)) {
222 if (tp
.item
->size
< sizeof(DIR_ITEM
)) {
223 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
));
225 DIR_ITEM
* di
= (DIR_ITEM
*)tp
.item
->data
;
226 DIR_ITEM
* di2
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
, ALLOC_TAG
);
228 ERR("out of memory\n");
229 return STATUS_INSUFFICIENT_RESOURCES
;
232 RtlCopyMemory(di2
, di
, tp
.item
->size
);
235 delete_tree_item(Vcb
, &tp
, rollback
);
236 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
);
240 return STATUS_SUCCESS
;
243 static NTSTATUS
set_symlink(PIRP Irp
, fcb
* fcb
, REPARSE_DATA_BUFFER
* rdb
, ULONG buflen
, LIST_ENTRY
* rollback
) {
246 UNICODE_STRING subname
;
249 traverse_ptr tp
, next_tp
;
251 LARGE_INTEGER offset
;
254 minlen
= offsetof(REPARSE_DATA_BUFFER
, SymbolicLinkReparseBuffer
.PathBuffer
) + sizeof(WCHAR
);
255 if (buflen
< minlen
) {
256 WARN("buffer was less than minimum length (%u < %u)\n", buflen
, minlen
);
257 return STATUS_INVALID_PARAMETER
;
260 subname
.Buffer
= &rdb
->SymbolicLinkReparseBuffer
.PathBuffer
[rdb
->SymbolicLinkReparseBuffer
.SubstituteNameOffset
/ sizeof(WCHAR
)];
261 subname
.MaximumLength
= subname
.Length
= rdb
->SymbolicLinkReparseBuffer
.SubstituteNameLength
;
263 ERR("substitute name = %.*S\n", subname
.Length
/ sizeof(WCHAR
), subname
.Buffer
);
265 fcb
->type
= BTRFS_TYPE_SYMLINK
;
267 searchkey
.obj_id
= fcb
->inode
;
268 searchkey
.obj_type
= TYPE_INODE_REF
;
269 searchkey
.offset
= 0;
271 Status
= find_item(fcb
->Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
);
272 if (!NT_SUCCESS(Status
)) {
273 ERR("error - find_item returned %08x\n", Status
);
278 if (tp
.item
->key
.obj_id
== fcb
->inode
&& tp
.item
->key
.obj_type
== TYPE_INODE_REF
) {
279 if (tp
.item
->size
< sizeof(INODE_REF
)) {
280 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
));
283 ULONG size
= tp
.item
->size
;
286 ir
= (INODE_REF
*)tp
.item
->data
;
289 if (size
< sizeof(INODE_REF
) || size
< sizeof(INODE_REF
) - 1 + ir
->n
) {
290 WARN("(%llx,%x,%llx) was truncated\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
294 utf8
.Buffer
= ir
->name
;
295 utf8
.Length
= utf8
.MaximumLength
= ir
->n
;
297 Status
= change_file_type(fcb
->Vcb
, fcb
->inode
, fcb
->subvol
, tp
.item
->key
.offset
, ir
->index
, &utf8
, BTRFS_TYPE_SYMLINK
, rollback
);
299 if (!NT_SUCCESS(Status
)) {
300 ERR("error - change_file_type returned %08x\n", Status
);
304 if (size
> sizeof(INODE_REF
) - 1 + ir
->n
) {
305 size
-= sizeof(INODE_REF
) - 1 + ir
->n
;
307 ir
= (INODE_REF
*)&ir
->name
[ir
->n
];
314 b
= find_next_item(fcb
->Vcb
, &tp
, &next_tp
, FALSE
);
318 b
= tp
.item
->key
.obj_id
== fcb
->inode
&& tp
.item
->key
.obj_type
== TYPE_INODE_REF
;
322 if (fcb
->Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF
) {
323 searchkey
.obj_id
= fcb
->inode
;
324 searchkey
.obj_type
= TYPE_INODE_EXTREF
;
325 searchkey
.offset
= 0;
327 Status
= find_item(fcb
->Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
);
328 if (!NT_SUCCESS(Status
)) {
329 ERR("error - find_item returned %08x\n", Status
);
334 if (tp
.item
->key
.obj_id
== fcb
->inode
&& tp
.item
->key
.obj_type
== TYPE_INODE_EXTREF
) {
335 if (tp
.item
->size
< sizeof(INODE_EXTREF
)) {
336 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
));
339 ULONG size
= tp
.item
->size
;
342 ier
= (INODE_EXTREF
*)tp
.item
->data
;
345 if (size
< sizeof(INODE_EXTREF
) || size
< sizeof(INODE_EXTREF
) - 1 + ier
->n
) {
346 WARN("(%llx,%x,%llx) was truncated\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
350 utf8
.Buffer
= ier
->name
;
351 utf8
.Length
= utf8
.MaximumLength
= ier
->n
;
353 Status
= change_file_type(fcb
->Vcb
, fcb
->inode
, fcb
->subvol
, ier
->dir
, ier
->index
, &utf8
, BTRFS_TYPE_SYMLINK
, rollback
);
355 if (!NT_SUCCESS(Status
)) {
356 ERR("error - change_file_type returned %08x\n", Status
);
360 if (size
> sizeof(INODE_EXTREF
) - 1 + ier
->n
) {
361 size
-= sizeof(INODE_EXTREF
) - 1 + ier
->n
;
363 ier
= (INODE_EXTREF
*)&ier
->name
[ier
->n
];
370 b
= find_next_item(fcb
->Vcb
, &tp
, &next_tp
, FALSE
);
374 b
= tp
.item
->key
.obj_id
== fcb
->inode
&& tp
.item
->key
.obj_type
== TYPE_INODE_EXTREF
;
379 fcb
->inode_item
.st_mode
|= __S_IFLNK
;
381 Status
= truncate_file(fcb
, 0, rollback
);
382 if (!NT_SUCCESS(Status
)) {
383 ERR("truncate_file returned %08x\n", Status
);
387 Status
= RtlUnicodeToUTF8N(NULL
, 0, (PULONG
)&target
.Length
, subname
.Buffer
, subname
.Length
);
388 if (!NT_SUCCESS(Status
)) {
389 ERR("RtlUnicodeToUTF8N 3 failed with error %08x\n", Status
);
393 target
.MaximumLength
= target
.Length
;
394 target
.Buffer
= ExAllocatePoolWithTag(PagedPool
, target
.MaximumLength
, ALLOC_TAG
);
395 if (!target
.Buffer
) {
396 ERR("out of memory\n");
397 return STATUS_INSUFFICIENT_RESOURCES
;
400 Status
= RtlUnicodeToUTF8N(target
.Buffer
, target
.Length
, (PULONG
)&target
.Length
, subname
.Buffer
, subname
.Length
);
401 if (!NT_SUCCESS(Status
)) {
402 ERR("RtlUnicodeToUTF8N 4 failed with error %08x\n", Status
);
403 ExFreePool(target
.Buffer
);
407 for (i
= 0; i
< target
.Length
; i
++) {
408 if (target
.Buffer
[i
] == '\\')
409 target
.Buffer
[i
] = '/';
413 Status
= write_file2(fcb
->Vcb
, Irp
, offset
, target
.Buffer
, (ULONG
*)&target
.Length
, Irp
->Flags
& IRP_PAGING_IO
, Irp
->Flags
& IRP_NOCACHE
, rollback
);
415 ExFreePool(target
.Buffer
);
420 static NTSTATUS
delete_symlink(fcb
* fcb
, LIST_ENTRY
* rollback
) {
423 traverse_ptr tp
, next_tp
;
428 searchkey
.obj_id
= fcb
->inode
;
429 searchkey
.obj_type
= TYPE_INODE_REF
;
430 searchkey
.offset
= 0;
432 Status
= find_item(fcb
->Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
);
433 if (!NT_SUCCESS(Status
)) {
434 ERR("error - find_item returned %08x\n", Status
);
439 if (tp
.item
->key
.obj_id
== fcb
->inode
&& tp
.item
->key
.obj_type
== TYPE_INODE_REF
) {
440 if (tp
.item
->size
< sizeof(INODE_REF
)) {
441 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
));
444 ULONG size
= tp
.item
->size
;
447 ir
= (INODE_REF
*)tp
.item
->data
;
450 if (size
< sizeof(INODE_REF
) || size
< sizeof(INODE_REF
) - 1 + ir
->n
) {
451 WARN("(%llx,%x,%llx) was truncated\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
455 utf8
.Buffer
= ir
->name
;
456 utf8
.Length
= utf8
.MaximumLength
= ir
->n
;
458 Status
= change_file_type(fcb
->Vcb
, fcb
->inode
, fcb
->subvol
, tp
.item
->key
.offset
, ir
->index
, &utf8
, BTRFS_TYPE_FILE
, rollback
);
460 if (!NT_SUCCESS(Status
)) {
461 ERR("error - change_file_type returned %08x\n", Status
);
465 if (size
> sizeof(INODE_REF
) - 1 + ir
->n
) {
466 size
-= sizeof(INODE_REF
) - 1 + ir
->n
;
468 ir
= (INODE_REF
*)&ir
->name
[ir
->n
];
475 b
= find_next_item(fcb
->Vcb
, &tp
, &next_tp
, FALSE
);
479 b
= tp
.item
->key
.obj_id
== fcb
->inode
&& tp
.item
->key
.obj_type
== TYPE_INODE_REF
;
483 if (fcb
->Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF
) {
484 searchkey
.obj_id
= fcb
->inode
;
485 searchkey
.obj_type
= TYPE_INODE_EXTREF
;
486 searchkey
.offset
= 0;
488 Status
= find_item(fcb
->Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
);
489 if (!NT_SUCCESS(Status
)) {
490 ERR("error - find_item returned %08x\n", Status
);
495 if (tp
.item
->key
.obj_id
== fcb
->inode
&& tp
.item
->key
.obj_type
== TYPE_INODE_EXTREF
) {
496 if (tp
.item
->size
< sizeof(INODE_EXTREF
)) {
497 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
));
500 ULONG size
= tp
.item
->size
;
503 ier
= (INODE_EXTREF
*)tp
.item
->data
;
506 if (size
< sizeof(INODE_EXTREF
) || size
< sizeof(INODE_EXTREF
) - 1 + ier
->n
) {
507 WARN("(%llx,%x,%llx) was truncated\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
511 utf8
.Buffer
= ier
->name
;
512 utf8
.Length
= utf8
.MaximumLength
= ier
->n
;
514 Status
= change_file_type(fcb
->Vcb
, fcb
->inode
, fcb
->subvol
, ier
->dir
, ier
->index
, &utf8
, BTRFS_TYPE_FILE
, rollback
);
516 if (!NT_SUCCESS(Status
)) {
517 ERR("error - change_file_type returned %08x\n", Status
);
521 if (size
> sizeof(INODE_EXTREF
) - 1 + ier
->n
) {
522 size
-= sizeof(INODE_EXTREF
) - 1 + ier
->n
;
524 ier
= (INODE_EXTREF
*)&ier
->name
[ier
->n
];
531 b
= find_next_item(fcb
->Vcb
, &tp
, &next_tp
, FALSE
);
535 b
= tp
.item
->key
.obj_id
== fcb
->inode
&& tp
.item
->key
.obj_type
== TYPE_INODE_EXTREF
;
540 Status
= truncate_file(fcb
, 0, rollback
);
541 if (!NT_SUCCESS(Status
)) {
542 ERR("truncate_file returned %08x\n", Status
);
546 KeQuerySystemTime(&time
);
548 win_time_to_unix(time
, &now
);
550 fcb
->type
= BTRFS_TYPE_FILE
;
551 fcb
->inode_item
.st_mode
&= ~__S_IFLNK
;
552 fcb
->inode_item
.st_mode
|= __S_IFREG
;
553 fcb
->inode_item
.transid
= fcb
->Vcb
->superblock
.generation
;
554 fcb
->inode_item
.sequence
++;
555 fcb
->inode_item
.st_ctime
= now
;
556 fcb
->inode_item
.st_mtime
= now
;
558 Status
= update_inode_item(fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, &fcb
->inode_item
, rollback
);
559 if (!NT_SUCCESS(Status
)) {
560 ERR("update_inode_item returned %08x\n", Status
);
564 fcb
->subvol
->root_item
.ctransid
= fcb
->Vcb
->superblock
.generation
;
565 fcb
->subvol
->root_item
.ctime
= now
;
570 NTSTATUS
set_reparse_point(PDEVICE_OBJECT DeviceObject
, PIRP Irp
) {
571 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
572 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
573 void* buffer
= Irp
->AssociatedIrp
.SystemBuffer
;
574 REPARSE_DATA_BUFFER
* rdb
= buffer
;
575 DWORD buflen
= IrpSp
->Parameters
.DeviceIoControl
.InputBufferLength
;
576 NTSTATUS Status
= STATUS_SUCCESS
;
581 // FIXME - send notification if this succeeds? The attributes will have changed.
582 // FIXME - check permissions
584 TRACE("(%p, %p)\n", DeviceObject
, Irp
);
586 InitializeListHead(&rollback
);
589 ERR("FileObject was NULL\n");
590 return STATUS_INVALID_PARAMETER
;
593 fcb
= FileObject
->FsContext
;
595 TRACE("%S\n", file_desc(FileObject
));
597 acquire_tree_lock(fcb
->Vcb
, TRUE
);
599 if (fcb
->type
== BTRFS_TYPE_SYMLINK
) {
600 WARN("tried to set a reparse point on an existing symlink\n");
601 Status
= STATUS_INVALID_PARAMETER
;
605 // FIXME - fail if we already have the attribute FILE_ATTRIBUTE_REPARSE_POINT
607 // FIXME - die if not file or directory
608 // FIXME - die if ADS
610 if (buflen
< sizeof(ULONG
)) {
611 WARN("buffer was not long enough to hold tag\n");
612 Status
= STATUS_INVALID_PARAMETER
;
616 RtlCopyMemory(&tag
, buffer
, sizeof(ULONG
));
618 if (fcb
->type
== BTRFS_TYPE_FILE
&& tag
== IO_REPARSE_TAG_SYMLINK
&& rdb
->SymbolicLinkReparseBuffer
.Flags
& SYMLINK_FLAG_RELATIVE
) {
619 Status
= set_symlink(Irp
, fcb
, rdb
, buflen
, &rollback
);
621 LARGE_INTEGER offset
;
624 if (fcb
->type
== BTRFS_TYPE_DIRECTORY
) { // for directories, store as xattr
625 Status
= set_xattr(fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, EA_REPARSE
, EA_REPARSE_HASH
, buffer
, buflen
, &rollback
);
626 if (!NT_SUCCESS(Status
)) {
627 ERR("set_xattr returned %08x\n", Status
);
630 } else { // otherwise, store as file data
631 Status
= truncate_file(fcb
, 0, &rollback
);
632 if (!NT_SUCCESS(Status
)) {
633 ERR("truncate_file returned %08x\n", Status
);
639 Status
= write_file2(fcb
->Vcb
, Irp
, offset
, buffer
, &buflen
, Irp
->Flags
& IRP_PAGING_IO
, Irp
->Flags
& IRP_NOCACHE
, &rollback
);
640 if (!NT_SUCCESS(Status
)) {
641 ERR("write_file2 returned %08x\n", Status
);
646 fcb
->atts
|= FILE_ATTRIBUTE_REPARSE_POINT
;
648 sprintf(val
, "0x%lx", fcb
->atts
);
650 Status
= set_xattr(fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, EA_DOSATTRIB
, EA_DOSATTRIB_HASH
, (UINT8
*)val
, strlen(val
), &rollback
);
651 if (!NT_SUCCESS(Status
)) {
652 ERR("set_xattr returned %08x\n", Status
);
657 if (NT_SUCCESS(Status
))
658 Status
= consider_write(fcb
->Vcb
);
661 if (NT_SUCCESS(Status
))
662 clear_rollback(&rollback
);
664 do_rollback(fcb
->Vcb
, &rollback
);
666 release_tree_lock(fcb
->Vcb
, TRUE
);
671 NTSTATUS
delete_reparse_point(PDEVICE_OBJECT DeviceObject
, PIRP Irp
) {
672 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
673 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
674 REPARSE_DATA_BUFFER
* rdb
= Irp
->AssociatedIrp
.SystemBuffer
;
675 DWORD buflen
= IrpSp
->Parameters
.DeviceIoControl
.InputBufferLength
;
682 // FIXME - send notification if this succeeds? The attributes will have changed.
683 // FIXME - check permissions
685 TRACE("(%p, %p)\n", DeviceObject
, Irp
);
687 InitializeListHead(&rollback
);
690 ERR("FileObject was NULL\n");
691 return STATUS_INVALID_PARAMETER
;
694 fcb
= FileObject
->FsContext
;
695 ccb
= FileObject
->FsContext2
;
696 fileref
= ccb
? ccb
->fileref
: NULL
;
698 TRACE("%S\n", file_desc(FileObject
));
701 ERR("fileref was NULL\n");
702 return STATUS_INVALID_PARAMETER
;
705 if (buflen
< offsetof(REPARSE_DATA_BUFFER
, GenericReparseBuffer
.DataBuffer
)) {
706 ERR("buffer was too short\n");
707 return STATUS_INVALID_PARAMETER
;
710 if (rdb
->ReparseDataLength
> 0) {
711 WARN("rdb->ReparseDataLength was not zero\n");
712 return STATUS_INVALID_PARAMETER
;
715 acquire_tree_lock(fcb
->Vcb
, TRUE
);
718 WARN("tried to delete reparse point on ADS\n");
719 Status
= STATUS_INVALID_PARAMETER
;
723 if (fcb
->type
== BTRFS_TYPE_SYMLINK
) {
724 if (rdb
->ReparseTag
!= IO_REPARSE_TAG_SYMLINK
) {
725 WARN("reparse tag was not IO_REPARSE_TAG_SYMLINK\n");
726 Status
= STATUS_INVALID_PARAMETER
;
730 Status
= delete_symlink(fcb
, &rollback
);
731 if (!NT_SUCCESS(Status
)) {
732 ERR("delete_symlink returned %08x\n", Status
);
735 } else if (fcb
->type
== BTRFS_TYPE_FILE
) {
740 // FIXME - do we need to check that the reparse tags match?
742 Status
= truncate_file(fcb
, 0, &rollback
);
743 if (!NT_SUCCESS(Status
)) {
744 ERR("truncate_file returned %08x\n", Status
);
748 fcb
->atts
&= ~FILE_ATTRIBUTE_REPARSE_POINT
;
750 defda
= get_file_attributes(fcb
->Vcb
, &fcb
->inode_item
, fcb
->subvol
, fcb
->inode
, fcb
->type
, fileref
->filepart
.Length
> 0 && fileref
->filepart
.Buffer
[0] == '.', TRUE
);
752 if (defda
!= fcb
->atts
) {
755 sprintf(val
, "0x%lx", fcb
->atts
);
757 Status
= set_xattr(fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, EA_DOSATTRIB
, EA_DOSATTRIB_HASH
, (UINT8
*)val
, strlen(val
), &rollback
);
758 if (!NT_SUCCESS(Status
)) {
759 ERR("set_xattr returned %08x\n", Status
);
763 delete_xattr(fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, EA_DOSATTRIB
, EA_DOSATTRIB_HASH
, &rollback
);
765 KeQuerySystemTime(&time
);
767 win_time_to_unix(time
, &now
);
769 fcb
->inode_item
.transid
= fcb
->Vcb
->superblock
.generation
;
770 fcb
->inode_item
.sequence
++;
771 fcb
->inode_item
.st_ctime
= now
;
772 fcb
->inode_item
.st_mtime
= now
;
774 Status
= update_inode_item(fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, &fcb
->inode_item
, &rollback
);
775 if (!NT_SUCCESS(Status
)) {
776 ERR("update_inode_item returned %08x\n", Status
);
780 fcb
->subvol
->root_item
.ctransid
= fcb
->Vcb
->superblock
.generation
;
781 fcb
->subvol
->root_item
.ctime
= now
;
782 } else if (fcb
->type
== BTRFS_TYPE_DIRECTORY
) {
787 // FIXME - do we need to check that the reparse tags match?
789 fcb
->atts
&= ~FILE_ATTRIBUTE_REPARSE_POINT
;
791 defda
= get_file_attributes(fcb
->Vcb
, &fcb
->inode_item
, fcb
->subvol
, fcb
->inode
, fcb
->type
, fileref
->filepart
.Length
> 0 && fileref
->filepart
.Buffer
[0] == '.', TRUE
);
792 defda
|= FILE_ATTRIBUTE_DIRECTORY
;
794 if (defda
!= fcb
->atts
) {
797 sprintf(val
, "0x%lx", fcb
->atts
);
799 Status
= set_xattr(fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, EA_DOSATTRIB
, EA_DOSATTRIB_HASH
, (UINT8
*)val
, strlen(val
), &rollback
);
800 if (!NT_SUCCESS(Status
)) {
801 ERR("set_xattr returned %08x\n", Status
);
805 delete_xattr(fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, EA_DOSATTRIB
, EA_DOSATTRIB_HASH
, &rollback
);
807 delete_xattr(fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, EA_REPARSE
, EA_REPARSE_HASH
, &rollback
);
809 KeQuerySystemTime(&time
);
811 win_time_to_unix(time
, &now
);
813 fcb
->inode_item
.transid
= fcb
->Vcb
->superblock
.generation
;
814 fcb
->inode_item
.sequence
++;
815 fcb
->inode_item
.st_ctime
= now
;
816 fcb
->inode_item
.st_mtime
= now
;
818 Status
= update_inode_item(fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, &fcb
->inode_item
, &rollback
);
819 if (!NT_SUCCESS(Status
)) {
820 ERR("update_inode_item returned %08x\n", Status
);
824 fcb
->subvol
->root_item
.ctransid
= fcb
->Vcb
->superblock
.generation
;
825 fcb
->subvol
->root_item
.ctime
= now
;
827 ERR("unsupported file type %u\n", fcb
->type
);
828 Status
= STATUS_INVALID_PARAMETER
;
832 Status
= STATUS_SUCCESS
;
834 if (NT_SUCCESS(Status
))
835 Status
= consider_write(fcb
->Vcb
);
838 if (NT_SUCCESS(Status
))
839 clear_rollback(&rollback
);
841 do_rollback(fcb
->Vcb
, &rollback
);
843 release_tree_lock(fcb
->Vcb
, TRUE
);