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