Sync with trunk (r48008)
[reactos.git] / dll / win32 / kernel32 / file / create.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS system libraries
5 * FILE: lib/kernel32/file/create.c
6 * PURPOSE: Directory functions
7 * PROGRAMMER: Ariadne ( ariadne@xs4all.nl)
8 * UPDATE HISTORY:
9 * Created 01/11/98
10 * Removed use of SearchPath (not used by Windows)
11 * 18/08/2002: CreateFileW mess cleaned up (KJK::Hyperion)
12 * 24/08/2002: removed superfluous DPRINTs (KJK::Hyperion)
13 */
14
15 /* INCLUDES *****************************************************************/
16
17 #include <k32.h>
18 #include <wine/debug.h>
19
20 WINE_DEFAULT_DEBUG_CHANNEL(kernel32file);
21
22 #define SYMLINK_FLAG_RELATIVE 1
23
24 typedef struct _REPARSE_DATA_BUFFER {
25 ULONG ReparseTag;
26 USHORT ReparseDataLength;
27 USHORT Reserved;
28 union {
29 struct {
30 USHORT SubstituteNameOffset;
31 USHORT SubstituteNameLength;
32 USHORT PrintNameOffset;
33 USHORT PrintNameLength;
34 ULONG Flags;
35 WCHAR PathBuffer[1];
36 } SymbolicLinkReparseBuffer;
37 struct {
38 USHORT SubstituteNameOffset;
39 USHORT SubstituteNameLength;
40 USHORT PrintNameOffset;
41 USHORT PrintNameLength;
42 WCHAR PathBuffer[1];
43 } MountPointReparseBuffer;
44 struct {
45 UCHAR DataBuffer[1];
46 } GenericReparseBuffer;
47 };
48 } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
49
50 #define REPARSE_DATA_BUFFER_HEADER_SIZE FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer)
51
52 /* FUNCTIONS ****************************************************************/
53
54 /*
55 * @implemented
56 */
57 HANDLE WINAPI CreateFileA (LPCSTR lpFileName,
58 DWORD dwDesiredAccess,
59 DWORD dwShareMode,
60 LPSECURITY_ATTRIBUTES lpSecurityAttributes,
61 DWORD dwCreationDisposition,
62 DWORD dwFlagsAndAttributes,
63 HANDLE hTemplateFile)
64 {
65 PWCHAR FileNameW;
66 HANDLE FileHandle;
67
68 TRACE("CreateFileA(lpFileName %s)\n",lpFileName);
69
70 if (!(FileNameW = FilenameA2W(lpFileName, FALSE)))
71 return INVALID_HANDLE_VALUE;
72
73 FileHandle = CreateFileW (FileNameW,
74 dwDesiredAccess,
75 dwShareMode,
76 lpSecurityAttributes,
77 dwCreationDisposition,
78 dwFlagsAndAttributes,
79 hTemplateFile);
80
81 return FileHandle;
82 }
83
84
85 /*
86 * @implemented
87 */
88 HANDLE WINAPI CreateFileW (LPCWSTR lpFileName,
89 DWORD dwDesiredAccess,
90 DWORD dwShareMode,
91 LPSECURITY_ATTRIBUTES lpSecurityAttributes,
92 DWORD dwCreationDisposition,
93 DWORD dwFlagsAndAttributes,
94 HANDLE hTemplateFile)
95 {
96 OBJECT_ATTRIBUTES ObjectAttributes;
97 IO_STATUS_BLOCK IoStatusBlock;
98 UNICODE_STRING NtPathU;
99 HANDLE FileHandle;
100 NTSTATUS Status;
101 ULONG FileAttributes, Flags = 0;
102 PVOID EaBuffer = NULL;
103 ULONG EaLength = 0;
104
105 if (!lpFileName || !lpFileName[0])
106 {
107 SetLastError( ERROR_PATH_NOT_FOUND );
108 return INVALID_HANDLE_VALUE;
109 }
110
111 TRACE("CreateFileW(lpFileName %S)\n",lpFileName);
112
113 /* validate & translate the creation disposition */
114 switch (dwCreationDisposition)
115 {
116 case CREATE_NEW:
117 dwCreationDisposition = FILE_CREATE;
118 break;
119
120 case CREATE_ALWAYS:
121 dwCreationDisposition = FILE_OVERWRITE_IF;
122 break;
123
124 case OPEN_EXISTING:
125 dwCreationDisposition = FILE_OPEN;
126 break;
127
128 case OPEN_ALWAYS:
129 dwCreationDisposition = FILE_OPEN_IF;
130 break;
131
132 case TRUNCATE_EXISTING:
133 dwCreationDisposition = FILE_OVERWRITE;
134 break;
135
136 default:
137 SetLastError(ERROR_INVALID_PARAMETER);
138 return (INVALID_HANDLE_VALUE);
139 }
140
141 /* check for console input/output */
142 if (0 == _wcsicmp(L"CONOUT$", lpFileName)
143 || 0 == _wcsicmp(L"CONIN$", lpFileName))
144 {
145 return OpenConsoleW(lpFileName,
146 dwDesiredAccess,
147 lpSecurityAttributes ? lpSecurityAttributes->bInheritHandle : FALSE,
148 FILE_SHARE_READ | FILE_SHARE_WRITE);
149 }
150
151 /* validate & translate the flags */
152
153 /* translate the flags that need no validation */
154 if (!(dwFlagsAndAttributes & FILE_FLAG_OVERLAPPED))
155 {
156 /* yes, nonalert is correct! apc's are not delivered
157 while waiting for file io to complete */
158 Flags |= FILE_SYNCHRONOUS_IO_NONALERT;
159 }
160
161 if(dwFlagsAndAttributes & FILE_FLAG_WRITE_THROUGH)
162 Flags |= FILE_WRITE_THROUGH;
163
164 if(dwFlagsAndAttributes & FILE_FLAG_NO_BUFFERING)
165 Flags |= FILE_NO_INTERMEDIATE_BUFFERING;
166
167 if(dwFlagsAndAttributes & FILE_FLAG_RANDOM_ACCESS)
168 Flags |= FILE_RANDOM_ACCESS;
169
170 if(dwFlagsAndAttributes & FILE_FLAG_SEQUENTIAL_SCAN)
171 Flags |= FILE_SEQUENTIAL_ONLY;
172
173 if(dwFlagsAndAttributes & FILE_FLAG_DELETE_ON_CLOSE)
174 Flags |= FILE_DELETE_ON_CLOSE;
175
176 if(dwFlagsAndAttributes & FILE_FLAG_BACKUP_SEMANTICS)
177 {
178 if(dwDesiredAccess & GENERIC_ALL)
179 Flags |= FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REMOTE_INSTANCE;
180 else
181 {
182 if(dwDesiredAccess & GENERIC_READ)
183 Flags |= FILE_OPEN_FOR_BACKUP_INTENT;
184
185 if(dwDesiredAccess & GENERIC_WRITE)
186 Flags |= FILE_OPEN_REMOTE_INSTANCE;
187 }
188 }
189 else
190 Flags |= FILE_NON_DIRECTORY_FILE;
191
192 if(dwFlagsAndAttributes & FILE_FLAG_OPEN_REPARSE_POINT)
193 Flags |= FILE_OPEN_REPARSE_POINT;
194
195 if(dwFlagsAndAttributes & FILE_FLAG_OPEN_NO_RECALL)
196 Flags |= FILE_OPEN_NO_RECALL;
197
198 FileAttributes = (dwFlagsAndAttributes & (FILE_ATTRIBUTE_VALID_FLAGS & ~FILE_ATTRIBUTE_DIRECTORY));
199
200 /* handle may allways be waited on and querying attributes are allways allowed */
201 dwDesiredAccess |= SYNCHRONIZE | FILE_READ_ATTRIBUTES;
202
203 /* FILE_FLAG_POSIX_SEMANTICS is handled later */
204
205 /* validate & translate the filename */
206 if (!RtlDosPathNameToNtPathName_U (lpFileName,
207 &NtPathU,
208 NULL,
209 NULL))
210 {
211 WARN("Invalid path\n");
212 SetLastError(ERROR_PATH_NOT_FOUND);
213 return INVALID_HANDLE_VALUE;
214 }
215
216 TRACE("NtPathU \'%wZ\'\n", &NtPathU);
217
218 if (hTemplateFile != NULL)
219 {
220 FILE_EA_INFORMATION EaInformation;
221
222 for (;;)
223 {
224 /* try to get the size of the extended attributes, if we fail just continue
225 creating the file without copying the attributes! */
226 Status = NtQueryInformationFile(hTemplateFile,
227 &IoStatusBlock,
228 &EaInformation,
229 sizeof(FILE_EA_INFORMATION),
230 FileEaInformation);
231 if (NT_SUCCESS(Status) && (EaInformation.EaSize != 0))
232 {
233 /* there's extended attributes to read, let's give it a try */
234 EaBuffer = RtlAllocateHeap(RtlGetProcessHeap(),
235 0,
236 EaInformation.EaSize);
237 if (EaBuffer == NULL)
238 {
239 RtlFreeHeap(RtlGetProcessHeap(),
240 0,
241 NtPathU.Buffer);
242
243 /* the template file handle is valid and has extended attributes,
244 however we seem to lack some memory here. We should fail here! */
245 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
246 return INVALID_HANDLE_VALUE;
247 }
248
249 Status = NtQueryEaFile(hTemplateFile,
250 &IoStatusBlock,
251 EaBuffer,
252 EaInformation.EaSize,
253 FALSE,
254 NULL,
255 0,
256 NULL,
257 TRUE);
258
259 if (NT_SUCCESS(Status))
260 {
261 /* we successfully read the extended attributes, break the loop
262 and continue */
263 EaLength = EaInformation.EaSize;
264 break;
265 }
266 else
267 {
268 RtlFreeHeap(RtlGetProcessHeap(),
269 0,
270 EaBuffer);
271 EaBuffer = NULL;
272
273 if (Status != STATUS_BUFFER_TOO_SMALL)
274 {
275 /* unless we just allocated not enough memory, break the loop
276 and just continue without copying extended attributes */
277 break;
278 }
279 }
280 }
281 else
282 {
283 /* we either failed to get the size of the extended attributes or
284 they're empty, just continue as there's no need to copy
285 attributes */
286 break;
287 }
288 }
289 }
290
291 /* build the object attributes */
292 InitializeObjectAttributes(&ObjectAttributes,
293 &NtPathU,
294 0,
295 NULL,
296 NULL);
297
298 if (lpSecurityAttributes)
299 {
300 if(lpSecurityAttributes->bInheritHandle)
301 ObjectAttributes.Attributes |= OBJ_INHERIT;
302
303 ObjectAttributes.SecurityDescriptor = lpSecurityAttributes->lpSecurityDescriptor;
304 }
305
306 if(!(dwFlagsAndAttributes & FILE_FLAG_POSIX_SEMANTICS))
307 ObjectAttributes.Attributes |= OBJ_CASE_INSENSITIVE;
308
309 /* perform the call */
310 Status = NtCreateFile (&FileHandle,
311 dwDesiredAccess,
312 &ObjectAttributes,
313 &IoStatusBlock,
314 NULL,
315 FileAttributes,
316 dwShareMode,
317 dwCreationDisposition,
318 Flags,
319 EaBuffer,
320 EaLength);
321
322 RtlFreeHeap(RtlGetProcessHeap(),
323 0,
324 NtPathU.Buffer);
325
326 /* free the extended attributes buffer if allocated */
327 if (EaBuffer != NULL)
328 {
329 RtlFreeHeap(RtlGetProcessHeap(),
330 0,
331 EaBuffer);
332 }
333
334 /* error */
335 if (!NT_SUCCESS(Status))
336 {
337 /* In the case file creation was rejected due to CREATE_NEW flag
338 * was specified and file with that name already exists, correct
339 * last error is ERROR_FILE_EXISTS and not ERROR_ALREADY_EXISTS.
340 * Note: RtlNtStatusToDosError is not the subject to blame here.
341 */
342 if (Status == STATUS_OBJECT_NAME_COLLISION &&
343 dwCreationDisposition == FILE_CREATE)
344 {
345 SetLastError( ERROR_FILE_EXISTS );
346 }
347 else
348 {
349 SetLastErrorByStatus (Status);
350 }
351
352 return INVALID_HANDLE_VALUE;
353 }
354
355 /*
356 create with OPEN_ALWAYS (FILE_OPEN_IF) returns info = FILE_OPENED or FILE_CREATED
357 create with CREATE_ALWAYS (FILE_OVERWRITE_IF) returns info = FILE_OVERWRITTEN or FILE_CREATED
358 */
359 if (dwCreationDisposition == FILE_OPEN_IF)
360 {
361 SetLastError(IoStatusBlock.Information == FILE_OPENED ? ERROR_ALREADY_EXISTS : 0);
362 }
363 else if (dwCreationDisposition == FILE_OVERWRITE_IF)
364 {
365 SetLastError(IoStatusBlock.Information == FILE_OVERWRITTEN ? ERROR_ALREADY_EXISTS : 0);
366 }
367
368 return FileHandle;
369 }
370
371
372 /*
373 * @implemented
374 */
375 BOOLEAN
376 WINAPI
377 CreateSymbolicLinkW(IN LPCWSTR lpSymlinkFileName,
378 IN LPCWSTR lpTargetFileName,
379 IN DWORD dwFlags)
380 {
381 IO_STATUS_BLOCK IoStatusBlock;
382 OBJECT_ATTRIBUTES ObjectAttributes;
383 HANDLE hSymlink = NULL;
384 UNICODE_STRING SymlinkFileName = { 0, 0, NULL };
385 UNICODE_STRING TargetFileName = { 0, 0, NULL };
386 BOOLEAN bAllocatedTarget = FALSE, bRelativePath = FALSE;
387 LPWSTR lpTargetFullFileName = NULL;
388 SIZE_T cbPrintName;
389 SIZE_T cbReparseData;
390 PREPARSE_DATA_BUFFER pReparseData = NULL;
391 PBYTE pBufTail;
392 NTSTATUS Status;
393 ULONG dwCreateOptions;
394 DWORD dwErr;
395
396 if(!lpSymlinkFileName || !lpTargetFileName || (dwFlags | SYMBOLIC_LINK_FLAG_DIRECTORY) != SYMBOLIC_LINK_FLAG_DIRECTORY)
397 {
398 SetLastError(ERROR_INVALID_PARAMETER);
399 return FALSE;
400 }
401
402 if(dwFlags & SYMBOLIC_LINK_FLAG_DIRECTORY)
403 dwCreateOptions = FILE_DIRECTORY_FILE;
404 else
405 dwCreateOptions = FILE_NON_DIRECTORY_FILE;
406
407 switch(RtlDetermineDosPathNameType_U(lpTargetFileName))
408 {
409 case RtlPathTypeUnknown:
410 case RtlPathTypeRooted:
411 case RtlPathTypeRelative:
412 bRelativePath = TRUE;
413 RtlInitUnicodeString(&TargetFileName, lpTargetFileName);
414 break;
415
416 case RtlPathTypeDriveRelative:
417 {
418 LPWSTR FilePart;
419 SIZE_T cchTargetFullFileName;
420
421 cchTargetFullFileName = GetFullPathNameW(lpTargetFileName, 0, NULL, &FilePart);
422
423 if(cchTargetFullFileName == 0)
424 {
425 dwErr = GetLastError();
426 goto Cleanup;
427 }
428
429 lpTargetFullFileName = RtlAllocateHeap(RtlGetProcessHeap(), 0, cchTargetFullFileName * sizeof(WCHAR));
430
431 if(lpTargetFullFileName == NULL)
432 {
433 dwErr = ERROR_NOT_ENOUGH_MEMORY;
434 goto Cleanup;
435 }
436
437 if(GetFullPathNameW(lpTargetFileName, cchTargetFullFileName, lpTargetFullFileName, &FilePart) == 0)
438 {
439 dwErr = GetLastError();
440 goto Cleanup;
441 }
442 }
443
444 lpTargetFileName = lpTargetFullFileName;
445
446 // fallthrough
447
448 case RtlPathTypeUncAbsolute:
449 case RtlPathTypeDriveAbsolute:
450 case RtlPathTypeLocalDevice:
451 case RtlPathTypeRootLocalDevice:
452 default:
453 if(!RtlDosPathNameToNtPathName_U(lpTargetFileName, &TargetFileName, NULL, NULL))
454 {
455 bAllocatedTarget = TRUE;
456 dwErr = ERROR_INVALID_PARAMETER;
457 goto Cleanup;
458 }
459 }
460
461 cbPrintName = wcslen(lpTargetFileName) * sizeof(WCHAR);
462 cbReparseData = FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) + TargetFileName.Length + cbPrintName;
463 pReparseData = RtlAllocateHeap(RtlGetProcessHeap(), 0, cbReparseData);
464
465 if(pReparseData == NULL)
466 {
467 dwErr = ERROR_NOT_ENOUGH_MEMORY;
468 goto Cleanup;
469 }
470
471 pBufTail = (PBYTE)(pReparseData->SymbolicLinkReparseBuffer.PathBuffer);
472
473 pReparseData->ReparseTag = (ULONG)IO_REPARSE_TAG_SYMLINK;
474 pReparseData->ReparseDataLength = (USHORT)cbReparseData - REPARSE_DATA_BUFFER_HEADER_SIZE;
475 pReparseData->Reserved = 0;
476
477 pReparseData->SymbolicLinkReparseBuffer.SubstituteNameOffset = 0;
478 pReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength = TargetFileName.Length;
479 pBufTail += pReparseData->SymbolicLinkReparseBuffer.SubstituteNameOffset;
480 RtlCopyMemory(pBufTail, TargetFileName.Buffer, TargetFileName.Length);
481
482 pReparseData->SymbolicLinkReparseBuffer.PrintNameOffset = pReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength;
483 pReparseData->SymbolicLinkReparseBuffer.PrintNameLength = (USHORT)cbPrintName;
484 pBufTail += pReparseData->SymbolicLinkReparseBuffer.PrintNameOffset;
485 RtlCopyMemory(pBufTail, lpTargetFileName, cbPrintName);
486
487 pReparseData->SymbolicLinkReparseBuffer.Flags = 0;
488
489 if(bRelativePath)
490 pReparseData->SymbolicLinkReparseBuffer.Flags |= 1; // TODO! give this lone flag a name
491
492 if(!RtlDosPathNameToNtPathName_U(lpSymlinkFileName, &SymlinkFileName, NULL, NULL))
493 {
494 dwErr = ERROR_PATH_NOT_FOUND;
495 goto Cleanup;
496 }
497
498 InitializeObjectAttributes(&ObjectAttributes, &SymlinkFileName, OBJ_CASE_INSENSITIVE, NULL, NULL);
499
500 Status = NtCreateFile
501 (
502 &hSymlink,
503 FILE_WRITE_ATTRIBUTES | DELETE | SYNCHRONIZE,
504 &ObjectAttributes,
505 &IoStatusBlock,
506 NULL,
507 FILE_ATTRIBUTE_NORMAL,
508 0,
509 FILE_CREATE,
510 FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT | dwCreateOptions,
511 NULL,
512 0
513 );
514
515 if(!NT_SUCCESS(Status))
516 {
517 dwErr = RtlNtStatusToDosError(Status);
518 goto Cleanup;
519 }
520
521 Status = NtFsControlFile
522 (
523 hSymlink,
524 NULL,
525 NULL,
526 NULL,
527 &IoStatusBlock,
528 FSCTL_SET_REPARSE_POINT,
529 pReparseData,
530 cbReparseData,
531 NULL,
532 0
533 );
534
535 if(!NT_SUCCESS(Status))
536 {
537 FILE_DISPOSITION_INFORMATION DispInfo;
538 DispInfo.DeleteFile = TRUE;
539 NtSetInformationFile(hSymlink, &IoStatusBlock, &DispInfo, sizeof(DispInfo), FileDispositionInformation);
540
541 dwErr = RtlNtStatusToDosError(Status);
542 goto Cleanup;
543 }
544
545 dwErr = NO_ERROR;
546
547 Cleanup:
548 if(hSymlink)
549 NtClose(hSymlink);
550
551 RtlFreeUnicodeString(&SymlinkFileName);
552 if (bAllocatedTarget)
553 {
554 RtlFreeHeap(RtlGetProcessHeap(),
555 0,
556 TargetFileName.Buffer);
557 }
558
559 if(lpTargetFullFileName)
560 RtlFreeHeap(RtlGetProcessHeap(), 0, lpTargetFullFileName);
561
562 if(pReparseData)
563 RtlFreeHeap(RtlGetProcessHeap(), 0, pReparseData);
564
565 if(dwErr)
566 {
567 SetLastError(dwErr);
568 return FALSE;
569 }
570
571 return TRUE;
572 }
573
574
575 /*
576 * @implemented
577 */
578 BOOLEAN
579 NTAPI
580 CreateSymbolicLinkA(IN LPCSTR lpSymlinkFileName,
581 IN LPCSTR lpTargetFileName,
582 IN DWORD dwFlags)
583 {
584 PWCHAR SymlinkW, TargetW;
585 BOOLEAN Ret;
586
587 if(!lpSymlinkFileName || !lpTargetFileName)
588 {
589 SetLastError(ERROR_INVALID_PARAMETER);
590 return FALSE;
591 }
592
593 if (!(SymlinkW = FilenameA2W(lpSymlinkFileName, FALSE)))
594 return FALSE;
595
596 if (!(TargetW = FilenameA2W(lpTargetFileName, TRUE)))
597 return FALSE;
598
599 Ret = CreateSymbolicLinkW(SymlinkW,
600 TargetW,
601 dwFlags);
602
603 RtlFreeHeap(RtlGetProcessHeap(), 0, SymlinkW);
604 RtlFreeHeap(RtlGetProcessHeap(), 0, TargetW);
605
606 return Ret;
607 }
608
609
610 /* EOF */