f364dd3fb79a49b6ed8433da9d9fe52577f7ced5
[reactos.git] / reactos / drivers / filesystems / udfs / fileinfo.cpp
1 ////////////////////////////////////////////////////////////////////
2 // Copyright (C) Alexander Telyatnikov, Ivan Keliukh, Yegor Anchishkin, SKIF Software, 1999-2013. Kiev, Ukraine
3 // All rights reserved
4 // This file was released under the GPLv2 on June 2015.
5 ////////////////////////////////////////////////////////////////////
6 /*************************************************************************
7 *
8 * File: Fileinfo.cpp
9 *
10 * Module: UDF File System Driver (Kernel mode execution only)
11 *
12 * Description:
13 * Contains code to handle the "set/query file information" dispatch
14 * entry points.
15 *
16 *************************************************************************/
17
18 #include "udffs.h"
19
20 // define the file specific bug-check id
21 #define UDF_BUG_CHECK_ID UDF_FILE_INFORMATION
22
23 #define MEM_USREN_TAG "US_Ren"
24 #define MEM_USREN2_TAG "US_Ren2"
25 #define MEM_USFIDC_TAG "US_FIDC"
26 #define MEM_USHL_TAG "US_HL"
27
28 /*************************************************************************
29 *
30 * Function: UDFFileInfo()
31 *
32 * Description:
33 * The I/O Manager will invoke this routine to handle a set/query file
34 * information request
35 *
36 * Expected Interrupt Level (for execution) :
37 *
38 * IRQL_PASSIVE_LEVEL (invocation at higher IRQL will cause execution
39 * to be deferred to a worker thread context)
40 *
41 * Return Value: STATUS_SUCCESS/Error
42 *
43 *************************************************************************/
44 NTSTATUS
45 NTAPI
46 UDFFileInfo(
47 PDEVICE_OBJECT DeviceObject, // the logical volume device object
48 PIRP Irp // I/O Request Packet
49 )
50 {
51 NTSTATUS RC = STATUS_SUCCESS;
52 PtrUDFIrpContext PtrIrpContext = NULL;
53 BOOLEAN AreWeTopLevel = FALSE;
54
55 TmPrint(("UDFFileInfo: \n"));
56
57 FsRtlEnterFileSystem();
58 ASSERT(DeviceObject);
59 ASSERT(Irp);
60
61 // set the top level context
62 AreWeTopLevel = UDFIsIrpTopLevel(Irp);
63 ASSERT(!UDFIsFSDevObj(DeviceObject));
64
65 _SEH2_TRY {
66
67 // get an IRP context structure and issue the request
68 PtrIrpContext = UDFAllocateIrpContext(Irp, DeviceObject);
69 if(PtrIrpContext) {
70 RC = UDFCommonFileInfo(PtrIrpContext, Irp);
71 } else {
72 RC = STATUS_INSUFFICIENT_RESOURCES;
73 Irp->IoStatus.Status = RC;
74 Irp->IoStatus.Information = 0;
75 // complete the IRP
76 IoCompleteRequest(Irp, IO_DISK_INCREMENT);
77 }
78
79 } _SEH2_EXCEPT(UDFExceptionFilter(PtrIrpContext, _SEH2_GetExceptionInformation())) {
80
81 RC = UDFExceptionHandler(PtrIrpContext, Irp);
82
83 UDFLogEvent(UDF_ERROR_INTERNAL_ERROR, RC);
84 } _SEH2_END;
85
86 if(AreWeTopLevel) {
87 IoSetTopLevelIrp(NULL);
88 }
89
90 FsRtlExitFileSystem();
91
92 return(RC);
93 } // end UDFFileInfo()
94
95
96 /*************************************************************************
97 *
98 * Function: UDFCommonFileInfo()
99 *
100 * Description:
101 * The actual work is performed here. This routine may be invoked in one'
102 * of the two possible contexts:
103 * (a) in the context of a system worker thread
104 * (b) in the context of the original caller
105 *
106 * Expected Interrupt Level (for execution) :
107 *
108 * IRQL_PASSIVE_LEVEL
109 *
110 * Return Value: STATUS_SUCCESS/Error
111 *
112 *************************************************************************/
113 NTSTATUS
114 UDFCommonFileInfo(
115 PtrUDFIrpContext PtrIrpContext,
116 PIRP Irp
117 )
118 {
119 NTSTATUS RC = STATUS_SUCCESS;
120 PIO_STACK_LOCATION IrpSp = NULL;
121 PFILE_OBJECT FileObject = NULL;
122 PtrUDFFCB Fcb = NULL;
123 PtrUDFCCB Ccb = NULL;
124 PVCB Vcb = NULL;
125 PtrUDFNTRequiredFCB NtReqFcb = NULL;
126 BOOLEAN MainResourceAcquired = FALSE;
127 BOOLEAN ParentResourceAcquired = FALSE;
128 BOOLEAN PagingIoResourceAcquired = FALSE;
129 PVOID PtrSystemBuffer = NULL;
130 LONG BufferLength = 0;
131 FILE_INFORMATION_CLASS FunctionalityRequested;
132 BOOLEAN CanWait = FALSE;
133 BOOLEAN PostRequest = FALSE;
134 BOOLEAN AcquiredVcb = FALSE;
135 PIRP TopIrp;
136
137 TmPrint(("UDFCommonFileInfo: irp %x\n", Irp));
138
139 TopIrp = IoGetTopLevelIrp();
140 switch((ULONG)TopIrp) {
141 case FSRTL_FSP_TOP_LEVEL_IRP:
142 KdPrint((" FSRTL_FSP_TOP_LEVEL_IRP\n"));
143 break;
144 case FSRTL_CACHE_TOP_LEVEL_IRP:
145 KdPrint((" FSRTL_CACHE_TOP_LEVEL_IRP\n"));
146 break;
147 case FSRTL_MOD_WRITE_TOP_LEVEL_IRP:
148 KdPrint((" FSRTL_MOD_WRITE_TOP_LEVEL_IRP\n"));
149 break;
150 case FSRTL_FAST_IO_TOP_LEVEL_IRP:
151 KdPrint((" FSRTL_FAST_IO_TOP_LEVEL_IRP\n"));
152 BrutePoint()
153 break;
154 case NULL:
155 KdPrint((" NULL TOP_LEVEL_IRP\n"));
156 break;
157 default:
158 if(TopIrp == Irp) {
159 KdPrint((" TOP_LEVEL_IRP\n"));
160 } else {
161 KdPrint((" RECURSIVE_IRP, TOP = %x\n", TopIrp));
162 }
163 }
164
165 _SEH2_TRY {
166 // First, get a pointer to the current I/O stack location.
167 IrpSp = IoGetCurrentIrpStackLocation(Irp);
168 ASSERT(IrpSp);
169
170 FileObject = IrpSp->FileObject;
171 ASSERT(FileObject);
172
173 // Get the FCB and CCB pointers.
174 Ccb = (PtrUDFCCB)(FileObject->FsContext2);
175 ASSERT(Ccb);
176 if(!Ccb) {
177 // some applications sends us FO without Ccb
178 // This is not allowed...
179 RC = STATUS_INVALID_PARAMETER;
180 try_return(RC);
181 }
182 Fcb = Ccb->Fcb;
183 ASSERT(Fcb);
184
185 NtReqFcb = Fcb->NTRequiredFCB;
186
187 CanWait = (PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_CAN_BLOCK) ? TRUE : FALSE;
188
189 // If the caller has opened a logical volume and is attempting to
190 // query information for it as a file stream, return an error.
191 if(Fcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_VCB) {
192 // This is not allowed. Caller must use get/set volume information instead.
193 RC = STATUS_INVALID_PARAMETER;
194 try_return(RC);
195 }
196
197
198 Vcb = (PVCB)(IrpSp->DeviceObject->DeviceExtension);
199 ASSERT(Vcb);
200 ASSERT(Fcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_FCB);
201 //Vcb->VCBFlags |= UDF_VCB_SKIP_EJECT_CHECK;
202
203 // The NT I/O Manager always allocates and supplies a system
204 // buffer for query and set file information calls.
205 // Copying information to/from the user buffer and the system
206 // buffer is performed by the I/O Manager and the FSD need not worry about it.
207 PtrSystemBuffer = Irp->AssociatedIrp.SystemBuffer;
208
209 UDFFlushTryBreak(Vcb);
210 if(!UDFAcquireResourceShared(&(Vcb->VCBResource), CanWait)) {
211 PostRequest = TRUE;
212 try_return(RC = STATUS_PENDING);
213 }
214 AcquiredVcb = TRUE;
215
216 if(IrpSp->MajorFunction == IRP_MJ_QUERY_INFORMATION) {
217 // Now, obtain some parameters.
218 BufferLength = IrpSp->Parameters.QueryFile.Length;
219 FunctionalityRequested = IrpSp->Parameters.QueryFile.FileInformationClass;
220 #ifdef UDF_ENABLE_SECURITY
221 RC = IoCheckFunctionAccess(
222 Ccb->PreviouslyGrantedAccess,
223 PtrIrpContext->MajorFunction,
224 PtrIrpContext->MinorFunction,
225 0,
226 &FunctionalityRequested,
227 NULL);
228 if(!NT_SUCCESS(RC)) {
229 try_return(RC);
230 }
231 #endif //UDF_ENABLE_SECURITY
232 // Acquire the MainResource shared (NOTE: for paging-IO on a
233 // page file, we should avoid acquiring any resources and simply
234 // trust the VMM to do the right thing, else we could possibly
235 // run into deadlocks).
236 if(!(Fcb->FCBFlags & UDF_FCB_PAGE_FILE)) {
237 // Acquire the MainResource shared.
238 UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb);
239 UDFAcquireResourceShared(&(NtReqFcb->MainResource), TRUE);
240 MainResourceAcquired = TRUE;
241 }
242
243 // Do whatever the caller asked us to do
244 switch (FunctionalityRequested) {
245 case FileBasicInformation:
246 RC = UDFGetBasicInformation(FileObject, Fcb, (PFILE_BASIC_INFORMATION)PtrSystemBuffer, &BufferLength);
247 break;
248 case FileStandardInformation:
249 RC = UDFGetStandardInformation(Fcb, (PFILE_STANDARD_INFORMATION) PtrSystemBuffer, &BufferLength);
250 break;
251 #if(_WIN32_WINNT >= 0x0400)
252 case FileNetworkOpenInformation:
253 RC = UDFGetNetworkInformation(Fcb, (PFILE_NETWORK_OPEN_INFORMATION)PtrSystemBuffer, &BufferLength);
254 break;
255 #endif // _WIN32_WINNT >= 0x0400
256 case FileInternalInformation:
257 RC = UDFGetInternalInformation(PtrIrpContext, Fcb, Ccb, (PFILE_INTERNAL_INFORMATION) PtrSystemBuffer, &BufferLength);
258 break;
259 case FileEaInformation:
260 RC = UDFGetEaInformation(PtrIrpContext, Fcb, (PFILE_EA_INFORMATION) PtrSystemBuffer, &BufferLength);
261 break;
262 case FileNameInformation:
263 RC = UDFGetFullNameInformation(FileObject, (PFILE_NAME_INFORMATION) PtrSystemBuffer, &BufferLength);
264 break;
265 case FileAlternateNameInformation:
266 RC = UDFGetAltNameInformation(Fcb, (PFILE_NAME_INFORMATION) PtrSystemBuffer, &BufferLength);
267 break;
268 // case FileCompressionInformation:
269 // // RC = UDFGetCompressionInformation(...);
270 // break;
271 case FilePositionInformation:
272 RC = UDFGetPositionInformation(FileObject, (PFILE_POSITION_INFORMATION)PtrSystemBuffer, &BufferLength);
273 break;
274 case FileStreamInformation:
275 RC = UDFGetFileStreamInformation(Fcb, (PFILE_STREAM_INFORMATION) PtrSystemBuffer, &BufferLength);
276 break;
277 case FileAllInformation:
278 // The I/O Manager supplies the Mode, Access, and Alignment
279 // information. The rest is up to us to provide.
280 // Therefore, decrement the BufferLength appropriately (assuming
281 // that the above 3 types on information are already in the
282 // buffer)
283 {
284 PFILE_ALL_INFORMATION PtrAllInfo = (PFILE_ALL_INFORMATION)PtrSystemBuffer;
285
286 BufferLength -= (sizeof(FILE_MODE_INFORMATION) +
287 sizeof(FILE_ACCESS_INFORMATION) +
288 sizeof(FILE_ALIGNMENT_INFORMATION));
289
290 // Get the remaining stuff.
291 if(!NT_SUCCESS(RC = UDFGetBasicInformation(FileObject, Fcb, &(PtrAllInfo->BasicInformation), &BufferLength)) ||
292 !NT_SUCCESS(RC = UDFGetStandardInformation(Fcb, &(PtrAllInfo->StandardInformation), &BufferLength)) ||
293 !NT_SUCCESS(RC = UDFGetInternalInformation(PtrIrpContext, Fcb, Ccb, &(PtrAllInfo->InternalInformation), &BufferLength)) ||
294 !NT_SUCCESS(RC = UDFGetEaInformation(PtrIrpContext, Fcb, &(PtrAllInfo->EaInformation), &BufferLength)) ||
295 !NT_SUCCESS(RC = UDFGetPositionInformation(FileObject, &(PtrAllInfo->PositionInformation), &BufferLength)) ||
296 !NT_SUCCESS(RC = UDFGetFullNameInformation(FileObject, &(PtrAllInfo->NameInformation), &BufferLength))
297 )
298 try_return(RC);
299 }
300 break;
301 default:
302 RC = STATUS_INVALID_PARAMETER;
303 try_return(RC);
304 }
305
306 #ifndef UDF_READ_ONLY_BUILD
307 } else {
308 // if(IrpSp->MajorFunction == IRP_MJ_SET_INFORMATION) {
309 Vcb->VCBFlags |= UDF_VCB_SKIP_EJECT_CHECK;
310 ASSERT(IrpSp->MajorFunction == IRP_MJ_SET_INFORMATION);
311 // Now, obtain some parameters.
312 FunctionalityRequested = IrpSp->Parameters.SetFile.FileInformationClass;
313 if((Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_READ_ONLY) &&
314 (FunctionalityRequested != FilePositionInformation)) {
315 try_return(RC = STATUS_ACCESS_DENIED);
316 }
317 #ifdef UDF_ENABLE_SECURITY
318 RC = IoCheckFunctionAccess(
319 Ccb->PreviouslyGrantedAccess,
320 PtrIrpContext->MajorFunction,
321 PtrIrpContext->MinorFunction,
322 0,
323 &FunctionalityRequested,
324 NULL);
325 if(!NT_SUCCESS(RC)) {
326 try_return(RC);
327 }
328 #endif //UDF_ENABLE_SECURITY
329 // If the FSD supports opportunistic locking,
330 // then we should check whether the oplock state
331 // allows the caller to proceed.
332
333 // Rename, and link operations require creation of a directory
334 // entry and possibly deletion of another directory entry.
335
336 // Unless this is an operation on a page file, we should go ahead and
337 // acquire the FCB exclusively at this time. Note that we will pretty
338 // much block out anything being done to the FCB from this point on.
339 if(!(Fcb->FCBFlags & UDF_FCB_PAGE_FILE) &&
340 (FunctionalityRequested != FilePositionInformation) &&
341 (FunctionalityRequested != FileRenameInformation) &&
342 (FunctionalityRequested != FileLinkInformation)) {
343 // Acquire the Parent & Main Resources exclusive.
344 if(Fcb->FileInfo->ParentFile) {
345 UDF_CHECK_PAGING_IO_RESOURCE(Fcb->ParentFcb->NTRequiredFCB);
346 if(!UDFAcquireResourceExclusive(&(Fcb->ParentFcb->NTRequiredFCB->MainResource), CanWait)) {
347 PostRequest = TRUE;
348 try_return(RC = STATUS_PENDING);
349 }
350 ParentResourceAcquired = TRUE;
351 }
352 UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb);
353 if(!UDFAcquireResourceExclusive(&(NtReqFcb->MainResource), CanWait)) {
354 PostRequest = TRUE;
355 try_return(RC = STATUS_PENDING);
356 }
357 MainResourceAcquired = TRUE;
358 } else
359 // The only operations that could conceivably proceed from this point
360 // on are paging-IO read/write operations. For delete, link (rename),
361 // set allocation size, and set EOF, should also acquire the paging-IO
362 // resource, thereby synchronizing with paging-IO requests.
363 if((Fcb->FCBFlags & UDF_FCB_PAGE_FILE) &&
364 ((FunctionalityRequested == FileDispositionInformation) ||
365 (FunctionalityRequested == FileAllocationInformation) ||
366 (FunctionalityRequested == FileEndOfFileInformation)) ) {
367
368 // Acquire the MainResource shared.
369 UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb);
370 if(!UDFAcquireResourceShared(&(NtReqFcb->MainResource), CanWait)) {
371 PostRequest = TRUE;
372 try_return(RC = STATUS_PENDING);
373 }
374 MainResourceAcquired = TRUE;
375 // Acquire the PagingResource exclusive.
376 if(!UDFAcquireResourceExclusive(&(NtReqFcb->PagingIoResource), CanWait)) {
377 PostRequest = TRUE;
378 try_return(RC = STATUS_PENDING);
379 }
380 PagingIoResourceAcquired = TRUE;
381 } else if((FunctionalityRequested != FileRenameInformation) &&
382 (FunctionalityRequested != FileLinkInformation)) {
383 // Acquire the MainResource shared.
384 UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb);
385 if(!UDFAcquireResourceShared(&(NtReqFcb->MainResource), CanWait)) {
386 PostRequest = TRUE;
387 try_return(RC = STATUS_PENDING);
388 }
389 MainResourceAcquired = TRUE;
390 }
391
392 if((Vcb->VCBFlags & UDF_VCB_FLAGS_RAW_DISK) &&
393 (FunctionalityRequested != FilePositionInformation)) {
394 AdPrint((" Can't change File Information on blank volume ;)\n"));
395 try_return(RC = STATUS_ACCESS_DENIED);
396 }
397
398 // Do whatever the caller asked us to do
399 switch (FunctionalityRequested) {
400 case FileBasicInformation:
401 RC = UDFSetBasicInformation(Fcb, Ccb, FileObject, (PFILE_BASIC_INFORMATION)PtrSystemBuffer);
402 break;
403 case FilePositionInformation: {
404 // Check if no intermediate buffering has been specified.
405 // If it was specified, do not allow non-aligned set file
406 // position requests to succeed.
407 PFILE_POSITION_INFORMATION PtrFileInfoBuffer;
408
409 PtrFileInfoBuffer = (PFILE_POSITION_INFORMATION)PtrSystemBuffer;
410
411 if(FileObject->Flags & FO_NO_INTERMEDIATE_BUFFERING) {
412 if(PtrFileInfoBuffer->CurrentByteOffset.LowPart & IrpSp->DeviceObject->AlignmentRequirement) {
413 // Invalid alignment.
414 try_return(RC = STATUS_INVALID_PARAMETER);
415 }
416 }
417
418 FileObject->CurrentByteOffset = PtrFileInfoBuffer->CurrentByteOffset;
419 break;
420 }
421 case FileDispositionInformation:
422 RC = UDFSetDispositionInformation(Fcb, Ccb, Vcb, FileObject,
423 ((PFILE_DISPOSITION_INFORMATION)PtrSystemBuffer)->DeleteFile ? TRUE : FALSE);
424 break;
425 case FileRenameInformation:
426 if(!CanWait) {
427 PostRequest = TRUE;
428 try_return(RC = STATUS_PENDING);
429 }
430 RC = UDFRename(IrpSp, Fcb, Ccb, FileObject, (PFILE_RENAME_INFORMATION)PtrSystemBuffer);
431 if(RC == STATUS_PENDING) {
432 PostRequest = TRUE;
433 try_return(RC);
434 }
435 break;
436 #ifdef UDF_ALLOW_HARD_LINKS
437 case FileLinkInformation:
438 if(!CanWait) {
439 PostRequest = TRUE;
440 try_return(RC = STATUS_PENDING);
441 }
442 RC = UDFHardLink(IrpSp, Fcb, Ccb, FileObject, (PFILE_LINK_INFORMATION)PtrSystemBuffer);
443 break;
444 #endif //UDF_ALLOW_HARD_LINKS
445 case FileAllocationInformation:
446 RC = UDFSetAllocationInformation(Fcb, Ccb, Vcb, FileObject,
447 PtrIrpContext, Irp,
448 (PFILE_ALLOCATION_INFORMATION)PtrSystemBuffer);
449 break;
450 case FileEndOfFileInformation:
451 RC = UDFSetEOF(IrpSp, Fcb, Ccb, Vcb, FileObject, Irp, (PFILE_END_OF_FILE_INFORMATION)PtrSystemBuffer);
452 break;
453 default:
454 RC = STATUS_INVALID_PARAMETER;
455 try_return(RC);
456 }
457 #endif //UDF_READ_ONLY_BUILD
458 }
459
460 try_exit: NOTHING;
461
462 } _SEH2_FINALLY {
463
464 if(PagingIoResourceAcquired) {
465 UDFReleaseResource(&(NtReqFcb->PagingIoResource));
466 PagingIoResourceAcquired = FALSE;
467 }
468
469 if(MainResourceAcquired) {
470 UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb);
471 UDFReleaseResource(&(NtReqFcb->MainResource));
472 MainResourceAcquired = FALSE;
473 }
474
475 if(ParentResourceAcquired) {
476 UDF_CHECK_PAGING_IO_RESOURCE(Fcb->ParentFcb->NTRequiredFCB);
477 UDFReleaseResource(&(Fcb->ParentFcb->NTRequiredFCB->MainResource));
478 ParentResourceAcquired = FALSE;
479 }
480
481 // Post IRP if required
482 if(PostRequest) {
483
484 // Since, the I/O Manager gave us a system buffer, we do not
485 // need to "lock" anything.
486
487 // Perform the post operation which will mark the IRP pending
488 // and will return STATUS_PENDING back to us
489 RC = UDFPostRequest(PtrIrpContext, Irp);
490
491 } else {
492
493 if (!_SEH2_AbnormalTermination()) {
494 Irp->IoStatus.Status = RC;
495 // Set status for "query" requests
496 if(IrpSp->MajorFunction == IRP_MJ_QUERY_INFORMATION) {
497 // Return the amount of information transferred.
498 Irp->IoStatus.Information = IrpSp->Parameters.QueryFile.Length - BufferLength;
499 #ifndef UDF_READ_ONLY_BUILD
500 #ifdef UDF_DELAYED_CLOSE
501 } else
502 if(NT_SUCCESS(RC)) {
503 if(FunctionalityRequested == FileDispositionInformation) {
504 if(AcquiredVcb) {
505 AcquiredVcb = FALSE;
506 UDFReleaseResource(&(Vcb->VCBResource));
507 }
508 UDFRemoveFromDelayedQueue(Fcb);
509 }
510 #endif //UDF_DELAYED_CLOSE
511 #endif //UDF_READ_ONLY_BUILD
512 }
513 // complete the IRP
514 IoCompleteRequest(Irp, IO_DISK_INCREMENT);
515 // Free up the Irp Context
516 UDFReleaseIrpContext(PtrIrpContext);
517 } // can we complete the IRP ?
518
519 }
520 if(AcquiredVcb) {
521 UDFReleaseResource(&(Vcb->VCBResource));
522 }
523 } _SEH2_END;// end of "__finally" processing
524
525 return(RC);
526 } // end UDFCommonFileInfo()
527
528 /*
529 Return some time-stamps and file attributes to the caller.
530 */
531 NTSTATUS
532 UDFGetBasicInformation(
533 IN PFILE_OBJECT FileObject,
534 IN PtrUDFFCB Fcb,
535 IN PFILE_BASIC_INFORMATION PtrBuffer,
536 IN OUT LONG* PtrReturnedLength
537 )
538 {
539 NTSTATUS RC = STATUS_SUCCESS;
540 PUDF_FILE_INFO FileInfo;
541 PDIR_INDEX_ITEM DirNdx;
542
543 AdPrint(("UDFGetBasicInformation: \n"));
544
545 _SEH2_TRY {
546
547 if(*PtrReturnedLength < (LONG)sizeof(FILE_BASIC_INFORMATION)) {
548 try_return(RC = STATUS_BUFFER_OVERFLOW);
549 }
550
551 // Zero out the supplied buffer.
552 RtlZeroMemory(PtrBuffer, sizeof(FILE_BASIC_INFORMATION));
553
554 // Get information from the FCB and update TimesCache in DirIndex
555 FileInfo = Fcb->FileInfo;
556
557 if(!FileInfo) {
558 AdPrint(("!!!!!!!! Bu-u-u-u-u-g !!!!!!!!!!!\n"));
559 AdPrint(("!!!! GetBasicInfo to unopened file !!!!\n"));
560 try_return(RC = STATUS_INVALID_PARAMETER);
561 }
562
563 DirNdx = UDFDirIndex(UDFGetDirIndexByFileInfo(FileInfo), FileInfo->Index);
564
565 PtrBuffer->CreationTime = Fcb->NTRequiredFCB->CreationTime;
566 DirNdx->CreationTime = PtrBuffer->CreationTime.QuadPart;
567
568 PtrBuffer->LastAccessTime = Fcb->NTRequiredFCB->LastAccessTime;
569 DirNdx->LastAccessTime = PtrBuffer->LastAccessTime.QuadPart;
570
571 PtrBuffer->LastWriteTime = Fcb->NTRequiredFCB->LastWriteTime;
572 DirNdx->LastWriteTime = PtrBuffer->LastWriteTime.QuadPart;
573
574 PtrBuffer->ChangeTime = Fcb->NTRequiredFCB->ChangeTime;
575 DirNdx->ChangeTime = PtrBuffer->ChangeTime.QuadPart;
576
577 // Now fill in the attributes.
578 if(Fcb->FCBFlags & UDF_FCB_DIRECTORY) {
579 PtrBuffer->FileAttributes = FILE_ATTRIBUTE_DIRECTORY;
580 #ifdef UDF_DBG
581 if(!FileInfo->Dloc->DirIndex) AdPrint(("*****!!!!! Directory has no DirIndex !!!!!*****\n"));
582 #endif
583 }
584 // Similarly, fill in attributes indicating a hidden file, system
585 // file, compressed file, temporary file, etc. if the FSD supports
586 // such file attribute values.
587 PtrBuffer->FileAttributes |= UDFAttributesToNT(DirNdx,NULL);
588 if(FileObject->Flags & FO_TEMPORARY_FILE) {
589 PtrBuffer->FileAttributes |= FILE_ATTRIBUTE_TEMPORARY;
590 } else {
591 PtrBuffer->FileAttributes &= ~FILE_ATTRIBUTE_TEMPORARY;
592 }
593 if(!PtrBuffer->FileAttributes) {
594 PtrBuffer->FileAttributes = FILE_ATTRIBUTE_NORMAL;
595 }
596
597 try_exit: NOTHING;
598
599 } _SEH2_FINALLY {
600
601 if(NT_SUCCESS(RC)) {
602 // Return the amount of information filled in.
603 (*PtrReturnedLength) -= sizeof(FILE_BASIC_INFORMATION);
604 }
605 } _SEH2_END;
606 return(RC);
607 } // end UDFGetBasicInformation()
608
609
610 /*
611 Return file sizes to the caller.
612 */
613 NTSTATUS
614 UDFGetStandardInformation(
615 IN PtrUDFFCB Fcb,
616 IN PFILE_STANDARD_INFORMATION PtrBuffer,
617 IN OUT LONG* PtrReturnedLength
618 )
619 {
620 NTSTATUS RC = STATUS_SUCCESS;
621 PUDF_FILE_INFO FileInfo;
622 // PVCB Vcb;
623
624 AdPrint(("UDFGetStandardInformation: \n"));
625
626 _SEH2_TRY {
627
628 if(*PtrReturnedLength < (LONG)sizeof(FILE_STANDARD_INFORMATION)) {
629 try_return(RC = STATUS_BUFFER_OVERFLOW);
630 }
631
632 // Zero out the supplied buffer.
633 RtlZeroMemory(PtrBuffer, sizeof(FILE_STANDARD_INFORMATION));
634
635 FileInfo = Fcb->FileInfo;
636
637 if(!FileInfo) {
638 AdPrint(("!!!!!!!! Bu-u-u-u-u-g !!!!!!!!!!!\n"));
639 AdPrint(("!!!! GetStandardInfo to unopened file !!!!\n"));
640 try_return(RC = STATUS_INVALID_PARAMETER);
641 }
642 // Vcb = Fcb->Vcb;
643 PtrBuffer->NumberOfLinks = UDFGetFileLinkCount(FileInfo);
644 PtrBuffer->DeletePending = (Fcb->FCBFlags & UDF_FCB_DELETE_ON_CLOSE) ? TRUE : FALSE;
645
646 // Case on whether this is a file or a directory, and extract
647 // the information and fill in the fcb/dcb specific parts
648 // of the output buffer
649 if(UDFIsADirectory(Fcb->FileInfo)) {
650 PtrBuffer->Directory = TRUE;
651 } else {
652 if(Fcb->NTRequiredFCB->CommonFCBHeader.AllocationSize.LowPart == 0xffffffff) {
653 Fcb->NTRequiredFCB->CommonFCBHeader.AllocationSize.QuadPart =
654 UDFSysGetAllocSize(Fcb->Vcb, UDFGetFileSize(FileInfo));
655 }
656 PtrBuffer->AllocationSize = Fcb->NTRequiredFCB->CommonFCBHeader.AllocationSize;
657 PtrBuffer->EndOfFile = Fcb->NTRequiredFCB->CommonFCBHeader.FileSize;
658
659 PtrBuffer->Directory = FALSE;
660 }
661
662 try_exit: NOTHING;
663 } _SEH2_FINALLY {
664 if(NT_SUCCESS(RC)) {
665 // Return the amount of information filled in.
666 *PtrReturnedLength -= sizeof(FILE_STANDARD_INFORMATION);
667 }
668 } _SEH2_END;
669 return(RC);
670 } // end UDFGetStandardInformation()
671
672 /*
673 Return some time-stamps and file attributes to the caller.
674 */
675 NTSTATUS
676 UDFGetNetworkInformation(
677 IN PtrUDFFCB Fcb,
678 IN PFILE_NETWORK_OPEN_INFORMATION PtrBuffer,
679 IN OUT PLONG PtrReturnedLength
680 )
681 {
682 NTSTATUS RC = STATUS_SUCCESS;
683 PUDF_FILE_INFO FileInfo;
684
685 AdPrint(("UDFGetNetworkInformation: \n"));
686
687 _SEH2_TRY {
688
689 if(*PtrReturnedLength < (LONG)sizeof(FILE_NETWORK_OPEN_INFORMATION)) {
690 try_return(RC = STATUS_BUFFER_OVERFLOW);
691 }
692
693 // Zero out the supplied buffer.
694 RtlZeroMemory(PtrBuffer, sizeof(FILE_NETWORK_OPEN_INFORMATION));
695
696 // Get information from the FCB.
697 PtrBuffer->CreationTime = Fcb->NTRequiredFCB->CreationTime;
698 PtrBuffer->LastAccessTime = Fcb->NTRequiredFCB->LastAccessTime;
699 PtrBuffer->LastWriteTime = Fcb->NTRequiredFCB->LastWriteTime;
700
701 FileInfo = Fcb->FileInfo;
702
703 if(!FileInfo) {
704 AdPrint(("!!!!!!!! Bu-u-u-u-u-g !!!!!!!!!!!\n"));
705 AdPrint(("!!!! UDFGetNetworkInformation to unopened file !!!!\n"));
706 try_return(RC = STATUS_INVALID_PARAMETER);
707 }
708 // Now fill in the attributes.
709 if(Fcb->FCBFlags & UDF_FCB_DIRECTORY) {
710 PtrBuffer->FileAttributes = FILE_ATTRIBUTE_DIRECTORY;
711 #ifdef UDF_DBG
712 if(!FileInfo->Dloc->DirIndex) AdPrint(("*****!!!!! Directory has no DirIndex !!!!!*****\n"));
713 #endif
714 }
715 // Similarly, fill in attributes indicating a hidden file, system
716 // file, compressed file, temporary file, etc. if the FSD supports
717 // such file attribute values.
718 PtrBuffer->FileAttributes |= UDFAttributesToNT(UDFDirIndex(UDFGetDirIndexByFileInfo(FileInfo), FileInfo->Index),NULL);
719 if(!PtrBuffer->FileAttributes) {
720 PtrBuffer->FileAttributes = FILE_ATTRIBUTE_NORMAL;
721 }
722
723 try_exit: NOTHING;
724
725 } _SEH2_FINALLY {
726 if(NT_SUCCESS(RC)) {
727 // Return the amount of information filled in.
728 (*PtrReturnedLength) -= sizeof(FILE_NETWORK_OPEN_INFORMATION);
729 }
730 } _SEH2_END;
731 return(RC);
732 } // end UDFGetNetworkInformation()
733
734
735 /*
736 Return some time-stamps and file attributes to the caller.
737 */
738 NTSTATUS
739 UDFGetInternalInformation(
740 PtrUDFIrpContext PtrIrpContext,
741 IN PtrUDFFCB Fcb,
742 IN PtrUDFCCB Ccb,
743 IN PFILE_INTERNAL_INFORMATION PtrBuffer,
744 IN OUT PLONG PtrReturnedLength
745 )
746 {
747 NTSTATUS RC = STATUS_SUCCESS;
748 PUDF_FILE_INFO FileInfo;
749 PVCB Vcb;
750
751 AdPrint(("UDFGetInternalInformation\n"));
752
753 _SEH2_TRY {
754
755 if(*PtrReturnedLength < (LONG)sizeof(FILE_INTERNAL_INFORMATION)) {
756 try_return(RC = STATUS_BUFFER_OVERFLOW);
757 }
758
759 // Zero out the supplied buffer.
760 RtlZeroMemory(PtrBuffer, sizeof(FILE_INTERNAL_INFORMATION));
761
762 FileInfo = Fcb->FileInfo;
763
764 if(!FileInfo) {
765 AdPrint(("!!!!!!!! Bu-u-u-u-u-g !!!!!!!!!!!\n"));
766 AdPrint(("!!!! UDFGetInternalInformation to unopened file !!!!\n"));
767 try_return(RC = STATUS_INVALID_PARAMETER);
768 }
769
770 Vcb = Fcb->Vcb;
771 PtrBuffer->IndexNumber.QuadPart = UDFGetNTFileId(Vcb, FileInfo, &(Fcb->FCBName->ObjectName));
772
773 UDFAcquireResourceExclusive(&(Fcb->Vcb->FileIdResource), TRUE);
774 // remember File Id & full path
775 UDFStoreFileId(Fcb->Vcb, Ccb, FileInfo, PtrBuffer->IndexNumber.QuadPart);
776 UDFReleaseResource(&(Fcb->Vcb->FileIdResource));
777
778 try_exit: NOTHING;
779
780 } _SEH2_FINALLY {
781 if(NT_SUCCESS(RC)) {
782 // Return the amount of information filled in.
783 *PtrReturnedLength -= sizeof(FILE_INTERNAL_INFORMATION);
784 }
785 } _SEH2_END;
786 return(RC);
787 } // end UDFGetInternalInformation()
788
789 /*
790 Return zero-filled EAs to the caller.
791 */
792 NTSTATUS
793 UDFGetEaInformation(
794 PtrUDFIrpContext PtrIrpContext,
795 IN PtrUDFFCB Fcb,
796 IN PFILE_EA_INFORMATION PtrBuffer,
797 IN OUT PLONG PtrReturnedLength
798 )
799 {
800 NTSTATUS RC = STATUS_SUCCESS;
801
802 AdPrint(("UDFGetEaInformation\n"));
803
804 _SEH2_TRY {
805
806 if(*PtrReturnedLength < (LONG)sizeof(FILE_EA_INFORMATION)) {
807 try_return(RC = STATUS_BUFFER_OVERFLOW);
808 }
809
810 // Zero out the supplied buffer.
811 PtrBuffer->EaSize = 0;
812
813 try_exit: NOTHING;
814
815 } _SEH2_FINALLY {
816 if(NT_SUCCESS(RC)) {
817 // Return the amount of information filled in.
818 *PtrReturnedLength -= sizeof(FILE_EA_INFORMATION);
819 }
820 } _SEH2_END;
821 return(RC);
822 } // end UDFGetEaInformation()
823
824 /*
825 Return file's long name to the caller.
826 */
827 NTSTATUS
828 UDFGetFullNameInformation(
829 IN PFILE_OBJECT FileObject,
830 IN PFILE_NAME_INFORMATION PtrBuffer,
831 IN OUT PLONG PtrReturnedLength
832 )
833 {
834 NTSTATUS RC = STATUS_SUCCESS;
835
836
837 AdPrint(("UDFGetFullNameInformation\n"));
838
839 PtrBuffer->FileNameLength = FileObject->FileName.Length;
840
841 if (PtrBuffer->FileNameLength + sizeof( ULONG ) > (ULONG)(*PtrReturnedLength)) {
842
843 PtrBuffer->FileNameLength = *PtrReturnedLength - sizeof( ULONG );
844 RC = STATUS_BUFFER_OVERFLOW;
845 }
846
847 RtlCopyMemory( PtrBuffer->FileName, FileObject->FileName.Buffer, PtrBuffer->FileNameLength );
848
849 // Reduce the available bytes by the amount stored into this buffer.
850 *PtrReturnedLength -= sizeof( ULONG ) + PtrBuffer->FileNameLength;
851
852 return RC;
853 } // end UDFGetFullNameInformation()
854
855 /*
856 Return file short(8.3) name to the caller.
857 */
858 NTSTATUS
859 UDFGetAltNameInformation(
860 IN PtrUDFFCB Fcb,
861 IN PFILE_NAME_INFORMATION PtrBuffer,
862 IN OUT PLONG PtrReturnedLength
863 )
864 {
865 PDIR_INDEX_ITEM DirNdx;
866 ULONG BytesToCopy;
867 UNICODE_STRING ShortName;
868 WCHAR ShortNameBuffer[13];
869
870 AdPrint(("UDFGetAltNameInformation: \n"));
871
872 *PtrReturnedLength -= FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]);
873 DirNdx = UDFDirIndex(UDFGetDirIndexByFileInfo(Fcb->FileInfo), Fcb->FileInfo->Index);
874
875 ShortName.MaximumLength = 13 * sizeof(WCHAR);
876 ShortName.Buffer = (PWCHAR)&ShortNameBuffer;
877
878 UDFDOSName__(Fcb->Vcb, &ShortName, &(DirNdx->FName), Fcb->FileInfo);
879
880 if(*PtrReturnedLength < ShortName.Length) {
881 return(STATUS_BUFFER_OVERFLOW);
882 } else {
883 BytesToCopy = ShortName.Length;
884 *PtrReturnedLength -= ShortName.Length;
885 }
886
887 RtlCopyMemory( &(PtrBuffer->FileName),
888 ShortName.Buffer,
889 BytesToCopy );
890
891 PtrBuffer->FileNameLength = ShortName.Length;
892
893 return(STATUS_SUCCESS);
894 } // end UDFGetAltNameInformation()
895
896 /*
897 Get file position information
898 */
899 NTSTATUS
900 UDFGetPositionInformation(
901 IN PFILE_OBJECT FileObject,
902 IN PFILE_POSITION_INFORMATION PtrBuffer,
903 IN OUT PLONG PtrReturnedLength
904 )
905 {
906 if(*PtrReturnedLength < (LONG)sizeof(FILE_POSITION_INFORMATION)) {
907 return(STATUS_BUFFER_OVERFLOW);
908 }
909 PtrBuffer->CurrentByteOffset = FileObject->CurrentByteOffset;
910 // Modify the local variable for BufferLength appropriately.
911 *PtrReturnedLength -= sizeof(FILE_POSITION_INFORMATION);
912
913 return(STATUS_SUCCESS);
914 } // end UDFGetAltNameInformation()
915
916 /*
917 Get file file stream(s) information
918 */
919 NTSTATUS
920 UDFGetFileStreamInformation(
921 IN PtrUDFFCB Fcb,
922 IN PFILE_STREAM_INFORMATION PtrBuffer,
923 IN OUT PLONG PtrReturnedLength
924 )
925 {
926 NTSTATUS RC = STATUS_SUCCESS;
927 PUDF_FILE_INFO FileInfo;
928 PUDF_FILE_INFO SDirInfo;
929 PVCB Vcb;
930 BOOLEAN FcbAcquired = FALSE;
931 uint_di i;
932 LONG l;
933 PDIR_INDEX_HDR hSDirIndex;
934 PDIR_INDEX_ITEM SDirIndex;
935 PFILE_BOTH_DIR_INFORMATION NTFileInfo = NULL;
936
937 AdPrint(("UDFGetFileStreamInformation\n"));
938
939 _SEH2_TRY {
940
941 UDFAcquireResourceExclusive(&(Fcb->Vcb->FileIdResource), TRUE);
942 FcbAcquired = TRUE;
943
944 FileInfo = Fcb->FileInfo;
945 if(!FileInfo) {
946 AdPrint(("!!!!!!!! Bu-u-u-u-u-g !!!!!!!!!!!\n"));
947 AdPrint(("!!!! UDFGetFileStreamInformation to unopened file !!!!\n"));
948 try_return(RC = STATUS_INVALID_PARAMETER);
949 }
950 Vcb = Fcb->Vcb;
951 // Zero out the supplied buffer.
952 RtlZeroMemory(PtrBuffer, *PtrReturnedLength);
953 if(!(SDirInfo = FileInfo->Dloc->SDirInfo) ||
954 UDFIsSDirDeleted(SDirInfo) ) {
955 (*PtrReturnedLength) -= (sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR));
956 try_return(RC = STATUS_SUCCESS);
957 }
958
959 hSDirIndex = SDirInfo->Dloc->DirIndex;
960 NTFileInfo = (PFILE_BOTH_DIR_INFORMATION)MyAllocatePool__(NonPagedPool, sizeof(FILE_BOTH_DIR_INFORMATION)+UDF_NAME_LEN*sizeof(WCHAR));
961 if(!NTFileInfo) try_return(RC = STATUS_INSUFFICIENT_RESOURCES);
962
963 for(i=2; (SDirIndex = UDFDirIndex(hSDirIndex,i)); i++) {
964 if((SDirIndex->FI_Flags & UDF_FI_FLAG_FI_INTERNAL) ||
965 UDFIsDeleted(SDirIndex) ||
966 !SDirIndex->FName.Buffer )
967 continue;
968 // copy data to buffer
969 if(*PtrReturnedLength < (l = ((sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR)) +
970 SDirIndex->FName.Length + 3) & (~3)) ) {
971 try_return(RC = STATUS_BUFFER_OVERFLOW);
972 }
973 RC = UDFFileDirInfoToNT(Vcb, SDirIndex, NTFileInfo);
974
975 PtrBuffer->NextEntryOffset = l;
976 PtrBuffer->StreamNameLength = SDirIndex->FName.Length;
977 PtrBuffer->StreamSize = NTFileInfo->EndOfFile;
978 PtrBuffer->StreamAllocationSize = NTFileInfo->AllocationSize;
979 RtlCopyMemory(&(PtrBuffer->StreamName), SDirIndex->FName.Buffer, SDirIndex->FName.Length);
980 *PtrReturnedLength -= l;
981 *((PCHAR*)(&PtrBuffer)) += l;
982 }
983
984 try_exit: NOTHING;
985
986 } _SEH2_FINALLY {
987 if(FcbAcquired)
988 UDFReleaseResource(&(Fcb->Vcb->FileIdResource));
989 if(NTFileInfo)
990 MyFreePool__(NTFileInfo);
991 } _SEH2_END;
992 return(RC);
993 } // end UDFGetFileStreamInformation()
994
995 //*******************************************************************
996
997 #ifndef UDF_READ_ONLY_BUILD
998
999 /*
1000 Set some time-stamps and file attributes supplied by the caller.
1001 */
1002 NTSTATUS
1003 UDFSetBasicInformation(
1004 IN PtrUDFFCB Fcb,
1005 IN PtrUDFCCB Ccb,
1006 IN PFILE_OBJECT FileObject,
1007 IN PFILE_BASIC_INFORMATION PtrBuffer)
1008 {
1009 NTSTATUS RC = STATUS_SUCCESS;
1010 ULONG NotifyFilter = 0;
1011
1012 AdPrint(("UDFSetBasicInformation\n"));
1013
1014 _SEH2_TRY {
1015
1016 // Obtain a pointer to the directory entry associated with
1017 // the FCB being modifed. The directory entry is obviously
1018 // part of the data associated with the parent directory that
1019 // contains this particular file stream.
1020 if(PtrBuffer->FileAttributes) {
1021 UDFUpdateAttrTime(Fcb->Vcb, Fcb->FileInfo);
1022 } else
1023 if( UDFIsADirectory(Fcb->FileInfo) &&
1024 !(Fcb->Vcb->CompatFlags & UDF_VCB_IC_UPDATE_UCHG_DIR_ACCESS_TIME) &&
1025 ((Fcb->FileInfo->Dloc->DataLoc.Modified ||
1026 Fcb->FileInfo->Dloc->AllocLoc.Modified ||
1027 (Fcb->FileInfo->Dloc->FE_Flags & UDF_FE_FLAG_FE_MODIFIED) ||
1028 Fcb->FileInfo->Dloc->FELoc.Modified))
1029 ) {
1030 // ignore Access Time Modification for unchanged Dir
1031 if(!PtrBuffer->CreationTime.QuadPart &&
1032 PtrBuffer->LastAccessTime.QuadPart &&
1033 !PtrBuffer->ChangeTime.QuadPart &&
1034 !PtrBuffer->LastWriteTime.QuadPart)
1035 try_return(RC);
1036 }
1037
1038 UDFSetFileXTime(Fcb->FileInfo,
1039 &(PtrBuffer->CreationTime.QuadPart),
1040 &(PtrBuffer->LastAccessTime.QuadPart),
1041 &(PtrBuffer->ChangeTime.QuadPart),
1042 &(PtrBuffer->LastWriteTime.QuadPart) );
1043
1044 if(PtrBuffer->CreationTime.QuadPart) {
1045 // The interesting thing here is that the user has set certain time
1046 // fields. However, before doing this, the user may have performed
1047 // I/O which in turn would have caused FSD to mark the fact that
1048 // write/access time should be modifed at cleanup.
1049 // We'll mark the fact that such updates are no longer
1050 // required since the user has explicitly specified the values he
1051 // wishes to see associated with the file stream.
1052 Fcb->NTRequiredFCB->CreationTime = PtrBuffer->CreationTime;
1053 Ccb->CCBFlags |= UDF_CCB_CREATE_TIME_SET;
1054 NotifyFilter |= FILE_NOTIFY_CHANGE_CREATION;
1055 }
1056 if(PtrBuffer->LastAccessTime.QuadPart) {
1057 Fcb->NTRequiredFCB->LastAccessTime = PtrBuffer->LastAccessTime;
1058 Ccb->CCBFlags |= UDF_CCB_ACCESS_TIME_SET;
1059 NotifyFilter |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
1060 }
1061 if(PtrBuffer->ChangeTime.QuadPart) {
1062 Fcb->NTRequiredFCB->ChangeTime = PtrBuffer->ChangeTime;
1063 Ccb->CCBFlags |= UDF_CCB_MODIFY_TIME_SET;
1064 }
1065 if(PtrBuffer->LastWriteTime.QuadPart) {
1066 Fcb->NTRequiredFCB->LastWriteTime = PtrBuffer->LastWriteTime;
1067 Ccb->CCBFlags |= UDF_CCB_WRITE_TIME_SET;
1068 NotifyFilter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
1069 }
1070
1071 // Now come the attributes.
1072 if(PtrBuffer->FileAttributes) {
1073 // We have a non-zero attribute value.
1074 // The presence of a particular attribute indicates that the
1075 // user wishes to set the attribute value. The absence indicates
1076 // the user wishes to clear the particular attribute.
1077
1078 // Our routine ignores unsupported flags
1079 PtrBuffer->FileAttributes &= ~(FILE_ATTRIBUTE_NORMAL);
1080
1081 // Similarly, we should pick out other invalid flag values.
1082 if( (PtrBuffer->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
1083 !(Fcb->FCBFlags & UDF_FCB_DIRECTORY))
1084 try_return(RC = STATUS_INVALID_PARAMETER);
1085
1086 if(PtrBuffer->FileAttributes & FILE_ATTRIBUTE_TEMPORARY) {
1087 if(Fcb->FCBFlags & UDF_FCB_DIRECTORY)
1088 try_return(RC = STATUS_INVALID_PARAMETER);
1089 FileObject->Flags |= FO_TEMPORARY_FILE;
1090 } else {
1091 FileObject->Flags &= ~FO_TEMPORARY_FILE;
1092 }
1093
1094 if(PtrBuffer->FileAttributes & FILE_ATTRIBUTE_READONLY) {
1095 Fcb->FCBFlags |= UDF_FCB_READ_ONLY;
1096 } else {
1097 Fcb->FCBFlags &= ~UDF_FCB_READ_ONLY;
1098 }
1099
1100 UDFAttributesToUDF(UDFDirIndex(UDFGetDirIndexByFileInfo(Fcb->FileInfo), Fcb->FileInfo->Index),
1101 NULL, PtrBuffer->FileAttributes);
1102
1103 (UDFDirIndex(UDFGetDirIndexByFileInfo(Fcb->FileInfo), Fcb->FileInfo->Index))
1104 ->FI_Flags |= UDF_FI_FLAG_SYS_ATTR;
1105 // If the FSD supports file compression, we may wish to
1106 // note the user's preferences for compressing/not compressing
1107 // the file at this time.
1108 Ccb->CCBFlags |= UDF_CCB_ATTRIBUTES_SET;
1109 NotifyFilter |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
1110 }
1111
1112 if(NotifyFilter) {
1113 UDFNotifyFullReportChange( Fcb->Vcb, Fcb->FileInfo,
1114 NotifyFilter, FILE_ACTION_MODIFIED);
1115 UDFSetFileSizeInDirNdx(Fcb->Vcb, Fcb->FileInfo, NULL);
1116 Fcb->FileInfo->Dloc->FE_Flags |= UDF_FE_FLAG_FE_MODIFIED;
1117 }
1118
1119 try_exit: NOTHING;
1120 } _SEH2_FINALLY {
1121 ;
1122 } _SEH2_END;
1123 return(RC);
1124 } // end UDFSetBasicInformation()
1125
1126 NTSTATUS
1127 UDFMarkStreamsForDeletion(
1128 IN PVCB Vcb,
1129 IN PtrUDFFCB Fcb,
1130 IN BOOLEAN ForDel
1131 )
1132 {
1133 NTSTATUS RC = STATUS_SUCCESS;
1134 PUDF_FILE_INFO SDirInfo = NULL;
1135 PUDF_FILE_INFO FileInfo = NULL;
1136 ULONG lc;
1137 BOOLEAN SDirAcq = FALSE;
1138 BOOLEAN StrAcq = FALSE;
1139 uint_di d,i;
1140
1141 _SEH2_TRY {
1142
1143 // In some cases we needn't marking Streams for deleteion
1144 // (Not opened or Don't exist)
1145 if(UDFIsAStream(Fcb->FileInfo) ||
1146 UDFIsAStreamDir(Fcb->FileInfo) ||
1147 !UDFHasAStreamDir(Fcb->FileInfo) ||
1148 !Fcb->FileInfo->Dloc->SDirInfo ||
1149 UDFIsSDirDeleted(Fcb->FileInfo->Dloc->SDirInfo) ||
1150 (UDFGetFileLinkCount(Fcb->FileInfo) > 1) )
1151 try_return (RC /*=STATUS_SUCCESS*/);
1152
1153 // We shall mark Streams for deletion if there is no
1154 // Links to the file. Otherwise we'll delete only the file.
1155 // If we are asked to unmark Streams, we'll precess the whole Tree
1156 RC = UDFOpenStreamDir__(Vcb, Fcb->FileInfo, &SDirInfo);
1157 if(!NT_SUCCESS(RC))
1158 try_return(RC);
1159
1160 if(SDirInfo->Fcb &&
1161 SDirInfo->Fcb->NTRequiredFCB) {
1162 UDF_CHECK_PAGING_IO_RESOURCE(SDirInfo->Fcb->NTRequiredFCB);
1163 UDFAcquireResourceExclusive(&(SDirInfo->Fcb->NTRequiredFCB->MainResource),TRUE);
1164 SDirAcq = TRUE;
1165 }
1166
1167 if(!ForDel || ((lc = UDFGetFileLinkCount(Fcb->FileInfo)) < 2)) {
1168
1169 UDF_DIR_SCAN_CONTEXT ScanContext;
1170 PDIR_INDEX_ITEM DirNdx;
1171
1172 // It is not worth checking whether the Stream can be deleted if
1173 // Undelete requested
1174 if(ForDel &&
1175 // scan DirIndex
1176 UDFDirIndexInitScan(SDirInfo, &ScanContext, 2)) {
1177
1178 // Check if we can delete Streams
1179 while((DirNdx = UDFDirIndexScan(&ScanContext, &FileInfo))) {
1180 if(!FileInfo)
1181 continue;
1182 if(FileInfo->Fcb) {
1183 FileInfo->Fcb->NTRequiredFCB->AcqFlushCount++;
1184 MmPrint((" MmFlushImageSection() for Stream\n"));
1185 if(!MmFlushImageSection(&(FileInfo->Fcb->NTRequiredFCB->SectionObject), MmFlushForDelete)) {
1186 FileInfo->Fcb->NTRequiredFCB->AcqFlushCount--;
1187 try_return(RC = STATUS_CANNOT_DELETE);
1188 }
1189 FileInfo->Fcb->NTRequiredFCB->AcqFlushCount--;
1190 }
1191 }
1192 }
1193 // (Un)Mark Streams for deletion
1194
1195 // Perform sequencial Open for Streams & mark 'em
1196 // for deletion. We should not get FileInfo pointers directly
1197 // from DirNdx[i] to prevent great troubles with linked
1198 // files. We should mark for deletion FI with proper ParentFile
1199 // pointer.
1200 d = UDFDirIndexGetLastIndex(SDirInfo->Dloc->DirIndex);
1201 for(i=2; i<d; i++) {
1202 RC = UDFOpenFile__(Vcb,
1203 FALSE,TRUE,NULL,
1204 SDirInfo,&FileInfo,&i);
1205 ASSERT(NT_SUCCESS(RC) || (RC == STATUS_FILE_DELETED));
1206 if(NT_SUCCESS(RC)) {
1207 if(FileInfo->Fcb) {
1208 if(FileInfo->Fcb->NTRequiredFCB) {
1209 UDF_CHECK_PAGING_IO_RESOURCE(FileInfo->Fcb->NTRequiredFCB);
1210 UDFAcquireResourceExclusive(&(FileInfo->Fcb->NTRequiredFCB->MainResource),TRUE);
1211 StrAcq = TRUE;
1212 }
1213 #ifndef UDF_ALLOW_LINKS_TO_STREAMS
1214 if(UDFGetFileLinkCount(FileInfo) >= 2) {
1215 // Currently, UDF_INFO package doesn't
1216 // support this case, so we'll inform developer
1217 // about this to prevent on-disk space leaks...
1218 BrutePoint();
1219 try_return(RC = STATUS_CANNOT_DELETE);
1220 }
1221 #endif //UDF_ALLOW_LINKS_TO_STREAMS
1222 if(ForDel) {
1223 AdPrint((" SET stream DeleteOnClose\n"));
1224 #ifdef UDF_DBG
1225 ASSERT(!(FileInfo->Fcb->FCBFlags & UDF_FCB_ROOT_DIRECTORY));
1226 if(FileInfo->ParentFile &&
1227 FileInfo->ParentFile->Fcb) {
1228 ASSERT(!(FileInfo->ParentFile->Fcb->FCBFlags & UDF_FCB_ROOT_DIRECTORY));
1229 }
1230 #endif // UDF_DBG
1231 FileInfo->Fcb->FCBFlags |= (UDF_FCB_DELETE_ON_CLOSE |
1232 UDF_FCB_DELETE_PARENT);
1233 } else {
1234 AdPrint((" CLEAR stream DeleteOnClose\n"));
1235 FileInfo->Fcb->FCBFlags &= !(UDF_FCB_DELETE_ON_CLOSE |
1236 UDF_FCB_DELETE_PARENT);
1237 }
1238 }
1239 UDFCloseFile__(Vcb, FileInfo);
1240 } else
1241 if(RC == STATUS_FILE_DELETED) {
1242 // That's OK if STATUS_FILE_DELETED returned...
1243 RC = STATUS_SUCCESS;
1244 }
1245 if(FileInfo) {
1246 if(UDFCleanUpFile__(Vcb, FileInfo)) {
1247 ASSERT(!StrAcq && !(FileInfo->Fcb));
1248 MyFreePool__(FileInfo);
1249 }
1250 if(StrAcq) {
1251 UDF_CHECK_PAGING_IO_RESOURCE(FileInfo->Fcb->NTRequiredFCB);
1252 UDFReleaseResource(&(FileInfo->Fcb->NTRequiredFCB->MainResource));
1253 StrAcq = FALSE;
1254 }
1255 }
1256 FileInfo = NULL;
1257 }
1258 // Mark SDir for deletion
1259 if(SDirInfo->Fcb) {
1260 if(ForDel) {
1261 #ifdef UDF_DBG
1262 ASSERT(!(SDirInfo->Fcb->FCBFlags & UDF_FCB_ROOT_DIRECTORY));
1263 if(SDirInfo->ParentFile &&
1264 SDirInfo->ParentFile->Fcb) {
1265 ASSERT(!(SDirInfo->ParentFile->Fcb->FCBFlags & UDF_FCB_ROOT_DIRECTORY));
1266 }
1267 #endif // UDF_DBG
1268 AdPrint((" SET stream dir DeleteOnClose\n"));
1269 SDirInfo->Fcb->FCBFlags |= (UDF_FCB_DELETE_ON_CLOSE |
1270 UDF_FCB_DELETE_PARENT);
1271 } else {
1272 AdPrint((" CLEAR stream dir DeleteOnClose\n"));
1273 SDirInfo->Fcb->FCBFlags &= ~(UDF_FCB_DELETE_ON_CLOSE |
1274 UDF_FCB_DELETE_PARENT);
1275 }
1276 }
1277 } else
1278 if(lc >= 2) {
1279 // if caller wants us to perform DelTree for Streams, but
1280 // someone keeps Stream opened and there is a Link to this
1281 // file, we can't delete it immediately (on Cleanup) & should
1282 // not delete the whole Tree. Instead, we'll set DELETE_PARENT
1283 // flag in SDir to kill this file later, when all the Handles
1284 // to Streams, opened via this file, would be closed
1285 #ifdef UDF_DBG
1286 ASSERT(!(SDirInfo->Fcb->FCBFlags & UDF_FCB_ROOT_DIRECTORY));
1287 if(SDirInfo->ParentFile &&
1288 SDirInfo->ParentFile->Fcb) {
1289 ASSERT(!(SDirInfo->ParentFile->Fcb->FCBFlags & UDF_FCB_ROOT_DIRECTORY));
1290 }
1291 #endif // UDF_DBG
1292 if(SDirInfo->Fcb)
1293 SDirInfo->Fcb->FCBFlags |= UDF_FCB_DELETE_PARENT;
1294 }
1295
1296 try_exit: NOTHING;
1297
1298 } _SEH2_FINALLY {
1299 if(FileInfo) {
1300 UDFCloseFile__(Vcb, FileInfo);
1301 if(UDFCleanUpFile__(Vcb, FileInfo)) {
1302 ASSERT(!StrAcq && !(FileInfo->Fcb));
1303 MyFreePool__(FileInfo);
1304 }
1305 if(StrAcq) {
1306 UDF_CHECK_PAGING_IO_RESOURCE(FileInfo->Fcb->NTRequiredFCB);
1307 UDFReleaseResource(&(FileInfo->Fcb->NTRequiredFCB->MainResource));
1308 }
1309 SDirInfo = NULL;
1310 }
1311 if(SDirInfo) {
1312 UDFCloseFile__(Vcb, SDirInfo);
1313 if(SDirAcq) {
1314 UDF_CHECK_PAGING_IO_RESOURCE(SDirInfo->Fcb->NTRequiredFCB);
1315 UDFReleaseResource(&(SDirInfo->Fcb->NTRequiredFCB->MainResource));
1316 }
1317 if(UDFCleanUpFile__(Vcb, SDirInfo)) {
1318 MyFreePool__(SDirInfo);
1319 }
1320 SDirInfo = NULL;
1321 }
1322 } _SEH2_END;
1323 return RC;
1324 } // end UDFMarkStreamsForDeletion()
1325
1326 /*
1327 (Un)Mark file for deletion.
1328 */
1329 NTSTATUS
1330 UDFSetDispositionInformation(
1331 IN PtrUDFFCB Fcb,
1332 IN PtrUDFCCB Ccb,
1333 IN PVCB Vcb,
1334 IN PFILE_OBJECT FileObject,
1335 IN BOOLEAN Delete
1336 )
1337 {
1338 NTSTATUS RC = STATUS_SUCCESS;
1339 // PUDF_FILE_INFO SDirInfo = NULL;
1340 // PUDF_FILE_INFO FileInfo = NULL;
1341 ULONG lc;
1342
1343 AdPrint(("UDFSetDispositionInformation\n"));
1344
1345 _SEH2_TRY {
1346
1347 if(!Delete) {
1348 AdPrint((" CLEAR DeleteOnClose\n"));
1349 // "un-delete" the file.
1350 Fcb->FCBFlags &= ~UDF_FCB_DELETE_ON_CLOSE;
1351 if(FileObject)
1352 FileObject->DeletePending = FALSE;
1353 RC = UDFMarkStreamsForDeletion(Vcb, Fcb, FALSE); // Undelete
1354 try_return(RC);
1355 }
1356 AdPrint((" SET DeleteOnClose\n"));
1357
1358 // The easy part is over. Now, we know that the user wishes to
1359 // delete the corresponding directory entry (of course, if this
1360 // is the only link to the file stream, any on-disk storage space
1361 // associated with the file stream will also be released when the
1362 // (only) link is deleted!)
1363
1364 // Do some checking to see if the file can even be deleted.
1365 if(Fcb->FCBFlags & UDF_FCB_DELETE_ON_CLOSE) {
1366 // All done!
1367 try_return(RC);
1368 }
1369
1370 if(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_READ_ONLY) {
1371 try_return(RC = STATUS_CANNOT_DELETE);
1372 }
1373
1374 if(Fcb->FCBFlags & UDF_FCB_READ_ONLY) {
1375 RC = UDFCheckAccessRights(NULL, NULL, Fcb->ParentFcb, NULL, FILE_DELETE_CHILD, 0);
1376 if(!NT_SUCCESS(RC)) {
1377 try_return (RC = STATUS_CANNOT_DELETE);
1378 }
1379 }
1380
1381 // It would not be prudent to allow deletion of either a root
1382 // directory or a directory that is not empty.
1383 if(Fcb->FCBFlags & UDF_FCB_ROOT_DIRECTORY)
1384 try_return(RC = STATUS_CANNOT_DELETE);
1385
1386 lc = UDFGetFileLinkCount(Fcb->FileInfo);
1387
1388 if(Fcb->FCBFlags & UDF_FCB_DIRECTORY) {
1389 // Perform check to determine whether the directory
1390 // is empty or not.
1391 if(!UDFIsDirEmpty__(Fcb->FileInfo)) {
1392 try_return(RC = STATUS_DIRECTORY_NOT_EMPTY);
1393 }
1394
1395 } else {
1396 // An important step is to check if the file stream has been
1397 // mapped by any process. The delete cannot be allowed to proceed
1398 // in this case.
1399 MmPrint((" MmFlushImageSection()\n"));
1400 Fcb->NTRequiredFCB->AcqFlushCount++;
1401 if(!MmFlushImageSection(&(Fcb->NTRequiredFCB->SectionObject),
1402 (lc > 1) ? MmFlushForWrite : MmFlushForDelete)) {
1403 Fcb->NTRequiredFCB->AcqFlushCount--;
1404 try_return(RC = STATUS_CANNOT_DELETE);
1405 }
1406 Fcb->NTRequiredFCB->AcqFlushCount--;
1407 }
1408 // We should also mark Streams for deletion if there are no
1409 // Links to the file. Otherwise we'll delete only the file
1410
1411 if(lc > 1) {
1412 RC = STATUS_SUCCESS;
1413 } else {
1414 RC = UDFMarkStreamsForDeletion(Vcb, Fcb, TRUE); // Delete
1415 if(!NT_SUCCESS(RC))
1416 try_return(RC);
1417 }
1418
1419 // Set a flag to indicate that this directory entry will become history
1420 // at cleanup.
1421 Fcb->FCBFlags |= UDF_FCB_DELETE_ON_CLOSE;
1422 if(FileObject)
1423 FileObject->DeletePending = TRUE;
1424
1425 if((Fcb->FCBFlags & UDF_FCB_DIRECTORY) && Ccb) {
1426 FsRtlNotifyFullChangeDirectory( Vcb->NotifyIRPMutex, &(Vcb->NextNotifyIRP),
1427 (PVOID)Ccb, NULL, FALSE, FALSE,
1428 0, NULL, NULL, NULL );
1429 }
1430
1431 try_exit: NOTHING;
1432
1433 } _SEH2_FINALLY {
1434 ;
1435 } _SEH2_END;
1436 return(RC);
1437 } // end UDFSetDispositionInformation()
1438
1439
1440 /*
1441 Change file allocation length.
1442 */
1443 NTSTATUS
1444 UDFSetAllocationInformation(
1445 IN PtrUDFFCB Fcb,
1446 IN PtrUDFCCB Ccb,
1447 IN PVCB Vcb,
1448 IN PFILE_OBJECT FileObject,
1449 IN PtrUDFIrpContext PtrIrpContext,
1450 IN PIRP Irp,
1451 IN PFILE_ALLOCATION_INFORMATION PtrBuffer
1452 )
1453 {
1454 NTSTATUS RC = STATUS_SUCCESS;
1455 BOOLEAN TruncatedFile = FALSE;
1456 BOOLEAN ModifiedAllocSize = FALSE;
1457 BOOLEAN CacheMapInitialized = FALSE;
1458 BOOLEAN AcquiredPagingIo = FALSE;
1459
1460 AdPrint(("UDFSetAllocationInformation\n"));
1461
1462 _SEH2_TRY {
1463 // Increasing the allocation size associated with a file stream
1464 // is relatively easy. All we have to do is execute some FSD
1465 // specific code to check whether we have enough space available
1466 // (and if the FSD supports user/volume quotas, whether the user
1467 // is not exceeding quota), and then increase the file size in the
1468 // corresponding on-disk and in-memory structures.
1469 // Then, all we should do is inform the Cache Manager about the
1470 // increased allocation size.
1471
1472 // First, do whatever error checking is appropriate here (e.g. whether
1473 // the caller is trying the change size for a directory, etc.).
1474 if(Fcb->FCBFlags & UDF_FCB_DIRECTORY)
1475 try_return(RC = STATUS_INVALID_PARAMETER);
1476
1477 Fcb->NTRequiredFCB->CommonFCBHeader.IsFastIoPossible = UDFIsFastIoPossible(Fcb);
1478
1479 if ((FileObject->SectionObjectPointer->DataSectionObject != NULL) &&
1480 (FileObject->SectionObjectPointer->SharedCacheMap == NULL) &&
1481 !FlagOn(Irp->Flags, IRP_PAGING_IO)) {
1482 ASSERT( !FlagOn( FileObject->Flags, FO_CLEANUP_COMPLETE ) );
1483 // Now initialize the cache map.
1484 MmPrint((" CcInitializeCacheMap()\n"));
1485 CcInitializeCacheMap( FileObject,
1486 (PCC_FILE_SIZES)&Fcb->NTRequiredFCB->CommonFCBHeader.AllocationSize,
1487 FALSE,
1488 &(UDFGlobalData.CacheMgrCallBacks),
1489 Fcb->NTRequiredFCB );
1490
1491 CacheMapInitialized = TRUE;
1492 }
1493
1494 // Are we increasing the allocation size?
1495 if(Fcb->NTRequiredFCB->CommonFCBHeader.AllocationSize.QuadPart <
1496 PtrBuffer->AllocationSize.QuadPart) {
1497
1498 // Yes. Do the FSD specific stuff i.e. increase reserved
1499 // space on disk.
1500 if(((LONGLONG)UDFGetFreeSpace(Vcb) << Vcb->LBlockSizeBits) < PtrBuffer->AllocationSize.QuadPart) {
1501 try_return(RC = STATUS_DISK_FULL);
1502 }
1503 // RC = STATUS_SUCCESS;
1504 ModifiedAllocSize = TRUE;
1505
1506 } else if(Fcb->NTRequiredFCB->CommonFCBHeader.AllocationSize.QuadPart >
1507 PtrBuffer->AllocationSize.QuadPart) {
1508 // This is the painful part. See if the VMM will allow us to proceed.
1509 // The VMM will deny the request if:
1510 // (a) any image section exists OR
1511 // (b) a data section exists and the size of the user mapped view
1512 // is greater than the new size
1513 // Otherwise, the VMM should allow the request to proceed.
1514 MmPrint((" MmCanFileBeTruncated()\n"));
1515 if(!MmCanFileBeTruncated(&(Fcb->NTRequiredFCB->SectionObject), &(PtrBuffer->AllocationSize))) {
1516 // VMM said no way!
1517 try_return(RC = STATUS_USER_MAPPED_FILE);
1518 }
1519
1520 // Perform our directory entry modifications. Release any on-disk
1521 // space we may need to in the process.
1522 ModifiedAllocSize = TRUE;
1523 TruncatedFile = TRUE;
1524 }
1525
1526 ASSERT(NT_SUCCESS(RC));
1527 // This is a good place to check if we have performed a truncate
1528 // operation. If we have perform a truncate (whether we extended
1529 // or reduced file size or even leave it intact), we should update
1530 // file time stamps.
1531 FileObject->Flags |= FO_FILE_MODIFIED;
1532
1533 // Last, but not the lease, we must inform the Cache Manager of file size changes.
1534 if(ModifiedAllocSize) {
1535
1536 // If we decreased the allocation size to less than the
1537 // current file size, modify the file size value.
1538 // Similarly, if we decreased the value to less than the
1539 // current valid data length, modify that value as well.
1540
1541 AcquiredPagingIo = UDFAcquireResourceExclusiveWithCheck(&(Fcb->NTRequiredFCB->PagingIoResource));
1542 // Update the FCB Header with the new allocation size.
1543 if(TruncatedFile) {
1544 if(Fcb->NTRequiredFCB->CommonFCBHeader.ValidDataLength.QuadPart >
1545 PtrBuffer->AllocationSize.QuadPart) {
1546 // Decrease the valid data length value.
1547 Fcb->NTRequiredFCB->CommonFCBHeader.ValidDataLength =
1548 PtrBuffer->AllocationSize;
1549 }
1550 if(Fcb->NTRequiredFCB->CommonFCBHeader.FileSize.QuadPart >
1551 PtrBuffer->AllocationSize.QuadPart) {
1552 // Decrease the file size value.
1553 Fcb->NTRequiredFCB->CommonFCBHeader.FileSize =
1554 PtrBuffer->AllocationSize;
1555 RC = UDFResizeFile__(Vcb, Fcb->FileInfo, PtrBuffer->AllocationSize.QuadPart);
1556 // UDFSetFileSizeInDirNdx(Vcb, Fcb->FileInfo, NULL);
1557 }
1558 } else {
1559 Fcb->NTRequiredFCB->CommonFCBHeader.AllocationSize = PtrBuffer->AllocationSize;
1560 // UDFSetFileSizeInDirNdx(Vcb, Fcb->FileInfo,
1561 // &(PtrBuffer->AllocationSize.QuadPart));
1562 }
1563 if(AcquiredPagingIo) {
1564 UDFReleaseResource(&(Fcb->NTRequiredFCB->PagingIoResource));
1565 AcquiredPagingIo = FALSE;
1566 }
1567 // If the FCB has not had caching initiated, it is still valid
1568 // for us to invoke the NT Cache Manager. It is possible in such
1569 // situations for the call to be no'oped (unless some user has
1570 // mapped in the file)
1571
1572 // NOTE: The invocation to CcSetFileSizes() will quite possibly
1573 // result in a recursive call back into the file system.
1574 // This is because the NT Cache Manager will typically
1575 // perform a flush before telling the VMM to purge pages
1576 // especially when caching has not been initiated on the
1577 // file stream, but the user has mapped the file into
1578 // the process' virtual address space.
1579 MmPrint((" CcSetFileSizes()\n"));
1580 Fcb->NTRequiredFCB->AcqFlushCount++;
1581 CcSetFileSizes(FileObject, (PCC_FILE_SIZES)&(Fcb->NTRequiredFCB->CommonFCBHeader.AllocationSize));
1582 Fcb->NTRequiredFCB->AcqFlushCount--;
1583 Fcb->NTRequiredFCB->NtReqFCBFlags |= UDF_NTREQ_FCB_MODIFIED;
1584
1585 // Inform any pending IRPs (notify change directory).
1586 if(UDFIsAStream(Fcb->FileInfo)) {
1587 UDFNotifyFullReportChange( Vcb, Fcb->FileInfo,
1588 FILE_NOTIFY_CHANGE_STREAM_SIZE,
1589 FILE_ACTION_MODIFIED_STREAM);
1590 } else {
1591 UDFNotifyFullReportChange( Vcb, Fcb->FileInfo,
1592 FILE_NOTIFY_CHANGE_SIZE,
1593 FILE_ACTION_MODIFIED);
1594 }
1595 }
1596
1597 try_exit: NOTHING;
1598
1599 } _SEH2_FINALLY {
1600 if(AcquiredPagingIo) {
1601 UDFReleaseResource(&(Fcb->NTRequiredFCB->PagingIoResource));
1602 AcquiredPagingIo = FALSE;
1603 }
1604 if (CacheMapInitialized) {
1605
1606 MmPrint((" CcUninitializeCacheMap()\n"));
1607 CcUninitializeCacheMap( FileObject, NULL, NULL );
1608 }
1609 } _SEH2_END;
1610 return(RC);
1611 } // end UDFSetAllocationInformation()
1612
1613 /*
1614 Set end of file (resize).
1615 */
1616 NTSTATUS
1617 UDFSetEOF(
1618 IN PIO_STACK_LOCATION PtrSp,
1619 IN PtrUDFFCB Fcb,
1620 IN PtrUDFCCB Ccb,
1621 IN PVCB Vcb,
1622 IN PFILE_OBJECT FileObject,
1623 IN PIRP Irp,
1624 IN PFILE_END_OF_FILE_INFORMATION PtrBuffer
1625 )
1626 {
1627 NTSTATUS RC = STATUS_SUCCESS;
1628 BOOLEAN TruncatedFile = FALSE;
1629 BOOLEAN ModifiedAllocSize = FALSE;
1630 ULONG Attr;
1631 PDIR_INDEX_ITEM DirNdx;
1632 PtrUDFNTRequiredFCB NtReqFcb = NULL;
1633 LONGLONG OldFileSize;
1634 // BOOLEAN ZeroBlock;
1635 BOOLEAN CacheMapInitialized = FALSE;
1636 BOOLEAN AcquiredPagingIo = FALSE;
1637
1638 AdPrint(("UDFSetEOF\n"));
1639
1640 _SEH2_TRY {
1641 // Increasing the allocation size associated with a file stream
1642 // is relatively easy. All we have to do is execute some FSD
1643 // specific code to check whether we have enough space available
1644 // (and if the FSD supports user/volume quotas, whether the user
1645 // is not exceeding quota), and then increase the file size in the
1646 // corresponding on-disk and in-memory structures.
1647 // Then, all we should do is inform the Cache Manager about the
1648 // increased allocation size.
1649
1650 // First, do whatever error checking is appropriate here (e.g. whether
1651 // the caller is trying the change size for a directory, etc.).
1652 if(Fcb->FCBFlags & UDF_FCB_DIRECTORY)
1653 try_return(RC = STATUS_INVALID_PARAMETER);
1654
1655 NtReqFcb = Fcb->NTRequiredFCB;
1656
1657 if((Fcb->FCBFlags & UDF_FCB_DELETED) ||
1658 (NtReqFcb->NtReqFCBFlags & UDF_NTREQ_FCB_DELETED)) {
1659 #ifdef UDF_DBG
1660 if(UDFGetFileLinkCount(Fcb->FileInfo) < 1) {
1661 BrutePoint();
1662 try_return(RC = STATUS_SUCCESS);
1663 } else
1664 #endif // UDF_DBG
1665 try_return(RC = STATUS_SUCCESS);
1666 }
1667
1668 NtReqFcb->CommonFCBHeader.IsFastIoPossible = UDFIsFastIoPossible(Fcb);
1669
1670 if ((FileObject->SectionObjectPointer->DataSectionObject != NULL) &&
1671 (FileObject->SectionObjectPointer->SharedCacheMap == NULL) &&
1672 !(Irp->Flags & IRP_PAGING_IO)) {
1673 ASSERT( !FlagOn( FileObject->Flags, FO_CLEANUP_COMPLETE ) );
1674 // Now initialize the cache map.
1675 MmPrint((" CcInitializeCacheMap()\n"));
1676 CcInitializeCacheMap( FileObject,
1677 (PCC_FILE_SIZES)&Fcb->NTRequiredFCB->CommonFCBHeader.AllocationSize,
1678 FALSE,
1679 &(UDFGlobalData.CacheMgrCallBacks),
1680 Fcb->NTRequiredFCB );
1681
1682 CacheMapInitialized = TRUE;
1683 }
1684
1685 AcquiredPagingIo = UDFAcquireResourceExclusiveWithCheck(&(Fcb->NTRequiredFCB->PagingIoResource));
1686 // Do a special case here for the lazy write of file sizes.
1687 if(PtrSp->Parameters.SetFile.AdvanceOnly) {
1688 // Never have the dirent filesize larger than the fcb filesize
1689 PtrBuffer->EndOfFile.QuadPart =
1690 min(PtrBuffer->EndOfFile.QuadPart,
1691 NtReqFcb->CommonFCBHeader.FileSize.QuadPart);
1692 // Only advance the file size, never reduce it with this call
1693 RC = STATUS_SUCCESS;
1694 if(UDFGetFileSizeFromDirNdx(Vcb, Fcb->FileInfo) >=
1695 PtrBuffer->EndOfFile.QuadPart)
1696 try_return(RC);
1697
1698 UDFSetFileSizeInDirNdx(Vcb, Fcb->FileInfo, &(PtrBuffer->EndOfFile.QuadPart));
1699 goto notify_size_changes;
1700 }
1701
1702 // !!! IMPORTANT !!!
1703
1704 // We can get here after all Handles to the file are closed
1705 // To prevent allocation size incoherency we should
1706 // reference FileInfo _before_ call to UDFResizeFile__()
1707 // and use UDFCloseFile__() _after_ that
1708
1709 // Are we increasing the allocation size?
1710 OldFileSize = NtReqFcb->CommonFCBHeader.FileSize.QuadPart;
1711 if(OldFileSize < PtrBuffer->EndOfFile.QuadPart) {
1712
1713 // Yes. Do the FSD specific stuff i.e. increase reserved
1714 // space on disk.
1715 /*
1716 if (FileObject->PrivateCacheMap)
1717 ZeroBlock = TRUE;
1718 */
1719
1720 // reference file to pretend that it is opened
1721 UDFReferenceFile__(Fcb->FileInfo);
1722 UDFInterlockedIncrement((PLONG)&(Fcb->ReferenceCount));
1723 UDFInterlockedIncrement((PLONG)&(NtReqFcb->CommonRefCount));
1724 // perform resize operation
1725 RC = UDFResizeFile__(Vcb, Fcb->FileInfo, PtrBuffer->EndOfFile.QuadPart);
1726 // dereference file
1727 UDFCloseFile__(Vcb, Fcb->FileInfo);
1728 UDFInterlockedDecrement((PLONG)&(Fcb->ReferenceCount));
1729 UDFInterlockedDecrement((PLONG)&(NtReqFcb->CommonRefCount));
1730 // update values in NtReqFcb
1731 NtReqFcb->CommonFCBHeader.FileSize.QuadPart =
1732 // NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart =
1733 PtrBuffer->EndOfFile.QuadPart;
1734 ModifiedAllocSize = TRUE;
1735
1736 } else if(NtReqFcb->CommonFCBHeader.FileSize.QuadPart >
1737 PtrBuffer->EndOfFile.QuadPart) {
1738
1739 // This is the painful part. See if the VMM will allow us to proceed.
1740 // The VMM will deny the request if:
1741 // (a) any image section exists OR
1742 // (b) a data section exists and the size of the user mapped view
1743 // is greater than the new size
1744 // Otherwise, the VMM should allow the request to proceed.
1745
1746 MmPrint((" MmCanFileBeTruncated()\n"));
1747 if(!MmCanFileBeTruncated(&(NtReqFcb->SectionObject), &(PtrBuffer->EndOfFile))) {
1748 // VMM said no way!
1749 try_return(RC = STATUS_USER_MAPPED_FILE);
1750 }
1751
1752 // Perform directory entry modifications. Release any on-disk
1753 // space we may need to in the process.
1754 UDFReferenceFile__(Fcb->FileInfo);
1755 UDFInterlockedIncrement((PLONG)&(Fcb->ReferenceCount));
1756 UDFInterlockedIncrement((PLONG)&(NtReqFcb->CommonRefCount));
1757 // perform resize operation
1758 RC = UDFResizeFile__(Vcb, Fcb->FileInfo, PtrBuffer->EndOfFile.QuadPart);
1759 // dereference file
1760 UDFCloseFile__(Vcb, Fcb->FileInfo);
1761 UDFInterlockedDecrement((PLONG)&(Fcb->ReferenceCount));
1762 UDFInterlockedDecrement((PLONG)&(NtReqFcb->CommonRefCount));
1763
1764 ModifiedAllocSize = TRUE;
1765 TruncatedFile = TRUE;
1766 }
1767
1768 // This is a good place to check if we have performed a truncate
1769 // operation. If we have perform a truncate (whether we extended
1770 // or reduced file size), we should update file time stamps.
1771
1772 // Last, but not the least, we must inform the Cache Manager of file size changes.
1773 if(ModifiedAllocSize && NT_SUCCESS(RC)) {
1774 // If we decreased the allocation size to less than the
1775 // current file size, modify the file size value.
1776 // Similarly, if we decreased the value to less than the
1777 // current valid data length, modify that value as well.
1778 if(TruncatedFile) {
1779 if(NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart >
1780 PtrBuffer->EndOfFile.QuadPart) {
1781 // Decrease the valid data length value.
1782 NtReqFcb->CommonFCBHeader.ValidDataLength =
1783 PtrBuffer->EndOfFile;
1784 }
1785 if(NtReqFcb->CommonFCBHeader.FileSize.QuadPart >
1786 PtrBuffer->EndOfFile.QuadPart) {
1787 // Decrease the file size value.
1788 NtReqFcb->CommonFCBHeader.FileSize =
1789 PtrBuffer->EndOfFile;
1790 }
1791 UDFSetFileSizeInDirNdx(Vcb, Fcb->FileInfo, NULL);
1792 } else {
1793 // Update the FCB Header with the new allocation size.
1794 // NT expects AllocationSize to be decreased on Close only
1795 NtReqFcb->CommonFCBHeader.AllocationSize.QuadPart =
1796 PtrBuffer->EndOfFile.QuadPart;
1797 // UDFSysGetAllocSize(Vcb, UDFGetFileSize(Fcb->FileInfo));
1798 UDFSetFileSizeInDirNdx(Vcb, Fcb->FileInfo, &(PtrBuffer->EndOfFile.QuadPart));
1799 }
1800
1801 FileObject->Flags |= FO_FILE_MODIFIED;
1802 // UDFGetFileAllocationSize(Vcb, Fcb->FileInfo);
1803
1804 // If the FCB has not had caching initiated, it is still valid
1805 // for us to invoke the NT Cache Manager. It is possible in such
1806 // situations for the call to be no'oped (unless some user has
1807 // mapped in the file)
1808
1809 // Archive bit
1810 if(Vcb->CompatFlags & UDF_VCB_IC_UPDATE_ARCH_BIT) {
1811 DirNdx = UDFDirIndex(UDFGetDirIndexByFileInfo(Fcb->FileInfo), Fcb->FileInfo->Index);
1812 Ccb->CCBFlags &= ~UDF_CCB_ATTRIBUTES_SET;
1813 Attr = UDFAttributesToNT(DirNdx, Fcb->FileInfo->Dloc->FileEntry);
1814 if(!(Attr & FILE_ATTRIBUTE_ARCHIVE))
1815 UDFAttributesToUDF(DirNdx, Fcb->FileInfo->Dloc->FileEntry, Attr | FILE_ATTRIBUTE_ARCHIVE);
1816 }
1817
1818 // NOTE: The invocation to CcSetFileSizes() will quite possibly
1819 // result in a recursive call back into the file system.
1820 // This is because the NT Cache Manager will typically
1821 // perform a flush before telling the VMM to purge pages
1822 // especially when caching has not been initiated on the
1823 // file stream, but the user has mapped the file into
1824 // the process' virtual address space.
1825 MmPrint((" CcSetFileSizes(), thrd:%8.8x\n",PsGetCurrentThread()));
1826 Fcb->NTRequiredFCB->AcqFlushCount++;
1827 CcSetFileSizes(FileObject, (PCC_FILE_SIZES)&(NtReqFcb->CommonFCBHeader.AllocationSize));
1828 Fcb->NTRequiredFCB->AcqFlushCount--;
1829 /* if(ZeroBlock) {
1830 UDFZeroDataEx(NtReqFcb,
1831 OldFileSize,
1832 PtrBuffer->EndOfFile.QuadPart - OldFileSize,
1833 TRUE // CanWait, Vcb, FileObject);
1834 }*/
1835 Fcb->NTRequiredFCB->NtReqFCBFlags |= UDF_NTREQ_FCB_MODIFIED;
1836
1837 notify_size_changes:
1838 if(AcquiredPagingIo) {
1839 UDFReleaseResource(&(Fcb->NTRequiredFCB->PagingIoResource));
1840 AcquiredPagingIo = FALSE;
1841 }
1842
1843 // Inform any pending IRPs (notify change directory).
1844 if(UDFIsAStream(Fcb->FileInfo)) {
1845 UDFNotifyFullReportChange( Vcb, Fcb->FileInfo,
1846 FILE_NOTIFY_CHANGE_STREAM_SIZE,
1847 FILE_ACTION_MODIFIED_STREAM);
1848 } else {
1849 UDFNotifyFullReportChange( Vcb, Fcb->FileInfo,
1850 FILE_NOTIFY_CHANGE_SIZE,
1851 FILE_ACTION_MODIFIED);
1852 }
1853 }
1854
1855 try_exit: NOTHING;
1856
1857 } _SEH2_FINALLY {
1858 if(AcquiredPagingIo) {
1859 UDFReleaseResource(&(Fcb->NTRequiredFCB->PagingIoResource));
1860 AcquiredPagingIo = FALSE;
1861 }
1862 if (CacheMapInitialized) {
1863
1864 MmPrint((" CcUninitializeCacheMap()\n"));
1865 CcUninitializeCacheMap( FileObject, NULL, NULL );
1866 }
1867 } _SEH2_END;
1868 return(RC);
1869 } // end UDFSetEOF()
1870
1871 NTSTATUS
1872 UDFPrepareForRenameMoveLink(
1873 PVCB Vcb,
1874 PBOOLEAN AcquiredVcb,
1875 PBOOLEAN AcquiredVcbEx,
1876 PBOOLEAN SingleDir,
1877 PBOOLEAN AcquiredDir1,
1878 PBOOLEAN AcquiredFcb1,
1879 IN PtrUDFCCB Ccb1,
1880 PUDF_FILE_INFO File1,
1881 PUDF_FILE_INFO Dir1,
1882 PUDF_FILE_INFO Dir2,
1883 BOOLEAN HardLink
1884 )
1885 {
1886 // convert acquisition to Exclusive
1887 // this will prevent us from the following situation:
1888 // There is a pair of objects among input dirs &
1889 // one of them is a parent of another. Sequential resource
1890 // acquisition may lead to deadlock due to concurrent
1891 // CleanUpFcbChain() or UDFCloseFileInfoChain()
1892 UDFInterlockedIncrement((PLONG)&(Vcb->VCBOpenCount));
1893 UDFReleaseResource(&(Vcb->VCBResource));
1894 (*AcquiredVcb) = FALSE;
1895
1896 // At first, make system to issue last Close request
1897 // for our Source & Target ...
1898 // we needn't flush/purge for Source on HLink
1899 UDFRemoveFromSystemDelayedQueue(Dir2->Fcb);
1900 if(!HardLink && (Dir2 != Dir1))
1901 UDFRemoveFromSystemDelayedQueue(File1->Fcb);
1902
1903 #ifdef UDF_DELAYED_CLOSE
1904 _SEH2_TRY {
1905 // Do actual close for all "delayed close" calls
1906
1907 // ... and now remove the rest from our queue
1908 if(!HardLink) {
1909 UDFCloseAllDelayedInDir(Vcb, Dir1);
1910 if(Dir2 != Dir1)
1911 UDFCloseAllDelayedInDir(Vcb, Dir2);
1912 } else {
1913 UDFCloseAllDelayedInDir(Vcb, Dir2);
1914 }
1915
1916 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
1917 BrutePoint();
1918 UDFInterlockedDecrement((PLONG)&(Vcb->VCBOpenCount));
1919 return (STATUS_DRIVER_INTERNAL_ERROR);
1920 } _SEH2_END;
1921 #endif //UDF_DELAYED_CLOSE
1922
1923 (*SingleDir) = ((Dir1 == Dir2) && (Dir1->Fcb));
1924
1925 if(!(*SingleDir) ||
1926 (UDFGetFileLinkCount(File1) != 1)) {
1927 UDFAcquireResourceExclusive(&(Vcb->VCBResource), TRUE);
1928 (*AcquiredVcb) = TRUE;
1929 (*AcquiredVcbEx) = TRUE;
1930 UDFInterlockedDecrement((PLONG)&(Vcb->VCBOpenCount));
1931 } else {
1932 UDFAcquireResourceShared(&(Vcb->VCBResource), TRUE);
1933 (*AcquiredVcb) = TRUE;
1934 UDFInterlockedDecrement((PLONG)&(Vcb->VCBOpenCount));
1935
1936 UDF_CHECK_PAGING_IO_RESOURCE(Dir1->Fcb->NTRequiredFCB);
1937 UDFAcquireResourceExclusive(&(Dir1->Fcb->NTRequiredFCB->MainResource),TRUE);
1938 (*AcquiredDir1) = TRUE;
1939
1940 UDF_CHECK_PAGING_IO_RESOURCE(File1->Fcb->NTRequiredFCB);
1941 UDFAcquireResourceExclusive(&(File1->Fcb->NTRequiredFCB->MainResource),TRUE);
1942 (*AcquiredFcb1) = TRUE;
1943 }
1944 return STATUS_SUCCESS;
1945 } // end UDFPrepareForRenameMoveLink()
1946
1947 /*
1948 Rename or move file
1949 */
1950 NTSTATUS
1951 UDFRename(
1952 IN PIO_STACK_LOCATION PtrSp,
1953 IN PtrUDFFCB Fcb1,
1954 IN PtrUDFCCB Ccb1,
1955 IN PFILE_OBJECT FileObject1, // Source File
1956 IN PFILE_RENAME_INFORMATION PtrBuffer
1957 )
1958 {
1959 // Source Directory
1960 PFILE_OBJECT DirObject1 = FileObject1->RelatedFileObject;
1961 // Target Directory
1962 PFILE_OBJECT DirObject2 = PtrSp->Parameters.SetFile.FileObject;
1963 // Overwite Flag
1964 BOOLEAN Replace = PtrSp->Parameters.SetFile.ReplaceIfExists &&
1965 PtrBuffer->ReplaceIfExists;
1966 NTSTATUS RC;
1967 PVCB Vcb = Fcb1->Vcb;
1968 PtrUDFFCB Fcb2;
1969 BOOLEAN ic;
1970 BOOLEAN AcquiredVcb = TRUE;
1971 BOOLEAN AcquiredVcbEx = FALSE;
1972 BOOLEAN AcquiredDir1 = FALSE;
1973 BOOLEAN AcquiredFcb1 = FALSE;
1974 BOOLEAN SingleDir = TRUE;
1975 BOOLEAN UseClose;
1976
1977 PUDF_FILE_INFO File1;
1978 PUDF_FILE_INFO Dir1;
1979 PUDF_FILE_INFO Dir2;
1980 PUDF_FILE_INFO NextFileInfo, fi;
1981
1982 UNICODE_STRING NewName;
1983 UNICODE_STRING LocalPath;
1984 PtrUDFCCB CurCcb = NULL;
1985 PLIST_ENTRY Link;
1986 ULONG i;
1987 ULONG DirRefCount;
1988 ULONG FileInfoRefCount;
1989 ULONG Attr;
1990 PDIR_INDEX_ITEM DirNdx;
1991
1992 AdPrint(("UDFRename %8.8x\n", DirObject2));
1993
1994 LocalPath.Buffer = NULL;
1995
1996 _SEH2_TRY {
1997 // do we try to rename Volume ?
1998 #ifdef UDF_ALLOW_RENAME_MOVE
1999 if(!(File1 = Fcb1->FileInfo))
2000 #endif //UDF_ALLOW_RENAME_MOVE
2001 try_return (RC = STATUS_ACCESS_DENIED);
2002
2003 // do we try to rename RootDir ?
2004 if(!(Dir1 = File1->ParentFile))
2005 try_return (RC = STATUS_ACCESS_DENIED);
2006
2007 // do we try to rename to RootDir or Volume ?
2008 if(!DirObject2) {
2009 Dir2 = File1->ParentFile;
2010 DirObject2 = DirObject1;
2011 } else
2012 if(DirObject2->FsContext2 &&
2013 (Fcb2 = ((PtrUDFCCB)(DirObject2->FsContext2))->Fcb)) {
2014 Dir2 = ((PtrUDFCCB)(DirObject2->FsContext2))->Fcb->FileInfo;
2015 } else {
2016 try_return (RC = STATUS_INVALID_PARAMETER);
2017 }
2018 // invalid destination ?
2019 if(!Dir2) try_return (RC = STATUS_ACCESS_DENIED);
2020
2021 // Stream can't be a Dir or have StreamDir
2022 if(UDFIsAStreamDir(Dir2)) {
2023 #ifdef UDF_ENABLE_SECURITY
2024 if(UDFIsADirectory(File1)) {
2025 try_return (RC = STATUS_ACCESS_DENIED);
2026 }
2027 // We should check whether File1 has only Internal
2028 // (or Deleted) streams. In this case SDir should be
2029 // removed (in UDFRenameMoveFile__()). Otherwise
2030 // return STATUS_ACCESS_DENIED
2031 if(UDFHasAStreamDir(File1)) {
2032 KdPrint(("TODO: We should remove Streams from source file\n"));
2033 try_return (RC = STATUS_ACCESS_DENIED);
2034 }
2035 #else //UDF_ENABLE_SECURITY
2036 if(UDFIsADirectory(File1) ||
2037 UDFHasAStreamDir(File1)) {
2038 try_return (RC = STATUS_ACCESS_DENIED);
2039 }
2040 #endif //UDF_ENABLE_SECURITY
2041 }
2042
2043 RC = UDFPrepareForRenameMoveLink(Vcb, &AcquiredVcb, &AcquiredVcbEx,
2044 &SingleDir,
2045 &AcquiredDir1, &AcquiredFcb1,
2046 Ccb1, File1,
2047 Dir1, Dir2,
2048 FALSE); // it is Rename operation
2049 if(!NT_SUCCESS(RC))
2050 try_return(RC);
2051
2052 // check if the source file is in use
2053 if(Fcb1->OpenHandleCount > 1)
2054 try_return (RC = STATUS_ACCESS_DENIED);
2055 ASSERT(Fcb1->OpenHandleCount);
2056 ASSERT(!Fcb1->IrpContextLite);
2057 if(Fcb1->IrpContextLite) {
2058 try_return (RC = STATUS_ACCESS_DENIED);
2059 }
2060 // Check if we have parallel/pending Close threads
2061 if(Fcb1->CcbCount && !SingleDir) {
2062 // if this is the 1st attempt, we'll try to
2063 // synchronize with Close requests
2064 // otherwise fail request
2065 RC = STATUS_ACCESS_DENIED;
2066 post_rename:
2067 if(Fcb1->FCBFlags & UDF_FCB_POSTED_RENAME) {
2068 Fcb1->FCBFlags &= ~UDF_FCB_POSTED_RENAME;
2069 try_return (RC);
2070 }
2071 Fcb1->FCBFlags |= UDF_FCB_POSTED_RENAME;
2072 try_return (RC = STATUS_PENDING);
2073 }
2074
2075 if(!DirObject2) {
2076 // Make sure the name is of legal length.
2077 if(PtrBuffer->FileNameLength > UDF_NAME_LEN*sizeof(WCHAR)) {
2078 try_return(RC = STATUS_OBJECT_NAME_INVALID);
2079 }
2080 NewName.Length = NewName.MaximumLength = (USHORT)(PtrBuffer->FileNameLength);
2081 NewName.Buffer = (PWCHAR)&(PtrBuffer->FileName);
2082 } else {
2083 // This name is by definition legal.
2084 NewName = *((PUNICODE_STRING)&DirObject2->FileName);
2085 }
2086
2087 ic = (Ccb1->CCBFlags & UDF_CCB_CASE_SENSETIVE) ? FALSE : TRUE;
2088
2089 AdPrint((" %ws ->\n %ws\n",
2090 Fcb1->FCBName->ObjectName.Buffer,
2091 NewName.Buffer));
2092
2093 if(UDFIsDirOpened__(File1)) {
2094 // We can't rename file because of unclean references.
2095 // UDF_INFO package can safely do it, but NT side cannot.
2096 // In this case NT requires STATUS_OBJECT_NAME_COLLISION
2097 // rather than STATUS_ACCESS_DENIED
2098 if(NT_SUCCESS(UDFFindFile__(Vcb, ic, &NewName, Dir2)))
2099 try_return(RC = STATUS_OBJECT_NAME_COLLISION);
2100 try_return (RC = STATUS_ACCESS_DENIED);
2101 } else {
2102 // Last check before Moving.
2103 // We can't move across Dir referenced (even internally) file
2104 if(!SingleDir) {
2105 RC = UDFDoesOSAllowFileToBeMoved__(File1);
2106 if(!NT_SUCCESS(RC)) {
2107 // try_return(RC);
2108 goto post_rename;
2109 }
2110 }
2111
2112 ASSERT_REF(Fcb1->ReferenceCount >= File1->RefCount);
2113 ASSERT_REF(Dir1->Fcb->ReferenceCount >= Dir1->RefCount);
2114 ASSERT_REF(Dir2->Fcb->ReferenceCount >= Dir2->RefCount);
2115
2116 RC = UDFRenameMoveFile__(Vcb, ic, &Replace, &NewName, Dir1, Dir2, File1);
2117 }
2118 if(!NT_SUCCESS(RC))
2119 try_return (RC);
2120
2121 ASSERT(UDFDirIndex(File1->ParentFile->Dloc->DirIndex, File1->Index)->FileInfo == File1);
2122
2123 RC = MyCloneUnicodeString(&LocalPath, (Dir2->Fcb->FCBFlags & UDF_FCB_ROOT_DIRECTORY) ?
2124 &UDFGlobalData.UnicodeStrRoot :
2125 &(Dir2->Fcb->FCBName->ObjectName) );
2126 if(!NT_SUCCESS(RC)) try_return (RC);
2127 // RC = MyAppendUnicodeStringToString(&LocalPath, (Dir2->Fcb->FCBFlags & UDF_FCB_ROOT_DIRECTORY) ? &(UDFGlobalData.UnicodeStrRoot) : &(Dir2->Fcb->FCBName->ObjectName));
2128 // if(!NT_SUCCESS(RC)) try_return (RC);
2129 if(Dir2->ParentFile) {
2130 RC = MyAppendUnicodeToString(&LocalPath, L"\\");
2131 if(!NT_SUCCESS(RC)) try_return (RC);
2132 }
2133 RC = MyAppendUnicodeStringToStringTag(&LocalPath, &NewName, MEM_USREN_TAG);
2134 if(!NT_SUCCESS(RC)) try_return (RC);
2135
2136 // Set Archive bit
2137 DirNdx = UDFDirIndex(File1->ParentFile->Dloc->DirIndex, File1->Index);
2138 if(Vcb->CompatFlags & UDF_VCB_IC_UPDATE_ARCH_BIT) {
2139 Attr = UDFAttributesToNT(DirNdx, File1->Dloc->FileEntry);
2140 if(!(Attr & FILE_ATTRIBUTE_ARCHIVE))
2141 UDFAttributesToUDF(DirNdx, File1->Dloc->FileEntry, Attr | FILE_ATTRIBUTE_ARCHIVE);
2142 }
2143 // Update Parent Objects (mark 'em as modified)
2144 if(Vcb->CompatFlags & UDF_VCB_IC_UPDATE_DIR_WRITE) {
2145 if(DirObject1)
2146 DirObject1->Flags |= FO_FILE_MODIFIED;
2147 if(DirObject2) {
2148 DirObject2->Flags |= FO_FILE_MODIFIED;
2149 if(!Replace)
2150 DirObject2->Flags |= FO_FILE_SIZE_CHANGED;
2151 }
2152 }
2153 // report changes
2154 if(SingleDir && !Replace) {
2155 UDFNotifyFullReportChange( Vcb, File1,
2156 UDFIsADirectory(File1) ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME,
2157 FILE_ACTION_RENAMED_OLD_NAME);
2158 /* UDFNotifyFullReportChange( Vcb, File2,
2159 UDFIsADirectory(File2) ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME,
2160 FILE_ACTION_RENAMED_NEW_NAME );*/
2161 FsRtlNotifyFullReportChange( Vcb->NotifyIRPMutex, &(Vcb->NextNotifyIRP),
2162 (PSTRING)&LocalPath,
2163 ((Dir2->Fcb->FCBFlags & UDF_FCB_ROOT_DIRECTORY) ? 0 : Dir2->Fcb->FCBName->ObjectName.Length) + sizeof(WCHAR),
2164 NULL,NULL,
2165 UDFIsADirectory(File1) ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME,
2166 FILE_ACTION_RENAMED_NEW_NAME,
2167 NULL);
2168 } else {
2169 UDFNotifyFullReportChange( Vcb, File1,
2170 UDFIsADirectory(File1) ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME,
2171 FILE_ACTION_REMOVED);
2172 if(Replace) {
2173 /* UDFNotifyFullReportChange( Vcb, File2,
2174 FILE_NOTIFY_CHANGE_ATTRIBUTES |
2175 FILE_NOTIFY_CHANGE_SIZE |
2176 FILE_NOTIFY_CHANGE_LAST_WRITE |
2177 FILE_NOTIFY_CHANGE_LAST_ACCESS |
2178 FILE_NOTIFY_CHANGE_CREATION |
2179 FILE_NOTIFY_CHANGE_EA,
2180 FILE_ACTION_MODIFIED );*/
2181 FsRtlNotifyFullReportChange( Vcb->NotifyIRPMutex, &(Vcb->NextNotifyIRP),
2182 (PSTRING)&LocalPath,
2183 ((Dir2->Fcb->FCBFlags & UDF_FCB_ROOT_DIRECTORY) ?
2184 0 : Dir2->Fcb->FCBName->ObjectName.Length) + sizeof(WCHAR),
2185 NULL,NULL,
2186 FILE_NOTIFY_CHANGE_ATTRIBUTES |
2187 FILE_NOTIFY_CHANGE_SIZE |
2188 FILE_NOTIFY_CHANGE_LAST_WRITE |
2189 FILE_NOTIFY_CHANGE_LAST_ACCESS |
2190 FILE_NOTIFY_CHANGE_CREATION |
2191 FILE_NOTIFY_CHANGE_EA,
2192 FILE_ACTION_MODIFIED,
2193 NULL);
2194 } else {
2195 /* UDFNotifyFullReportChange( Vcb, File2,
2196 UDFIsADirectory(File2) ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME,
2197 FILE_ACTION_ADDED );*/
2198 FsRtlNotifyFullReportChange( Vcb->NotifyIRPMutex, &(Vcb->NextNotifyIRP),
2199 (PSTRING)&LocalPath,
2200 ((Dir2->Fcb->FCBFlags & UDF_FCB_ROOT_DIRECTORY) ?
2201 0 : Dir2->Fcb->FCBName->ObjectName.Length) + sizeof(WCHAR),
2202 NULL,NULL,
2203 UDFIsADirectory(File1) ?
2204 FILE_NOTIFY_CHANGE_DIR_NAME :
2205 FILE_NOTIFY_CHANGE_FILE_NAME,
2206 FILE_ACTION_ADDED,
2207 NULL);
2208 }
2209 }
2210
2211 // this will prevent structutre release before call to
2212 // UDFCleanUpFcbChain()
2213 UDFInterlockedIncrement((PLONG)&(Dir1->Fcb->ReferenceCount));
2214 UDFInterlockedIncrement((PLONG)&(Dir1->Fcb->NTRequiredFCB->CommonRefCount));
2215 ASSERT_REF(Dir1->Fcb->ReferenceCount >= Dir1->RefCount);
2216
2217 // Look through Ccb list & decrement OpenHandleCounter(s)
2218 // acquire CcbList
2219 if(!SingleDir) {
2220 UDFAcquireResourceExclusive(&(Fcb1->CcbListResource),TRUE);
2221 Link = Fcb1->NextCCB.Flink;
2222 DirRefCount = 0;
2223 FileInfoRefCount = 0;
2224 ASSERT(Link != &(Fcb1->NextCCB));
2225 while (Link != &(Fcb1->NextCCB)) {
2226 NextFileInfo = Dir1;
2227 CurCcb = CONTAINING_RECORD(Link, UDFCCB, NextCCB);
2228 ASSERT(CurCcb->TreeLength);
2229 i = (CurCcb->TreeLength) ? (CurCcb->TreeLength - 1) : 0;
2230 Link = Link->Flink;
2231 UseClose = (CurCcb->CCBFlags & UDF_CCB_CLEANED) ? FALSE : TRUE;
2232
2233 AdPrint((" Ccb:%x:%s:i:%x\n", CurCcb, UseClose ? "Close" : "",i));
2234 // cleanup old parent chain
2235 for(; i && NextFileInfo; i--) {
2236 // remember parent file now
2237 // it will prevent us from data losses
2238 // due to eventual structure release
2239 fi = NextFileInfo->ParentFile;
2240 if(UseClose) {
2241 ASSERT_REF(NextFileInfo->Fcb->ReferenceCount >= NextFileInfo->RefCount);
2242 UDFCloseFile__(Vcb, NextFileInfo);
2243 }
2244 ASSERT_REF(NextFileInfo->Fcb->ReferenceCount > NextFileInfo->RefCount);
2245 ASSERT_REF(NextFileInfo->Fcb->ReferenceCount);
2246 ASSERT_REF(NextFileInfo->Fcb->NTRequiredFCB->CommonRefCount);
2247 UDFInterlockedDecrement((PLONG)&(NextFileInfo->Fcb->ReferenceCount));
2248 UDFInterlockedDecrement((PLONG)&(NextFileInfo->Fcb->NTRequiredFCB->CommonRefCount));
2249 ASSERT_REF(NextFileInfo->Fcb->ReferenceCount >= NextFileInfo->RefCount);
2250 NextFileInfo = fi;
2251 }
2252
2253 if(CurCcb->TreeLength > 1) {
2254 DirRefCount++;
2255 if(UseClose)
2256 FileInfoRefCount++;
2257 CurCcb->TreeLength = 2;
2258 #ifdef UDF_DBG
2259 } else {
2260 BrutePoint();
2261 #endif // UDF_DBG
2262 }
2263 }
2264 UDFReleaseResource(&(Fcb1->CcbListResource));
2265
2266 ASSERT_REF(DirRefCount >= FileInfoRefCount);
2267 // update counters & pointers
2268 Fcb1->ParentFcb = Dir2->Fcb;
2269 // move references to Dir2
2270 UDFInterlockedExchangeAdd((PLONG)&(Dir2->Fcb->ReferenceCount), DirRefCount);
2271 UDFInterlockedExchangeAdd((PLONG)&(Dir2->Fcb->NTRequiredFCB->CommonRefCount), DirRefCount);
2272 ASSERT_REF(Dir2->Fcb->ReferenceCount > Dir2->RefCount);
2273 UDFReferenceFileEx__(Dir2,FileInfoRefCount);
2274 ASSERT_REF(Dir2->Fcb->ReferenceCount >= Dir2->RefCount);
2275 }
2276 ASSERT_REF(Dir2->Fcb->ReferenceCount >= Dir2->RefCount);
2277 ASSERT_REF(Dir2->RefCount);
2278
2279 ASSERT_REF(Dir1->Fcb->ReferenceCount >= Dir1->RefCount);
2280 // Modify name in Fcb1
2281 if(Fcb1->FCBName) {
2282 if(Fcb1->FCBName->ObjectName.Buffer) {
2283 MyFreePool__(Fcb1->FCBName->ObjectName.Buffer);
2284 }
2285 UDFReleaseObjectName(Fcb1->FCBName);
2286 }
2287 Fcb1->FCBName = UDFAllocateObjectName();
2288 if(!(Fcb1->FCBName)) {
2289 insuf_res:
2290 BrutePoint();
2291 // UDFCleanUpFcbChain()...
2292 if(AcquiredFcb1) {
2293 UDF_CHECK_PAGING_IO_RESOURCE(Fcb1->NTRequiredFCB);
2294 UDFReleaseResource(&(Fcb1->NTRequiredFCB->MainResource));
2295 AcquiredDir1 = FALSE;
2296 }
2297 if(AcquiredDir1) {
2298 UDF_CHECK_PAGING_IO_RESOURCE(Dir1->Fcb->NTRequiredFCB);
2299 UDFReleaseResource(&(Dir1->Fcb->NTRequiredFCB->MainResource));
2300 AcquiredDir1 = FALSE;
2301 }
2302 UDFCleanUpFcbChain(Vcb, Dir1, 1, TRUE);
2303 try_return(RC = STATUS_INSUFFICIENT_RESOURCES);
2304 }
2305
2306 RC = MyCloneUnicodeString(&(Fcb1->FCBName->ObjectName), &(Fcb2->FCBName->ObjectName));
2307 if(!NT_SUCCESS(RC))
2308 goto insuf_res;
2309 /* RC = MyAppendUnicodeStringToString(&(Fcb1->FCBName->ObjectName), &(Fcb2->FCBName->ObjectName));
2310 if(!NT_SUCCESS(RC))
2311 goto insuf_res;*/
2312 // if Dir2 is a RootDir, we shoud not append '\\' because
2313 // uit will be the 2nd '\\' character (RootDir's name is also '\\')
2314 if(Dir2->ParentFile) {
2315 RC = MyAppendUnicodeToString(&(Fcb1->FCBName->ObjectName), L"\\");
2316 if(!NT_SUCCESS(RC))
2317 goto insuf_res;
2318 }
2319 RC = MyAppendUnicodeStringToStringTag(&(Fcb1->FCBName->ObjectName), &NewName, MEM_USREN2_TAG);
2320 if(!NT_SUCCESS(RC))
2321 goto insuf_res;
2322
2323 ASSERT_REF(Fcb1->ReferenceCount >= File1->RefCount);
2324 ASSERT_REF(Dir1->Fcb->ReferenceCount >= Dir1->RefCount);
2325 ASSERT_REF(Dir2->Fcb->ReferenceCount >= Dir2->RefCount);
2326
2327 RC = STATUS_SUCCESS;
2328
2329 try_exit: NOTHING;
2330
2331 } _SEH2_FINALLY {
2332
2333 if(AcquiredFcb1) {
2334 UDF_CHECK_PAGING_IO_RESOURCE(Fcb1->NTRequiredFCB);
2335 UDFReleaseResource(&(Fcb1->NTRequiredFCB->MainResource));
2336 }
2337 if(AcquiredDir1) {
2338 UDF_CHECK_PAGING_IO_RESOURCE(Dir1->Fcb->NTRequiredFCB);
2339 UDFReleaseResource(&(Dir1->Fcb->NTRequiredFCB->MainResource));
2340 }
2341 // perform protected structure release
2342 if(NT_SUCCESS(RC) &&
2343 (RC != STATUS_PENDING)) {
2344 ASSERT(AcquiredVcb);
2345 UDFCleanUpFcbChain(Vcb, Dir1, 1, TRUE);
2346 ASSERT_REF(Fcb1->ReferenceCount >= File1->RefCount);
2347 ASSERT_REF(Dir2->Fcb->ReferenceCount >= Dir2->RefCount);
2348 }
2349
2350 if(AcquiredVcb) {
2351 if(AcquiredVcbEx)
2352 UDFConvertExclusiveToSharedLite(&(Vcb->VCBResource));
2353 } else {
2354 // caller assumes Vcb to be acquired shared
2355 BrutePoint();
2356 UDFAcquireResourceShared(&(Vcb->VCBResource), TRUE);
2357 }
2358
2359 if(LocalPath.Buffer) {
2360 MyFreePool__(LocalPath.Buffer);
2361 }
2362 } _SEH2_END;
2363
2364 return RC;
2365 } // end UDFRename()
2366
2367 #endif //UDF_READ_ONLY_BUILD
2368
2369 LONG
2370 UDFFindFileId(
2371 IN PVCB Vcb,
2372 IN LONGLONG Id
2373 )
2374 {
2375 if(!Vcb->FileIdCache) return (-1);
2376 for(ULONG i=0; i<Vcb->FileIdCount; i++) {
2377 if(Vcb->FileIdCache[i].Id == Id) return i;
2378 }
2379 return (-1);
2380 } // end UDFFindFileId()
2381
2382 LONG
2383 UDFFindFreeFileId(
2384 IN PVCB Vcb,
2385 IN LONGLONG Id
2386 )
2387 {
2388 if(!Vcb->FileIdCache) {
2389 if(!(Vcb->FileIdCache = (PUDFFileIDCacheItem)MyAllocatePool__(NonPagedPool, sizeof(UDFFileIDCacheItem)*FILE_ID_CACHE_GRANULARITY)))
2390 return (-1);
2391 RtlZeroMemory(Vcb->FileIdCache, FILE_ID_CACHE_GRANULARITY*sizeof(UDFFileIDCacheItem));
2392 Vcb->FileIdCount = FILE_ID_CACHE_GRANULARITY;
2393 }
2394 for(ULONG i=0; i<Vcb->FileIdCount; i++) {
2395 if(!Vcb->FileIdCache[i].FullName.Buffer) return i;
2396 }
2397 if(!MyReallocPool__((PCHAR)(Vcb->FileIdCache), Vcb->FileIdCount*sizeof(UDFFileIDCacheItem),
2398 (PCHAR*)&(Vcb->FileIdCache), (Vcb->FileIdCount+FILE_ID_CACHE_GRANULARITY)*sizeof(UDFFileIDCacheItem))) {
2399 return (-1);
2400 }
2401 RtlZeroMemory(&(Vcb->FileIdCache[Vcb->FileIdCount]), FILE_ID_CACHE_GRANULARITY*sizeof(UDFFileIDCacheItem));
2402 Vcb->FileIdCount += FILE_ID_CACHE_GRANULARITY;
2403 return (Vcb->FileIdCount - FILE_ID_CACHE_GRANULARITY);
2404 } // end UDFFindFreeFileId()
2405
2406 NTSTATUS
2407 UDFStoreFileId(
2408 IN PVCB Vcb,
2409 IN PtrUDFCCB Ccb,
2410 IN PUDF_FILE_INFO fi,
2411 IN LONGLONG Id
2412 )
2413 {
2414 LONG i;
2415 NTSTATUS RC = STATUS_SUCCESS;
2416
2417 if((i = UDFFindFileId(Vcb, Id)) == (-1)) {
2418 if((i = UDFFindFreeFileId(Vcb, Id)) == (-1)) return STATUS_INSUFFICIENT_RESOURCES;
2419 } else {
2420 return STATUS_SUCCESS;
2421 }
2422 Vcb->FileIdCache[i].Id = Id;
2423 Vcb->FileIdCache[i].CaseSens = (Ccb->CCBFlags & UDF_CCB_CASE_SENSETIVE) ? TRUE : FALSE;
2424 RC = MyCloneUnicodeString(&(Vcb->FileIdCache[i].FullName), &(Ccb->Fcb->FCBName->ObjectName));
2425 /* if(NT_SUCCESS(RC)) {
2426 RC = MyAppendUnicodeStringToStringTag(&(Vcb->FileIdCache[i].FullName), &(Ccb->Fcb->FCBName->ObjectName), MEM_USFIDC_TAG);
2427 }*/
2428 return RC;
2429 } // end UDFStoreFileId()
2430
2431 NTSTATUS
2432 UDFRemoveFileId(
2433 IN PVCB Vcb,
2434 IN LONGLONG Id
2435 )
2436 {
2437 LONG i;
2438
2439 if((i = UDFFindFileId(Vcb, Id)) == (-1)) return STATUS_INVALID_PARAMETER;
2440 MyFreePool__(Vcb->FileIdCache[i].FullName.Buffer);
2441 RtlZeroMemory(&(Vcb->FileIdCache[i]), sizeof(UDFFileIDCacheItem));
2442 return STATUS_SUCCESS;
2443 } // end UDFRemoveFileId()
2444
2445 VOID
2446 UDFReleaseFileIdCache(
2447 IN PVCB Vcb
2448 )
2449 {
2450 if(!Vcb->FileIdCache) return;
2451 for(ULONG i=0; i<Vcb->FileIdCount; i++) {
2452 if(Vcb->FileIdCache[i].FullName.Buffer) {
2453 MyFreePool__(Vcb->FileIdCache[i].FullName.Buffer);
2454 }
2455 }
2456 MyFreePool__(Vcb->FileIdCache);
2457 Vcb->FileIdCache = NULL;
2458 Vcb->FileIdCount = 0;
2459 } // end UDFReleaseFileIdCache()
2460
2461 NTSTATUS
2462 UDFGetOpenParamsByFileId(
2463 IN PVCB Vcb,
2464 IN LONGLONG Id,
2465 OUT PUNICODE_STRING* FName,
2466 OUT BOOLEAN* CaseSens
2467 )
2468 {
2469 LONG i;
2470
2471 if((i = UDFFindFileId(Vcb, Id)) == (-1)) return STATUS_NOT_FOUND;
2472 (*FName) = &(Vcb->FileIdCache[i].FullName);
2473 (*CaseSens) = !(Vcb->FileIdCache[i].CaseSens);
2474 return STATUS_SUCCESS;
2475 } // end UDFGetOpenParamsByFileId()
2476
2477 #ifndef UDF_READ_ONLY_BUILD
2478
2479 #ifdef UDF_ALLOW_HARD_LINKS
2480 /*
2481 create hard link for the file
2482 */
2483 NTSTATUS
2484 UDFHardLink(
2485 IN PIO_STACK_LOCATION PtrSp,
2486 IN PtrUDFFCB Fcb1,
2487 IN PtrUDFCCB Ccb1,
2488 IN PFILE_OBJECT FileObject1, // Source File
2489 IN PFILE_LINK_INFORMATION PtrBuffer
2490 )
2491 {
2492 // Target Directory
2493 PFILE_OBJECT DirObject2 = PtrSp->Parameters.SetFile.FileObject;
2494 // Overwite Flag
2495 BOOLEAN Replace = PtrSp->Parameters.SetFile.ReplaceIfExists &&
2496 PtrBuffer->ReplaceIfExists;
2497 NTSTATUS RC;
2498 PVCB Vcb = Fcb1->Vcb;
2499 PtrUDFFCB Fcb2;
2500 BOOLEAN ic;
2501 BOOLEAN AcquiredVcb = TRUE;
2502 BOOLEAN AcquiredVcbEx = FALSE;
2503 BOOLEAN AcquiredDir1 = FALSE;
2504 BOOLEAN AcquiredFcb1 = FALSE;
2505 BOOLEAN SingleDir = TRUE;
2506
2507 PUDF_FILE_INFO File1;
2508 PUDF_FILE_INFO Dir1 = NULL;
2509 PUDF_FILE_INFO Dir2;
2510
2511 UNICODE_STRING NewName;
2512 UNICODE_STRING LocalPath;
2513 // PtrUDFCCB CurCcb = NULL;
2514
2515 AdPrint(("UDFHardLink\n"));
2516
2517 LocalPath.Buffer = NULL;
2518
2519 _SEH2_TRY {
2520
2521 // do we try to link Volume ?
2522 if(!(File1 = Fcb1->FileInfo))
2523 try_return (RC = STATUS_ACCESS_DENIED);
2524
2525 // do we try to link RootDir ?
2526 if(!(Dir1 = File1->ParentFile))
2527 try_return (RC = STATUS_ACCESS_DENIED);
2528
2529 // do we try to link Stream / Stream Dir ?
2530 #ifdef UDF_ALLOW_LINKS_TO_STREAMS
2531 if(UDFIsAStreamDir(File1))
2532 try_return (RC = STATUS_ACCESS_DENIED);
2533 #else //UDF_ALLOW_LINKS_TO_STREAMS
2534 if(UDFIsAStream(File1) || UDFIsAStreamDir(File1) /*||
2535 UDFIsADirectory(File1) || UDFHasAStreamDir(File1)*/)
2536 try_return (RC = STATUS_ACCESS_DENIED);
2537 #endif // UDF_ALLOW_LINKS_TO_STREAMS
2538
2539 // do we try to link to RootDir or Volume ?
2540 if(!DirObject2) {
2541 Dir2 = File1->ParentFile;
2542 DirObject2 = FileObject1->RelatedFileObject;
2543 } else
2544 if(DirObject2->FsContext2 &&
2545 (Fcb2 = ((PtrUDFCCB)(DirObject2->FsContext2))->Fcb)) {
2546 Dir2 = ((PtrUDFCCB)(DirObject2->FsContext2))->Fcb->FileInfo;
2547 } else {
2548 try_return (RC = STATUS_INVALID_PARAMETER);
2549 }
2550
2551 // check target dir
2552 if(!Dir2) try_return (RC = STATUS_ACCESS_DENIED);
2553
2554 // Stream can't be a Dir or have Streams
2555 if(UDFIsAStreamDir(Dir2)) {
2556 try_return (RC = STATUS_ACCESS_DENIED);
2557 /* if(UDFIsADirectory(File1) ||
2558 UDFHasAStreamDir(File1)) {
2559 BrutePoint();
2560 try_return (RC = STATUS_ACCESS_DENIED);
2561 }*/
2562 }
2563
2564 /* if(UDFIsAStreamDir(Dir2))
2565 try_return (RC = STATUS_ACCESS_DENIED);*/
2566
2567 RC = UDFPrepareForRenameMoveLink(Vcb, &AcquiredVcb, &AcquiredVcbEx,
2568 &SingleDir,
2569 &AcquiredDir1, &AcquiredFcb1,
2570 Ccb1, File1,
2571 Dir1, Dir2,
2572 TRUE); // it is HLink operation
2573 if(!NT_SUCCESS(RC))
2574 try_return(RC);
2575
2576 // check if the source file is used
2577 if(!DirObject2) {
2578 // Make sure the name is of legal length.
2579 if(PtrBuffer->FileNameLength > UDF_NAME_LEN*sizeof(WCHAR)) {
2580 try_return(RC = STATUS_OBJECT_NAME_INVALID);
2581 }
2582 NewName.Length = NewName.MaximumLength = (USHORT)(PtrBuffer->FileNameLength);
2583 NewName.Buffer = (PWCHAR)&(PtrBuffer->FileName);
2584 } else {
2585 // This name is by definition legal.
2586 NewName = *((PUNICODE_STRING)&DirObject2->FileName);
2587 }
2588
2589 ic = (Ccb1->CCBFlags & UDF_CCB_CASE_SENSETIVE) ? FALSE : TRUE;
2590
2591 AdPrint((" %ws ->\n %ws\n",
2592 Fcb1->FCBName->ObjectName.Buffer,
2593 NewName.Buffer));
2594
2595 RC = UDFHardLinkFile__(Vcb, ic, &Replace, &NewName, Dir1, Dir2, File1);
2596 if(!NT_SUCCESS(RC)) try_return (RC);
2597
2598 // Update Parent Objects (mark 'em as modified)
2599 if(Vcb->CompatFlags & UDF_VCB_IC_UPDATE_DIR_WRITE) {
2600 if(DirObject2) {
2601 DirObject2->Flags |= FO_FILE_MODIFIED;
2602 if(!Replace)
2603 DirObject2->Flags |= FO_FILE_SIZE_CHANGED;
2604 }
2605 }
2606 // report changes
2607 UDFNotifyFullReportChange( Vcb, File1,
2608 FILE_NOTIFY_CHANGE_LAST_WRITE |
2609 FILE_NOTIFY_CHANGE_LAST_ACCESS,
2610 FILE_ACTION_MODIFIED );
2611
2612 RC = MyCloneUnicodeString(&LocalPath, (Dir2->Fcb->FCBFlags & UDF_FCB_ROOT_DIRECTORY) ?
2613 &UDFGlobalData.UnicodeStrRoot :
2614 &(Dir2->Fcb->FCBName->ObjectName));
2615 if(!NT_SUCCESS(RC)) try_return (RC);
2616 /* RC = MyAppendUnicodeStringToString(&LocalPath, (Dir2->Fcb->FCBFlags & UDF_FCB_ROOT_DIRECTORY) ? &(UDFGlobalData.UnicodeStrRoot) : &(Dir2->Fcb->FCBName->ObjectName));
2617 if(!NT_SUCCESS(RC)) try_return (RC);*/
2618 // if Dir2 is a RootDir, we shoud not append '\\' because
2619 // it will be the 2nd '\\' character (RootDir's name is also '\\')
2620 if(Dir2->ParentFile) {
2621 RC = MyAppendUnicodeToString(&LocalPath, L"\\");
2622 if(!NT_SUCCESS(RC)) try_return (RC);
2623 }
2624 RC = MyAppendUnicodeStringToStringTag(&LocalPath, &NewName, MEM_USHL_TAG);
2625 if(!NT_SUCCESS(RC)) try_return (RC);
2626
2627 if(!Replace) {
2628 /* UDFNotifyFullReportChange( Vcb, File2,
2629 UDFIsADirectory(File1) ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME,
2630 FILE_ACTION_ADDED );*/
2631 FsRtlNotifyFullReportChange( Vcb->NotifyIRPMutex, &(Vcb->NextNotifyIRP),
2632 (PSTRING)&LocalPath,
2633 ((Dir2->Fcb->FCBFlags & UDF_FCB_ROOT_DIRECTORY) ? 0 : Dir2->Fcb->FCBName->ObjectName.Length) + sizeof(WCHAR),
2634 NULL,NULL,
2635 UDFIsADirectory(File1) ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME,
2636 FILE_ACTION_ADDED,
2637 NULL);
2638 } else {
2639 /* UDFNotifyFullReportChange( Vcb, File2,
2640 FILE_NOTIFY_CHANGE_ATTRIBUTES |
2641 FILE_NOTIFY_CHANGE_SIZE |
2642 FILE_NOTIFY_CHANGE_LAST_WRITE |
2643 FILE_NOTIFY_CHANGE_LAST_ACCESS |
2644 FILE_NOTIFY_CHANGE_CREATION |
2645 FILE_NOTIFY_CHANGE_EA,
2646 FILE_ACTION_MODIFIED );*/
2647 FsRtlNotifyFullReportChange( Vcb->NotifyIRPMutex, &(Vcb->NextNotifyIRP),
2648 (PSTRING)&LocalPath,
2649 ((Dir2->Fcb->FCBFlags & UDF_FCB_ROOT_DIRECTORY) ? 0 : Dir2->Fcb->FCBName->ObjectName.Length) + sizeof(WCHAR),
2650 NULL,NULL,
2651 UDFIsADirectory(File1) ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME,
2652 FILE_NOTIFY_CHANGE_ATTRIBUTES |
2653 FILE_NOTIFY_CHANGE_SIZE |
2654 FILE_NOTIFY_CHANGE_LAST_WRITE |
2655 FILE_NOTIFY_CHANGE_LAST_ACCESS |
2656 FILE_NOTIFY_CHANGE_CREATION |
2657 FILE_NOTIFY_CHANGE_EA,
2658 NULL);
2659 }
2660
2661 RC = STATUS_SUCCESS;
2662
2663 try_exit: NOTHING;
2664
2665 } _SEH2_FINALLY {
2666
2667 if(AcquiredFcb1) {
2668 UDF_CHECK_PAGING_IO_RESOURCE(Fcb1->NTRequiredFCB);
2669 UDFReleaseResource(&(Fcb1->NTRequiredFCB->MainResource));
2670 }
2671 if(AcquiredDir1) {
2672 UDF_CHECK_PAGING_IO_RESOURCE(Dir1->Fcb->NTRequiredFCB);
2673 UDFReleaseResource(&(Dir1->Fcb->NTRequiredFCB->MainResource));
2674 }
2675 if(AcquiredVcb) {
2676 if(AcquiredVcbEx)
2677 UDFConvertExclusiveToSharedLite(&(Vcb->VCBResource));
2678 } else {
2679 // caller assumes Vcb to be acquired shared
2680 BrutePoint();
2681 UDFAcquireResourceShared(&(Vcb->VCBResource), TRUE);
2682 }
2683
2684 if(LocalPath.Buffer) {
2685 MyFreePool__(LocalPath.Buffer);
2686 }
2687 } _SEH2_END;
2688
2689 return RC;
2690 } // end UDFHardLink()
2691 #endif //UDF_ALLOW_HARD_LINKS
2692
2693 #endif //UDF_READ_ONLY_BUILD