1 /* Copyright (c) Mark Harmstone 2016-17
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 extern tFsRtlValidateReparsePointBuffer fFsRtlValidateReparsePointBuffer;
21
22 typedef struct {
23 uint32_t unknown;
24 char name[1];
25 } REPARSE_DATA_BUFFER_LX_SYMLINK;
26
get_reparse_point(PFILE_OBJECT FileObject,void * buffer,DWORD buflen,ULONG_PTR * retlen)27 NTSTATUS get_reparse_point(PFILE_OBJECT FileObject, void* buffer, DWORD buflen, ULONG_PTR* retlen) {
28 USHORT subnamelen, printnamelen, i;
29 ULONG stringlen;
30 DWORD reqlen;
31 REPARSE_DATA_BUFFER* rdb = buffer;
32 fcb* fcb = FileObject->FsContext;
33 ccb* ccb = FileObject->FsContext2;
34 NTSTATUS Status;
35
36 TRACE("(%p, %p, %lx, %p)\n", FileObject, buffer, buflen, retlen);
37
38 if (!ccb)
39 return STATUS_INVALID_PARAMETER;
40
41 ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, true);
42 ExAcquireResourceSharedLite(fcb->Header.Resource, true);
43
44 if (fcb->type == BTRFS_TYPE_SYMLINK) {
45 if (ccb->lxss) {
46 reqlen = offsetof(REPARSE_DATA_BUFFER, GenericReparseBuffer.DataBuffer) + sizeof(uint32_t);
47
48 if (buflen < reqlen) {
49 Status = STATUS_BUFFER_OVERFLOW;
50 goto end;
51 }
52
53 rdb->ReparseTag = IO_REPARSE_TAG_LX_SYMLINK;
54 rdb->ReparseDataLength = offsetof(REPARSE_DATA_BUFFER, GenericReparseBuffer.DataBuffer) + sizeof(uint32_t);
55 rdb->Reserved = 0;
56
57 *((uint32_t*)rdb->GenericReparseBuffer.DataBuffer) = 1;
58
59 *retlen = reqlen;
60 } else {
61 char* data;
62
63 if (fcb->inode_item.st_size == 0 || fcb->inode_item.st_size > 0xffff) {
64 Status = STATUS_INVALID_PARAMETER;
65 goto end;
66 }
67
68 data = ExAllocatePoolWithTag(PagedPool, (ULONG)fcb->inode_item.st_size, ALLOC_TAG);
69 if (!data) {
70 ERR("out of memory\n");
71 Status = STATUS_INSUFFICIENT_RESOURCES;
72 goto end;
73 }
74
75 TRACE("data = %p, size = %I64x\n", data, fcb->inode_item.st_size);
76 Status = read_file(fcb, (uint8_t*)data, 0, fcb->inode_item.st_size, NULL, NULL);
77
78 if (!NT_SUCCESS(Status)) {
79 ERR("read_file returned %08lx\n", Status);
80 ExFreePool(data);
81 goto end;
82 }
83
84 Status = utf8_to_utf16(NULL, 0, &stringlen, data, (ULONG)fcb->inode_item.st_size);
85 if (!NT_SUCCESS(Status)) {
86 ERR("utf8_to_utf16 1 returned %08lx\n", Status);
87 ExFreePool(data);
88 goto end;
89 }
90
91 subnamelen = (uint16_t)stringlen;
92 printnamelen = (uint16_t)stringlen;
93
94 reqlen = offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) + subnamelen + printnamelen;
95
96 if (buflen >= offsetof(REPARSE_DATA_BUFFER, ReparseDataLength))
97 rdb->ReparseTag = IO_REPARSE_TAG_SYMLINK;
98
99 if (buflen >= offsetof(REPARSE_DATA_BUFFER, Reserved))
100 rdb->ReparseDataLength = (USHORT)(reqlen - offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer));
101
102 if (buflen >= offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.SubstituteNameOffset))
103 rdb->Reserved = 0;
104
105 if (buflen < reqlen) {
106 ExFreePool(data);
107 Status = STATUS_BUFFER_OVERFLOW;
108 *retlen = min(buflen, offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.SubstituteNameOffset));
109 goto end;
110 }
111
112 rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset = 0;
113 rdb->SymbolicLinkReparseBuffer.SubstituteNameLength = subnamelen;
114 rdb->SymbolicLinkReparseBuffer.PrintNameOffset = subnamelen;
115 rdb->SymbolicLinkReparseBuffer.PrintNameLength = printnamelen;
116 rdb->SymbolicLinkReparseBuffer.Flags = SYMLINK_FLAG_RELATIVE;
117
118 Status = utf8_to_utf16(&rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)],
119 stringlen, &stringlen, data, (ULONG)fcb->inode_item.st_size);
120
121 if (!NT_SUCCESS(Status)) {
122 ERR("utf8_to_utf16 2 returned %08lx\n", Status);
123 ExFreePool(data);
124 goto end;
125 }
126
127 for (i = 0; i < stringlen / sizeof(WCHAR); i++) {
128 if (rdb->SymbolicLinkReparseBuffer.PathBuffer[(rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)) + i] == '/')
129 rdb->SymbolicLinkReparseBuffer.PathBuffer[(rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)) + i] = '\\';
130 }
131
132 RtlCopyMemory(&rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(WCHAR)],
133 &rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)],
134 rdb->SymbolicLinkReparseBuffer.SubstituteNameLength);
135
136 *retlen = reqlen;
137
138 ExFreePool(data);
139 }
140
141 Status = STATUS_SUCCESS;
142 } else if (fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT) {
143 if (fcb->type == BTRFS_TYPE_FILE) {
144 ULONG len;
145
146 Status = read_file(fcb, buffer, 0, buflen, &len, NULL);
147
148 if (!NT_SUCCESS(Status)) {
149 ERR("read_file returned %08lx\n", Status);
150 }
151
152 *retlen = len;
153 } else if (fcb->type == BTRFS_TYPE_DIRECTORY) {
154 if (!fcb->reparse_xattr.Buffer || fcb->reparse_xattr.Length < sizeof(ULONG)) {
155 Status = STATUS_NOT_A_REPARSE_POINT;
156 goto end;
157 }
158
159 if (buflen > 0) {
160 *retlen = min(buflen, fcb->reparse_xattr.Length);
161 RtlCopyMemory(buffer, fcb->reparse_xattr.Buffer, *retlen);
162 } else
163 *retlen = 0;
164
165 Status = *retlen == fcb->reparse_xattr.Length ? STATUS_SUCCESS : STATUS_BUFFER_OVERFLOW;
166 } else
167 Status = STATUS_NOT_A_REPARSE_POINT;
168 } else {
169 Status = STATUS_NOT_A_REPARSE_POINT;
170 }
171
172 end:
173 ExReleaseResourceLite(fcb->Header.Resource);
174 ExReleaseResourceLite(&fcb->Vcb->tree_lock);
175
176 return Status;
177 }
178
set_symlink(PIRP Irp,file_ref * fileref,fcb * fcb,ccb * ccb,REPARSE_DATA_BUFFER * rdb,ULONG buflen,LIST_ENTRY * rollback)179 static NTSTATUS set_symlink(PIRP Irp, file_ref* fileref, fcb* fcb, ccb* ccb, REPARSE_DATA_BUFFER* rdb, ULONG buflen, LIST_ENTRY* rollback) {
180 NTSTATUS Status;
181 ULONG tlength;
182 ANSI_STRING target;
183 bool target_alloc = false;
184 LARGE_INTEGER offset, time;
185 BTRFS_TIME now;
186
187 if (rdb->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
188 UNICODE_STRING subname;
189 ULONG minlen, len;
190
191 minlen = offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) + sizeof(WCHAR);
192 if (buflen < minlen) {
193 WARN("buffer was less than minimum length (%lu < %lu)\n", buflen, minlen);
194 return STATUS_INVALID_PARAMETER;
195 }
196
197 if (rdb->SymbolicLinkReparseBuffer.SubstituteNameLength < sizeof(WCHAR)) {
198 WARN("rdb->SymbolicLinkReparseBuffer.SubstituteNameLength was too short\n");
199 return STATUS_INVALID_PARAMETER;
200 }
201
202 subname.Buffer = &rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)];
203 subname.MaximumLength = subname.Length = rdb->SymbolicLinkReparseBuffer.SubstituteNameLength;
204
205 TRACE("substitute name = %.*S\n", (int)(subname.Length / sizeof(WCHAR)), subname.Buffer);
206
207 Status = utf16_to_utf8(NULL, 0, &len, subname.Buffer, subname.Length);
208 if (!NT_SUCCESS(Status)) {
209 ERR("utf16_to_utf8 1 failed with error %08lx\n", Status);
210 return Status;
211 }
212
213 target.MaximumLength = target.Length = (USHORT)len;
214 target.Buffer = ExAllocatePoolWithTag(PagedPool, target.MaximumLength, ALLOC_TAG);
215 if (!target.Buffer) {
216 ERR("out of memory\n");
217 return STATUS_INSUFFICIENT_RESOURCES;
218 }
219
220 target_alloc = true;
221
222 Status = utf16_to_utf8(target.Buffer, target.Length, &len, subname.Buffer, subname.Length);
223 if (!NT_SUCCESS(Status)) {
224 ERR("utf16_to_utf8 2 failed with error %08lx\n", Status);
225 ExFreePool(target.Buffer);
226 return Status;
227 }
228
229 for (USHORT i = 0; i < target.Length; i++) {
230 if (target.Buffer[i] == '\\')
231 target.Buffer[i] = '/';
232 }
233 } else if (rdb->ReparseTag == IO_REPARSE_TAG_LX_SYMLINK) {
234 REPARSE_DATA_BUFFER_LX_SYMLINK* buf;
235
236 if (buflen < offsetof(REPARSE_DATA_BUFFER, GenericReparseBuffer.DataBuffer) + rdb->ReparseDataLength) {
237 WARN("buffer was less than expected length (%lu < %lu)\n", buflen,
238 (unsigned long)(offsetof(REPARSE_DATA_BUFFER, GenericReparseBuffer.DataBuffer) + rdb->ReparseDataLength));
239 return STATUS_INVALID_PARAMETER;
240 }
241
242 buf = (REPARSE_DATA_BUFFER_LX_SYMLINK*)rdb->GenericReparseBuffer.DataBuffer;
243
244 if (buflen < offsetof(REPARSE_DATA_BUFFER_LX_SYMLINK, name)) {
245 WARN("buffer was less than minimum length (%u < %lu)\n", rdb->ReparseDataLength,
246 (unsigned long)(offsetof(REPARSE_DATA_BUFFER_LX_SYMLINK, name)));
247 return STATUS_INVALID_PARAMETER;
248 }
249
250 target.Buffer = buf->name;
251 target.Length = target.MaximumLength = rdb->ReparseDataLength - offsetof(REPARSE_DATA_BUFFER_LX_SYMLINK, name);
252 } else {
253 ERR("unexpected reparse tag %08lx\n", rdb->ReparseTag);
254 return STATUS_INTERNAL_ERROR;
255 }
256
257 fcb->type = BTRFS_TYPE_SYMLINK;
258 fcb->inode_item.st_mode &= ~__S_IFMT;
259 fcb->inode_item.st_mode |= __S_IFLNK;
260 fcb->inode_item.generation = fcb->Vcb->superblock.generation; // so we don't confuse btrfs send on Linux
261
262 if (fileref && fileref->dc)
263 fileref->dc->type = fcb->type;
264
265 Status = truncate_file(fcb, 0, Irp, rollback);
266 if (!NT_SUCCESS(Status)) {
267 ERR("truncate_file returned %08lx\n", Status);
268
269 if (target_alloc)
270 ExFreePool(target.Buffer);
271
272 return Status;
273 }
274
275 offset.QuadPart = 0;
276 tlength = target.Length;
277 Status = write_file2(fcb->Vcb, Irp, offset, target.Buffer, &tlength, false, true,
278 true, false, false, rollback);
279
280 if (target_alloc)
281 ExFreePool(target.Buffer);
282
283 KeQuerySystemTime(&time);
284 win_time_to_unix(time, &now);
285
286 fcb->inode_item.transid = fcb->Vcb->superblock.generation;
287 fcb->inode_item.sequence++;
288
289 if (!ccb || !ccb->user_set_change_time)
290 fcb->inode_item.st_ctime = now;
291
292 if (!ccb || !ccb->user_set_write_time)
293 fcb->inode_item.st_mtime = now;
294
295 fcb->subvol->root_item.ctransid = fcb->Vcb->superblock.generation;
296 fcb->subvol->root_item.ctime = now;
297
298 fcb->inode_item_changed = true;
299 mark_fcb_dirty(fcb);
300
301 if (fileref)
302 mark_fileref_dirty(fileref);
303
304 return Status;
305 }
306
set_reparse_point2(fcb * fcb,REPARSE_DATA_BUFFER * rdb,ULONG buflen,ccb * ccb,file_ref * fileref,PIRP Irp,LIST_ENTRY * rollback)307 NTSTATUS set_reparse_point2(fcb* fcb, REPARSE_DATA_BUFFER* rdb, ULONG buflen, ccb* ccb, file_ref* fileref, PIRP Irp, LIST_ENTRY* rollback) {
308 NTSTATUS Status;
309 ULONG tag;
310
311 if (fcb->type == BTRFS_TYPE_SYMLINK) {
312 WARN("tried to set a reparse point on an existing symlink\n");
313 return STATUS_INVALID_PARAMETER;
314 }
315
316 // FIXME - fail if we already have the attribute FILE_ATTRIBUTE_REPARSE_POINT
317
318 // FIXME - die if not file or directory
319
320 if (fcb->type == BTRFS_TYPE_DIRECTORY && fcb->inode_item.st_size > 0) {
321 TRACE("directory not empty\n");
322 return STATUS_DIRECTORY_NOT_EMPTY;
323 }
324
325 if (buflen < sizeof(ULONG)) {
326 WARN("buffer was not long enough to hold tag\n");
327 return STATUS_INVALID_BUFFER_SIZE;
328 }
329
330 Status = fFsRtlValidateReparsePointBuffer(buflen, rdb);
331 if (!NT_SUCCESS(Status)) {
332 ERR("FsRtlValidateReparsePointBuffer returned %08lx\n", Status);
333 return Status;
334 }
335
336 tag = *(ULONG*)rdb;
337
338 if (tag == IO_REPARSE_TAG_MOUNT_POINT && fcb->type != BTRFS_TYPE_DIRECTORY)
339 return STATUS_NOT_A_DIRECTORY;
340
341 if (fcb->type == BTRFS_TYPE_FILE &&
342 ((tag == IO_REPARSE_TAG_SYMLINK && rdb->SymbolicLinkReparseBuffer.Flags & SYMLINK_FLAG_RELATIVE) || tag == IO_REPARSE_TAG_LX_SYMLINK)) {
343 Status = set_symlink(Irp, fileref, fcb, ccb, rdb, buflen, rollback);
344 fcb->atts |= FILE_ATTRIBUTE_REPARSE_POINT;
345 } else {
346 LARGE_INTEGER offset, time;
347 BTRFS_TIME now;
348
349 if (fcb->type == BTRFS_TYPE_DIRECTORY || fcb->type == BTRFS_TYPE_CHARDEV || fcb->type == BTRFS_TYPE_BLOCKDEV) { // store as xattr
350 ANSI_STRING buf;
351
352 buf.Buffer = ExAllocatePoolWithTag(PagedPool, buflen, ALLOC_TAG);
353 if (!buf.Buffer) {
354 ERR("out of memory\n");
355 return STATUS_INSUFFICIENT_RESOURCES;
356 }
357 buf.Length = buf.MaximumLength = (uint16_t)buflen;
358
359 if (fcb->reparse_xattr.Buffer)
360 ExFreePool(fcb->reparse_xattr.Buffer);
361
362 fcb->reparse_xattr = buf;
363 RtlCopyMemory(buf.Buffer, rdb, buflen);
364
365 fcb->reparse_xattr_changed = true;
366
367 Status = STATUS_SUCCESS;
368 } else { // otherwise, store as file data
369 Status = truncate_file(fcb, 0, Irp, rollback);
370 if (!NT_SUCCESS(Status)) {
371 ERR("truncate_file returned %08lx\n", Status);
372 return Status;
373 }
374
375 offset.QuadPart = 0;
376
377 Status = write_file2(fcb->Vcb, Irp, offset, rdb, &buflen, false, true, true, false, false, rollback);
378 if (!NT_SUCCESS(Status)) {
379 ERR("write_file2 returned %08lx\n", Status);
380 return Status;
381 }
382 }
383
384 KeQuerySystemTime(&time);
385 win_time_to_unix(time, &now);
386
387 fcb->inode_item.transid = fcb->Vcb->superblock.generation;
388 fcb->inode_item.sequence++;
389
390 if (!ccb || !ccb->user_set_change_time)
391 fcb->inode_item.st_ctime = now;
392
393 if (!ccb || !ccb->user_set_write_time)
394 fcb->inode_item.st_mtime = now;
395
396 fcb->atts |= FILE_ATTRIBUTE_REPARSE_POINT;
397 fcb->atts_changed = true;
398
399 fcb->subvol->root_item.ctransid = fcb->Vcb->superblock.generation;
400 fcb->subvol->root_item.ctime = now;
401
402 fcb->inode_item_changed = true;
403 mark_fcb_dirty(fcb);
404 }
405
406 return STATUS_SUCCESS;
407 }
408
set_reparse_point(PIRP Irp)409 NTSTATUS set_reparse_point(PIRP Irp) {
410 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
411 PFILE_OBJECT FileObject = IrpSp->FileObject;
412 void* buffer = Irp->AssociatedIrp.SystemBuffer;
413 REPARSE_DATA_BUFFER* rdb = buffer;
414 DWORD buflen = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
415 NTSTATUS Status = STATUS_SUCCESS;
416 fcb* fcb;
417 ccb* ccb;
418 file_ref* fileref;
419 LIST_ENTRY rollback;
420
421 TRACE("(%p)\n", Irp);
422
423 InitializeListHead(&rollback);
424
425 if (!FileObject) {
426 ERR("FileObject was NULL\n");
427 return STATUS_INVALID_PARAMETER;
428 }
429
430 // IFSTest insists on this, for some reason...
431 if (Irp->UserBuffer)
432 return STATUS_INVALID_PARAMETER;
433
434 fcb = FileObject->FsContext;
435 ccb = FileObject->FsContext2;
436
437 if (!ccb) {
438 ERR("ccb was NULL\n");
439 return STATUS_INVALID_PARAMETER;
440 }
441
442 if (Irp->RequestorMode == UserMode && !(ccb->access & (FILE_WRITE_ATTRIBUTES | FILE_WRITE_DATA))) {
443 WARN("insufficient privileges\n");
444 return STATUS_ACCESS_DENIED;
445 }
446
447 fileref = ccb->fileref;
448
449 if (!fileref) {
450 ERR("fileref was NULL\n");
451 return STATUS_INVALID_PARAMETER;
452 }
453
454 if (fcb->ads) {
455 fileref = fileref->parent;
456 fcb = fileref->fcb;
457 }
458
459 ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, true);
460 ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
461
462 Status = set_reparse_point2(fcb, rdb, buflen, ccb, fileref, Irp, &rollback);
463 if (!NT_SUCCESS(Status)) {
464 ERR("set_reparse_point2 returned %08lx\n", Status);
465 goto end;
466 }
467
468 queue_notification_fcb(fileref, FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_ATTRIBUTES, FILE_ACTION_MODIFIED, NULL);
469
470 end:
471 if (NT_SUCCESS(Status))
472 clear_rollback(&rollback);
473 else
474 do_rollback(fcb->Vcb, &rollback);
475
476 ExReleaseResourceLite(fcb->Header.Resource);
477 ExReleaseResourceLite(&fcb->Vcb->tree_lock);
478
479 return Status;
480 }
481
delete_reparse_point(PIRP Irp)482 NTSTATUS delete_reparse_point(PIRP Irp) {
483 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
484 PFILE_OBJECT FileObject = IrpSp->FileObject;
485 REPARSE_DATA_BUFFER* rdb = Irp->AssociatedIrp.SystemBuffer;
486 DWORD buflen = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
487 NTSTATUS Status;
488 fcb* fcb;
489 ccb* ccb;
490 file_ref* fileref;
491 LIST_ENTRY rollback;
492
493 TRACE("(%p)\n", Irp);
494
495 InitializeListHead(&rollback);
496
497 if (!FileObject) {
498 ERR("FileObject was NULL\n");
499 return STATUS_INVALID_PARAMETER;
500 }
501
502 fcb = FileObject->FsContext;
503
504 if (!fcb) {
505 ERR("fcb was NULL\n");
506 return STATUS_INVALID_PARAMETER;
507 }
508
509 ccb = FileObject->FsContext2;
510
511 if (!ccb) {
512 ERR("ccb was NULL\n");
513 return STATUS_INVALID_PARAMETER;
514 }
515
516 if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_ATTRIBUTES)) {
517 WARN("insufficient privileges\n");
518 return STATUS_ACCESS_DENIED;
519 }
520
521 fileref = ccb->fileref;
522
523 if (!fileref) {
524 ERR("fileref was NULL\n");
525 return STATUS_INVALID_PARAMETER;
526 }
527
528 ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, true);
529 ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
530
531 if (buflen < offsetof(REPARSE_DATA_BUFFER, GenericReparseBuffer.DataBuffer)) {
532 ERR("buffer was too short\n");
533 Status = STATUS_INVALID_PARAMETER;
534 goto end;
535 }
536
537 if (rdb->ReparseDataLength > 0) {
538 WARN("rdb->ReparseDataLength was not zero\n");
539 Status = STATUS_INVALID_PARAMETER;
540 goto end;
541 }
542
543 if (fcb->ads) {
544 WARN("tried to delete reparse point on ADS\n");
545 Status = STATUS_INVALID_PARAMETER;
546 goto end;
547 }
548
549 if (fcb->type == BTRFS_TYPE_SYMLINK) {
550 LARGE_INTEGER time;
551 BTRFS_TIME now;
552
553 if (rdb->ReparseTag != IO_REPARSE_TAG_SYMLINK) {
554 WARN("reparse tag was not IO_REPARSE_TAG_SYMLINK\n");
555 Status = STATUS_INVALID_PARAMETER;
556 goto end;
557 }
558
559 KeQuerySystemTime(&time);
560 win_time_to_unix(time, &now);
561
562 fileref->fcb->type = BTRFS_TYPE_FILE;
563 fileref->fcb->inode_item.st_mode &= ~__S_IFLNK;
564 fileref->fcb->inode_item.st_mode |= __S_IFREG;
565 fileref->fcb->inode_item.generation = fileref->fcb->Vcb->superblock.generation; // so we don't confuse btrfs send on Linux
566 fileref->fcb->inode_item.transid = fileref->fcb->Vcb->superblock.generation;
567 fileref->fcb->inode_item.sequence++;
568
569 if (!ccb->user_set_change_time)
570 fileref->fcb->inode_item.st_ctime = now;
571
572 if (!ccb->user_set_write_time)
573 fileref->fcb->inode_item.st_mtime = now;
574
575 fileref->fcb->atts &= ~FILE_ATTRIBUTE_REPARSE_POINT;
576
577 if (fileref->dc)
578 fileref->dc->type = fileref->fcb->type;
579
580 mark_fileref_dirty(fileref);
581
582 fileref->fcb->inode_item_changed = true;
583 mark_fcb_dirty(fileref->fcb);
584
585 fileref->fcb->subvol->root_item.ctransid = fcb->Vcb->superblock.generation;
586 fileref->fcb->subvol->root_item.ctime = now;
587 } else if (fcb->type == BTRFS_TYPE_FILE) {
588 LARGE_INTEGER time;
589 BTRFS_TIME now;
590
591 // FIXME - do we need to check that the reparse tags match?
592
593 Status = truncate_file(fcb, 0, Irp, &rollback);
594 if (!NT_SUCCESS(Status)) {
595 ERR("truncate_file returned %08lx\n", Status);
596 goto end;
597 }
598
599 fcb->atts &= ~FILE_ATTRIBUTE_REPARSE_POINT;
600 fcb->atts_changed = true;
601
602 KeQuerySystemTime(&time);
603 win_time_to_unix(time, &now);
604
605 fcb->inode_item.transid = fcb->Vcb->superblock.generation;
606 fcb->inode_item.sequence++;
607
608 if (!ccb->user_set_change_time)
609 fcb->inode_item.st_ctime = now;
610
611 if (!ccb->user_set_write_time)
612 fcb->inode_item.st_mtime = now;
613
614 fcb->inode_item_changed = true;
615 mark_fcb_dirty(fcb);
616
617 fcb->subvol->root_item.ctransid = fcb->Vcb->superblock.generation;
618 fcb->subvol->root_item.ctime = now;
619 } else if (fcb->type == BTRFS_TYPE_DIRECTORY) {
620 LARGE_INTEGER time;
621 BTRFS_TIME now;
622
623 // FIXME - do we need to check that the reparse tags match?
624
625 fcb->atts &= ~FILE_ATTRIBUTE_REPARSE_POINT;
626 fcb->atts_changed = true;
627
628 if (fcb->reparse_xattr.Buffer) {
629 ExFreePool(fcb->reparse_xattr.Buffer);
630 fcb->reparse_xattr.Buffer = NULL;
631 }
632
633 fcb->reparse_xattr_changed = true;
634
635 KeQuerySystemTime(&time);
636 win_time_to_unix(time, &now);
637
638 fcb->inode_item.transid = fcb->Vcb->superblock.generation;
639 fcb->inode_item.sequence++;
640
641 if (!ccb->user_set_change_time)
642 fcb->inode_item.st_ctime = now;
643
644 if (!ccb->user_set_write_time)
645 fcb->inode_item.st_mtime = now;
646
647 fcb->inode_item_changed = true;
648 mark_fcb_dirty(fcb);
649
650 fcb->subvol->root_item.ctransid = fcb->Vcb->superblock.generation;
651 fcb->subvol->root_item.ctime = now;
652 } else {
653 ERR("unsupported file type %u\n", fcb->type);
654 Status = STATUS_INVALID_PARAMETER;
655 goto end;
656 }
657
658 Status = STATUS_SUCCESS;
659
660 queue_notification_fcb(fileref, FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_ATTRIBUTES, FILE_ACTION_MODIFIED, NULL);
661
662 end:
663 if (NT_SUCCESS(Status))
664 clear_rollback(&rollback);
665 else
666 do_rollback(fcb->Vcb, &rollback);
667
668 ExReleaseResourceLite(fcb->Header.Resource);
669 ExReleaseResourceLite(&fcb->Vcb->tree_lock);
670
671 return Status;
672 }
673