[BTRFS]
[reactos.git] / reactos / drivers / filesystems / btrfs / reparse.c
1 /* Copyright (c) Mark Harmstone 2016
2 *
3 * This file is part of WinBtrfs.
4 *
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.
9 *
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.
14 *
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/>. */
17
18 #include "btrfs_drv.h"
19
20 NTSTATUS get_reparse_point(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, void* buffer, DWORD buflen, DWORD* retlen) {
21 USHORT subnamelen, printnamelen, i;
22 ULONG stringlen;
23 DWORD reqlen;
24 REPARSE_DATA_BUFFER* rdb = buffer;
25 fcb* fcb = FileObject->FsContext;
26 char* data;
27 NTSTATUS Status;
28
29 // FIXME - check permissions
30
31 TRACE("(%p, %p, %p, %x, %p)\n", DeviceObject, FileObject, buffer, buflen, retlen);
32
33 acquire_tree_lock(fcb->Vcb, FALSE);
34
35 if (fcb->type == BTRFS_TYPE_SYMLINK) {
36 data = ExAllocatePoolWithTag(PagedPool, fcb->inode_item.st_size, ALLOC_TAG);
37 if (!data) {
38 ERR("out of memory\n");
39 Status = STATUS_INSUFFICIENT_RESOURCES;
40 goto end;
41 }
42
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);
45
46 if (!NT_SUCCESS(Status)) {
47 ERR("read_file returned %08x\n", Status);
48 ExFreePool(data);
49 goto end;
50 }
51
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);
55 ExFreePool(data);
56 goto end;
57 }
58
59 subnamelen = stringlen;
60 printnamelen = stringlen;
61
62 reqlen = offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) + subnamelen + printnamelen;
63
64 if (buflen < reqlen) {
65 Status = STATUS_BUFFER_OVERFLOW;
66 goto end;
67 }
68
69 rdb->ReparseTag = IO_REPARSE_TAG_SYMLINK;
70 rdb->ReparseDataLength = reqlen - offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer);
71 rdb->Reserved = 0;
72
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;
78
79 Status = RtlUTF8ToUnicodeN(&rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)],
80 stringlen, &stringlen, data, fcb->inode_item.st_size);
81
82 if (!NT_SUCCESS(Status)) {
83 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
84 ExFreePool(data);
85 goto end;
86 }
87
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] = '\\';
91 }
92
93 RtlCopyMemory(&rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(WCHAR)],
94 &rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)],
95 rdb->SymbolicLinkReparseBuffer.SubstituteNameLength);
96
97 *retlen = reqlen;
98
99 ExFreePool(data);
100
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);
105
106 if (!NT_SUCCESS(Status)) {
107 ERR("read_file returned %08x\n", Status);
108 }
109 } else if (fcb->type == BTRFS_TYPE_DIRECTORY) {
110 UINT8* data;
111 UINT16 datalen;
112
113 if (!get_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_REPARSE, EA_REPARSE_HASH, &data, &datalen)) {
114 Status = STATUS_NOT_A_REPARSE_POINT;
115 goto end;
116 }
117
118 if (!data) {
119 Status = STATUS_NOT_A_REPARSE_POINT;
120 goto end;
121 }
122
123 if (datalen < sizeof(ULONG)) {
124 ExFreePool(data);
125 Status = STATUS_NOT_A_REPARSE_POINT;
126 goto end;
127 }
128
129 if (buflen > 0) {
130 *retlen = min(buflen, datalen);
131 RtlCopyMemory(buffer, data, *retlen);
132 } else
133 *retlen = 0;
134
135 ExFreePool(data);
136 } else
137 Status = STATUS_NOT_A_REPARSE_POINT;
138 } else {
139 Status = STATUS_NOT_A_REPARSE_POINT;
140 }
141
142 end:
143 release_tree_lock(fcb->Vcb, FALSE);
144
145 return Status;
146 }
147
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) {
149 KEY searchkey;
150 UINT32 crc32;
151 traverse_ptr tp;
152 NTSTATUS Status;
153
154 crc32 = calc_crc32c(0xfffffffe, (UINT8*)utf8->Buffer, (ULONG)utf8->Length);
155
156 searchkey.obj_id = parinode;
157 searchkey.obj_type = TYPE_DIR_ITEM;
158 searchkey.offset = crc32;
159
160 Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
161 if (!NT_SUCCESS(Status)) {
162 ERR("error - find_item returned %08x\n", Status);
163 return Status;
164 }
165
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));
169 } else {
170 DIR_ITEM *di = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG), *di2;
171 BOOL found = FALSE;
172 ULONG len = tp.item->size;
173
174 if (!di) {
175 ERR("out of memory\n");
176 return STATUS_INSUFFICIENT_RESOURCES;
177 }
178
179 RtlCopyMemory(di, tp.item->data, tp.item->size);
180
181 di2 = di;
182 do {
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);
185 break;
186 }
187
188 if (di2->n == utf8->Length && RtlCompareMemory(di2->name, utf8->Buffer, utf8->Length) == utf8->Length) {
189 di2->type = type;
190 found = TRUE;
191 break;
192 }
193
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];
197 } else
198 break;
199 } while (len > 0);
200
201 if (found) {
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);
204 } else
205 ExFreePool(di);
206 }
207 } else {
208 WARN("search for DIR_ITEM by crc32 failed\n");
209 }
210
211 searchkey.obj_id = parinode;
212 searchkey.obj_type = TYPE_DIR_INDEX;
213 searchkey.offset = index;
214
215 Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
216 if (!NT_SUCCESS(Status)) {
217 ERR("error - find_item returned %08x\n", Status);
218 return Status;
219 }
220
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));
224 } else {
225 DIR_ITEM* di = (DIR_ITEM*)tp.item->data;
226 DIR_ITEM* di2 = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
227 if (!di2) {
228 ERR("out of memory\n");
229 return STATUS_INSUFFICIENT_RESOURCES;
230 }
231
232 RtlCopyMemory(di2, di, tp.item->size);
233 di2->type = type;
234
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);
237 }
238 }
239
240 return STATUS_SUCCESS;
241 }
242
243 static NTSTATUS set_symlink(PIRP Irp, fcb* fcb, REPARSE_DATA_BUFFER* rdb, ULONG buflen, LIST_ENTRY* rollback) {
244 NTSTATUS Status;
245 ULONG minlen;
246 UNICODE_STRING subname;
247 ANSI_STRING target;
248 KEY searchkey;
249 traverse_ptr tp, next_tp;
250 BOOL b;
251 LARGE_INTEGER offset;
252 USHORT i;
253
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;
258 }
259
260 subname.Buffer = &rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)];
261 subname.MaximumLength = subname.Length = rdb->SymbolicLinkReparseBuffer.SubstituteNameLength;
262
263 ERR("substitute name = %.*S\n", subname.Length / sizeof(WCHAR), subname.Buffer);
264
265 fcb->type = BTRFS_TYPE_SYMLINK;
266
267 searchkey.obj_id = fcb->inode;
268 searchkey.obj_type = TYPE_INODE_REF;
269 searchkey.offset = 0;
270
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);
274 return Status;
275 }
276
277 do {
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));
281 } else {
282 INODE_REF* ir;
283 ULONG size = tp.item->size;
284 ANSI_STRING utf8;
285
286 ir = (INODE_REF*)tp.item->data;
287
288 do {
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);
291 break;
292 }
293
294 utf8.Buffer = ir->name;
295 utf8.Length = utf8.MaximumLength = ir->n;
296
297 Status = change_file_type(fcb->Vcb, fcb->inode, fcb->subvol, tp.item->key.offset, ir->index, &utf8, BTRFS_TYPE_SYMLINK, rollback);
298
299 if (!NT_SUCCESS(Status)) {
300 ERR("error - change_file_type returned %08x\n", Status);
301 return Status;
302 }
303
304 if (size > sizeof(INODE_REF) - 1 + ir->n) {
305 size -= sizeof(INODE_REF) - 1 + ir->n;
306
307 ir = (INODE_REF*)&ir->name[ir->n];
308 } else
309 break;
310 } while (TRUE);
311 }
312 }
313
314 b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE);
315 if (b) {
316 tp = next_tp;
317
318 b = tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_INODE_REF;
319 }
320 } while (b);
321
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;
326
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);
330 return Status;
331 }
332
333 do {
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));
337 } else {
338 INODE_EXTREF* ier;
339 ULONG size = tp.item->size;
340 ANSI_STRING utf8;
341
342 ier = (INODE_EXTREF*)tp.item->data;
343
344 do {
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);
347 break;
348 }
349
350 utf8.Buffer = ier->name;
351 utf8.Length = utf8.MaximumLength = ier->n;
352
353 Status = change_file_type(fcb->Vcb, fcb->inode, fcb->subvol, ier->dir, ier->index, &utf8, BTRFS_TYPE_SYMLINK, rollback);
354
355 if (!NT_SUCCESS(Status)) {
356 ERR("error - change_file_type returned %08x\n", Status);
357 return Status;
358 }
359
360 if (size > sizeof(INODE_EXTREF) - 1 + ier->n) {
361 size -= sizeof(INODE_EXTREF) - 1 + ier->n;
362
363 ier = (INODE_EXTREF*)&ier->name[ier->n];
364 } else
365 break;
366 } while (TRUE);
367 }
368 }
369
370 b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE);
371 if (b) {
372 tp = next_tp;
373
374 b = tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_INODE_EXTREF;
375 }
376 } while (b);
377 }
378
379 fcb->inode_item.st_mode |= __S_IFLNK;
380
381 Status = truncate_file(fcb, 0, rollback);
382 if (!NT_SUCCESS(Status)) {
383 ERR("truncate_file returned %08x\n", Status);
384 return Status;
385 }
386
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);
390 return Status;
391 }
392
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;
398 }
399
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);
404 return Status;
405 }
406
407 for (i = 0; i < target.Length; i++) {
408 if (target.Buffer[i] == '\\')
409 target.Buffer[i] = '/';
410 }
411
412 offset.QuadPart = 0;
413 Status = write_file2(fcb->Vcb, Irp, offset, target.Buffer, (ULONG*)&target.Length, Irp->Flags & IRP_PAGING_IO, Irp->Flags & IRP_NOCACHE, rollback);
414
415 ExFreePool(target.Buffer);
416
417 return Status;
418 }
419
420 static NTSTATUS delete_symlink(fcb* fcb, LIST_ENTRY* rollback) {
421 NTSTATUS Status;
422 KEY searchkey;
423 traverse_ptr tp, next_tp;
424 BOOL b;
425 LARGE_INTEGER time;
426 BTRFS_TIME now;
427
428 searchkey.obj_id = fcb->inode;
429 searchkey.obj_type = TYPE_INODE_REF;
430 searchkey.offset = 0;
431
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);
435 return Status;
436 }
437
438 do {
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));
442 } else {
443 INODE_REF* ir;
444 ULONG size = tp.item->size;
445 ANSI_STRING utf8;
446
447 ir = (INODE_REF*)tp.item->data;
448
449 do {
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);
452 break;
453 }
454
455 utf8.Buffer = ir->name;
456 utf8.Length = utf8.MaximumLength = ir->n;
457
458 Status = change_file_type(fcb->Vcb, fcb->inode, fcb->subvol, tp.item->key.offset, ir->index, &utf8, BTRFS_TYPE_FILE, rollback);
459
460 if (!NT_SUCCESS(Status)) {
461 ERR("error - change_file_type returned %08x\n", Status);
462 return Status;
463 }
464
465 if (size > sizeof(INODE_REF) - 1 + ir->n) {
466 size -= sizeof(INODE_REF) - 1 + ir->n;
467
468 ir = (INODE_REF*)&ir->name[ir->n];
469 } else
470 break;
471 } while (TRUE);
472 }
473 }
474
475 b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE);
476 if (b) {
477 tp = next_tp;
478
479 b = tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_INODE_REF;
480 }
481 } while (b);
482
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;
487
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);
491 return Status;
492 }
493
494 do {
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));
498 } else {
499 INODE_EXTREF* ier;
500 ULONG size = tp.item->size;
501 ANSI_STRING utf8;
502
503 ier = (INODE_EXTREF*)tp.item->data;
504
505 do {
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);
508 break;
509 }
510
511 utf8.Buffer = ier->name;
512 utf8.Length = utf8.MaximumLength = ier->n;
513
514 Status = change_file_type(fcb->Vcb, fcb->inode, fcb->subvol, ier->dir, ier->index, &utf8, BTRFS_TYPE_FILE, rollback);
515
516 if (!NT_SUCCESS(Status)) {
517 ERR("error - change_file_type returned %08x\n", Status);
518 return Status;
519 }
520
521 if (size > sizeof(INODE_EXTREF) - 1 + ier->n) {
522 size -= sizeof(INODE_EXTREF) - 1 + ier->n;
523
524 ier = (INODE_EXTREF*)&ier->name[ier->n];
525 } else
526 break;
527 } while (TRUE);
528 }
529 }
530
531 b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE);
532 if (b) {
533 tp = next_tp;
534
535 b = tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_INODE_EXTREF;
536 }
537 } while (b);
538 }
539
540 Status = truncate_file(fcb, 0, rollback);
541 if (!NT_SUCCESS(Status)) {
542 ERR("truncate_file returned %08x\n", Status);
543 return Status;
544 }
545
546 KeQuerySystemTime(&time);
547
548 win_time_to_unix(time, &now);
549
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;
557
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);
561 return Status;
562 }
563
564 fcb->subvol->root_item.ctransid = fcb->Vcb->superblock.generation;
565 fcb->subvol->root_item.ctime = now;
566
567 return Status;
568 }
569
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;
577 fcb* fcb;
578 ULONG tag;
579 LIST_ENTRY rollback;
580
581 // FIXME - send notification if this succeeds? The attributes will have changed.
582 // FIXME - check permissions
583
584 TRACE("(%p, %p)\n", DeviceObject, Irp);
585
586 InitializeListHead(&rollback);
587
588 if (!FileObject) {
589 ERR("FileObject was NULL\n");
590 return STATUS_INVALID_PARAMETER;
591 }
592
593 fcb = FileObject->FsContext;
594
595 TRACE("%S\n", file_desc(FileObject));
596
597 acquire_tree_lock(fcb->Vcb, TRUE);
598
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;
602 goto end;
603 }
604
605 // FIXME - fail if we already have the attribute FILE_ATTRIBUTE_REPARSE_POINT
606
607 // FIXME - die if not file or directory
608 // FIXME - die if ADS
609
610 if (buflen < sizeof(ULONG)) {
611 WARN("buffer was not long enough to hold tag\n");
612 Status = STATUS_INVALID_PARAMETER;
613 goto end;
614 }
615
616 RtlCopyMemory(&tag, buffer, sizeof(ULONG));
617
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);
620 } else {
621 LARGE_INTEGER offset;
622 char val[64];
623
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);
628 goto end;
629 }
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);
634 goto end;
635 }
636
637 offset.QuadPart = 0;
638
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);
642 goto end;
643 }
644 }
645
646 fcb->atts |= FILE_ATTRIBUTE_REPARSE_POINT;
647
648 sprintf(val, "0x%lx", fcb->atts);
649
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);
653 goto end;
654 }
655 }
656
657 if (NT_SUCCESS(Status))
658 Status = consider_write(fcb->Vcb);
659
660 end:
661 if (NT_SUCCESS(Status))
662 clear_rollback(&rollback);
663 else
664 do_rollback(fcb->Vcb, &rollback);
665
666 release_tree_lock(fcb->Vcb, TRUE);
667
668 return Status;
669 }
670
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;
676 NTSTATUS Status;
677 fcb* fcb;
678 ccb* ccb;
679 file_ref* fileref;
680 LIST_ENTRY rollback;
681
682 // FIXME - send notification if this succeeds? The attributes will have changed.
683 // FIXME - check permissions
684
685 TRACE("(%p, %p)\n", DeviceObject, Irp);
686
687 InitializeListHead(&rollback);
688
689 if (!FileObject) {
690 ERR("FileObject was NULL\n");
691 return STATUS_INVALID_PARAMETER;
692 }
693
694 fcb = FileObject->FsContext;
695 ccb = FileObject->FsContext2;
696 fileref = ccb ? ccb->fileref : NULL;
697
698 TRACE("%S\n", file_desc(FileObject));
699
700 if (!fileref) {
701 ERR("fileref was NULL\n");
702 return STATUS_INVALID_PARAMETER;
703 }
704
705 if (buflen < offsetof(REPARSE_DATA_BUFFER, GenericReparseBuffer.DataBuffer)) {
706 ERR("buffer was too short\n");
707 return STATUS_INVALID_PARAMETER;
708 }
709
710 if (rdb->ReparseDataLength > 0) {
711 WARN("rdb->ReparseDataLength was not zero\n");
712 return STATUS_INVALID_PARAMETER;
713 }
714
715 acquire_tree_lock(fcb->Vcb, TRUE);
716
717 if (fcb->ads) {
718 WARN("tried to delete reparse point on ADS\n");
719 Status = STATUS_INVALID_PARAMETER;
720 goto end;
721 }
722
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;
727 goto end;
728 }
729
730 Status = delete_symlink(fcb, &rollback);
731 if (!NT_SUCCESS(Status)) {
732 ERR("delete_symlink returned %08x\n", Status);
733 goto end;
734 }
735 } else if (fcb->type == BTRFS_TYPE_FILE) {
736 LARGE_INTEGER time;
737 BTRFS_TIME now;
738 ULONG defda;
739
740 // FIXME - do we need to check that the reparse tags match?
741
742 Status = truncate_file(fcb, 0, &rollback);
743 if (!NT_SUCCESS(Status)) {
744 ERR("truncate_file returned %08x\n", Status);
745 goto end;
746 }
747
748 fcb->atts &= ~FILE_ATTRIBUTE_REPARSE_POINT;
749
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);
751
752 if (defda != fcb->atts) {
753 char val[64];
754
755 sprintf(val, "0x%lx", fcb->atts);
756
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);
760 goto end;
761 }
762 } else
763 delete_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, &rollback);
764
765 KeQuerySystemTime(&time);
766
767 win_time_to_unix(time, &now);
768
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;
773
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);
777 goto end;
778 }
779
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) {
783 LARGE_INTEGER time;
784 BTRFS_TIME now;
785 ULONG defda;
786
787 // FIXME - do we need to check that the reparse tags match?
788
789 fcb->atts &= ~FILE_ATTRIBUTE_REPARSE_POINT;
790
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;
793
794 if (defda != fcb->atts) {
795 char val[64];
796
797 sprintf(val, "0x%lx", fcb->atts);
798
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);
802 goto end;
803 }
804 } else
805 delete_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, &rollback);
806
807 delete_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_REPARSE, EA_REPARSE_HASH, &rollback);
808
809 KeQuerySystemTime(&time);
810
811 win_time_to_unix(time, &now);
812
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;
817
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);
821 goto end;
822 }
823
824 fcb->subvol->root_item.ctransid = fcb->Vcb->superblock.generation;
825 fcb->subvol->root_item.ctime = now;
826 } else {
827 ERR("unsupported file type %u\n", fcb->type);
828 Status = STATUS_INVALID_PARAMETER;
829 goto end;
830 }
831
832 Status = STATUS_SUCCESS;
833
834 if (NT_SUCCESS(Status))
835 Status = consider_write(fcb->Vcb);
836
837 end:
838 if (NT_SUCCESS(Status))
839 clear_rollback(&rollback);
840 else
841 do_rollback(fcb->Vcb, &rollback);
842
843 release_tree_lock(fcb->Vcb, TRUE);
844
845 return Status;
846 }