1 //
2 // open.cpp
3 //
4 // Copyright (c) Microsoft Corporation. All rights reserved.
5 //
6 // Defines _open() and its friends, who are used to open or create files.
7 //
8 // These functions are used to open a file.
9 //
10 // oflag: The primary file open flags are passed via this parameter. It may
11 // have a combination of the following flags:
12 // * _O_APPEND: Reposition file ptr to end before every write
13 // * _O_BINARY: Open in binary mode
14 // * _O_CREAT: Create a new file* no effect if file already exists
15 // * _O_EXCL: Return error if file exists, only use with O_CREAT
16 // * _O_RDONLY: Open for reading only
17 // * _O_RDWR: Open for reading and writing
18 // * _O_TEXT: Open in text mode
19 // * _O_TRUNC: Open and truncate to 0 length (must have write permission)
20 // * _O_WRONLY: Open for writing only
21 // * _O_NOINHERIT: Handle will not be inherited by child processes.
22 // Exactly one of _O_RDONLY, _O_WRONLY, and _O_RDWR must be present.
23 //
24 // shflag: Specifies the sharing options with which the file is to be opened.
25 // This parameter is only supported by the sharing-enabled open
26 // functions (_tsopen, _tsopen_s, etc.). The following flags are
27 // supported:
28 // * _SH_COMPAT: Set compatability mode
29 // * _SH_DENYRW: Deny read and write access to the file
30 // * _SH_DENYWR: Deny write access to the file
31 // * _SH_DENYRD: Deny read access to the file
32 // * _SH_DENYNO: Permit read and write access
33 //
34 // pmode: The pmode argument is only required when _O_CREAT is specified. Its
35 // flags are as follows:
36 // * _S_IWRITE:
37 // * _S_IREAD:
38 // These flags may be combined (_S_IWRITE | _S_IREAD) to enable both
39 // reading and writing. The current file permission mask is applied to
40 // pmode before setting the permission (see umask).
41 //
42 // Functions that return an errno_t return 0 on success and an error code on
43 // failure. Functions that return an int return the file handle on success, and
44 // return -1 and set errno on failure.
45 //
46 #include <corecrt_internal_lowio.h>
47 #include <stdarg.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <sys/stat.h>
51
52
53
54 namespace
55 {
56 DWORD const GENERIC_READ_WRITE = (GENERIC_READ | GENERIC_WRITE);
57
58 struct file_options
59 {
60 // These are the flags that are used for the osflag of the CRT file
61 // object that is created.
62 char crt_flags;
63
64 // These are the flags that are eventually passed to CreateFile to tell
65 // the Operating System how to create the file:
66 DWORD access;
67 DWORD create;
68 DWORD share;
69 DWORD attributes;
70 DWORD flags;
71 };
72 }
73
74
75
76 #define UTF16LE_BOM 0xFEFF // UTF16 Little Endian Byte Order Mark
77 #define UTF16BE_BOM 0xFFFE // UTF16 Big Endian Byte Order Mark
78 #define BOM_MASK 0xFFFF // Mask for testing Byte Order Mark
79 #define UTF8_BOM 0xBFBBEF // UTF8 Byte Order Mark
80 #define UTF16_BOMLEN 2 // No of Bytes in a UTF16 BOM
81 #define UTF8_BOMLEN 3 // No of Bytes in a UTF8 BOM
82
83 template <typename Character>
common_open(_In_z_ Character const * const path,int const oflag,int const pmode)84 static int __cdecl common_open(
85 _In_z_ Character const* const path,
86 int const oflag,
87 int const pmode
88 ) throw()
89 {
90 typedef __crt_char_traits<Character> traits;
91
92 _VALIDATE_RETURN(path != nullptr, EINVAL, -1);
93
94 int fh = -1;
95 int unlock_flag = 0;
96 errno_t error_code = 0;
97 __try
98 {
99 error_code = traits::tsopen_nolock(&unlock_flag, &fh, path, oflag, _SH_DENYNO, pmode, 0);
100 }
101 __finally
102 {
103 if (unlock_flag)
104 {
105 if (error_code)
106 {
107 _osfile(fh) &= ~FOPEN;
108 }
109
110 __acrt_lowio_unlock_fh(fh);
111 }
112 }
113 __endtry
114
115 if (error_code != 0)
116 {
117 errno = error_code;
118 return -1;
119 }
120
121 return fh;
122 }
123
_open(char const * const path,int const oflag,...)124 extern "C" int _open(char const* const path, int const oflag, ...)
125 {
126 va_list arglist;
127 va_start(arglist, oflag);
128 int const pmode = va_arg(arglist, int);
129 va_end(arglist);
130
131 return common_open(path, oflag, pmode);
132 }
133
_wopen(wchar_t const * const path,int const oflag,...)134 extern "C" int _wopen(wchar_t const* const path, int const oflag, ...)
135 {
136 va_list arglist;
137 va_start(arglist, oflag);
138 int const pmode = va_arg(arglist, int);
139 va_end(arglist);
140
141 return common_open(path, oflag, pmode);
142 }
143
144
145
146 template <typename Character>
common_sopen_dispatch(_In_z_ Character const * const path,int const oflag,int const shflag,int const pmode,int * const pfh,int const secure)147 static errno_t __cdecl common_sopen_dispatch(
148 _In_z_ Character const* const path,
149 int const oflag,
150 int const shflag,
151 int const pmode,
152 int* const pfh,
153 int const secure
154 ) throw()
155 {
156 typedef __crt_char_traits<Character> traits;
157
158 _VALIDATE_RETURN_ERRCODE(pfh != nullptr, EINVAL);
159 *pfh = -1;
160
161 _VALIDATE_RETURN_ERRCODE(path != nullptr, EINVAL);
162
163 if(secure)
164 {
165 _VALIDATE_RETURN_ERRCODE((pmode & (~(_S_IREAD | _S_IWRITE))) == 0, EINVAL);
166 }
167
168
169 int unlock_flag = 0;
170 errno_t error_code = 0;
171 __try
172 {
173 error_code = traits::tsopen_nolock(&unlock_flag, pfh, path, oflag, shflag, pmode, secure);
174 }
175 __finally
176 {
177 if (unlock_flag)
178 {
179 if (error_code)
180 {
181 _osfile(*pfh) &= ~FOPEN;
182 }
183 __acrt_lowio_unlock_fh(*pfh);
184 }
185 }
186 __endtry
187
188 if (error_code != 0)
189 {
190 *pfh = -1;
191 }
192
193 return error_code;
194 }
195
_sopen_dispatch(char const * const path,int const oflag,int const shflag,int const pmode,int * const pfh,int const secure)196 extern "C" errno_t __cdecl _sopen_dispatch(
197 char const* const path,
198 int const oflag,
199 int const shflag,
200 int const pmode,
201 int* const pfh,
202 int const secure
203 )
204 {
205 return common_sopen_dispatch(path, oflag, shflag, pmode, pfh, secure);
206 }
207
_wsopen_dispatch(wchar_t const * const path,int const oflag,int const shflag,int const pmode,int * const pfh,int const secure)208 extern "C" errno_t __cdecl _wsopen_dispatch(
209 wchar_t const* const path,
210 int const oflag,
211 int const shflag,
212 int const pmode,
213 int* const pfh,
214 int const secure
215 )
216 {
217 return common_sopen_dispatch(path, oflag, shflag, pmode, pfh, secure);
218 }
219
220
221
create_file(PCWSTR const path,SECURITY_ATTRIBUTES * const security_attributes,file_options const options)222 static HANDLE __cdecl create_file(
223 PCWSTR const path,
224 SECURITY_ATTRIBUTES* const security_attributes,
225 file_options const options
226 ) throw()
227 {
228 return CreateFileW(
229 path,
230 options.access,
231 options.share,
232 security_attributes,
233 options.create,
234 options.flags | options.attributes,
235 nullptr);
236 }
237
238
239
decode_access_flags(int const oflag)240 static DWORD decode_access_flags(int const oflag) throw()
241 {
242 switch (oflag & (_O_RDONLY | _O_WRONLY | _O_RDWR))
243 {
244 case _O_RDONLY:
245 return GENERIC_READ;
246
247 case _O_WRONLY:
248 // If the file is being opened in append mode, we give read access as
249 // well because in append (a, not a+) mode, we need to read the BOM to
250 // determine the encoding (ANSI, UTF-8, or UTF-16).
251 if ((oflag & _O_APPEND) && (oflag & (_O_WTEXT | _O_U16TEXT | _O_U8TEXT)) != 0)
252 return GENERIC_READ | GENERIC_WRITE;
253
254 return GENERIC_WRITE;
255
256 case _O_RDWR:
257 return GENERIC_READ | GENERIC_WRITE;
258 }
259
260 // This is unreachable, but the compiler can't tell.
261 _VALIDATE_RETURN(("Invalid open flag", 0), EINVAL, static_cast<DWORD>(-1));
262 return 0;
263 }
264
decode_open_create_flags(int const oflag)265 static DWORD decode_open_create_flags(int const oflag) throw()
266 {
267 switch (oflag & (_O_CREAT | _O_EXCL | _O_TRUNC))
268 {
269 case 0:
270 case _O_EXCL: // ignore EXCL w/o CREAT
271 return OPEN_EXISTING;
272
273 case _O_CREAT:
274 return OPEN_ALWAYS;
275
276 case _O_CREAT | _O_EXCL:
277 case _O_CREAT | _O_TRUNC | _O_EXCL:
278 return CREATE_NEW;
279
280 case _O_TRUNC:
281 case _O_TRUNC | _O_EXCL: // ignore EXCL w/o CREAT
282 return TRUNCATE_EXISTING;
283
284 case _O_CREAT | _O_TRUNC:
285 return CREATE_ALWAYS;
286 }
287
288 // This is unreachable, but the compiler can't tell.
289 _VALIDATE_RETURN(("Invalid open flag", 0), EINVAL, static_cast<DWORD>(-1));
290 return 0;
291 }
292
decode_sharing_flags(int const shflag,int const access)293 static DWORD decode_sharing_flags(int const shflag, int const access) throw()
294 {
295 switch (shflag)
296 {
297 case _SH_DENYRW:
298 return 0;
299
300 case _SH_DENYWR:
301 return FILE_SHARE_READ;
302
303 case _SH_DENYRD:
304 return FILE_SHARE_WRITE;
305
306 case _SH_DENYNO:
307 return FILE_SHARE_READ | FILE_SHARE_WRITE;
308
309 case _SH_SECURE:
310 if (access == GENERIC_READ)
311 return FILE_SHARE_READ;
312 else
313 return 0;
314 }
315
316 _VALIDATE_RETURN(("Invalid sharing flag", 0), EINVAL, static_cast<DWORD>(-1));
317 return 0;
318 }
319
is_text_mode(int const oflag)320 static bool is_text_mode(int const oflag) throw()
321 {
322 if (oflag & _O_BINARY)
323 return false;
324
325 if (oflag & (_O_TEXT | _O_WTEXT | _O_U16TEXT | _O_U8TEXT))
326 return true;
327
328 // Finally, check the global default mode:
329 int fmode;
330 _ERRCHECK(_get_fmode(&fmode));
331 if (fmode != _O_BINARY)
332 return true;
333
334 return false;
335 }
336
decode_options(int const oflag,int const shflag,int const pmode)337 static file_options decode_options(int const oflag, int const shflag, int const pmode) throw()
338 {
339 file_options result;
340 result.crt_flags = 0;
341 result.access = decode_access_flags(oflag);
342 result.create = decode_open_create_flags(oflag);
343 result.share = decode_sharing_flags(shflag, result.access);
344 result.attributes = FILE_ATTRIBUTE_NORMAL;
345 result.flags = 0;
346
347 if (oflag & _O_NOINHERIT)
348 {
349 result.crt_flags |= FNOINHERIT;
350 }
351
352 if (is_text_mode(oflag))
353 {
354 result.crt_flags |= FTEXT;
355 }
356
357 if (oflag & _O_CREAT)
358 {
359 if (((pmode & ~_umaskval) & _S_IWRITE) == 0)
360 result.attributes = FILE_ATTRIBUTE_READONLY;
361 }
362
363 if (oflag & _O_TEMPORARY)
364 {
365 result.flags |= FILE_FLAG_DELETE_ON_CLOSE;
366 result.access |= DELETE;
367 result.share |= FILE_SHARE_DELETE;
368 }
369
370 if (oflag & _O_SHORT_LIVED)
371 {
372 result.attributes |= FILE_ATTRIBUTE_TEMPORARY;
373 }
374
375 if (oflag & _O_OBTAIN_DIR)
376 {
377 result.flags |= FILE_FLAG_BACKUP_SEMANTICS;
378 }
379
380 if (oflag & _O_SEQUENTIAL)
381 {
382 result.flags |= FILE_FLAG_SEQUENTIAL_SCAN;
383 }
384 else if (oflag & _O_RANDOM)
385 {
386 result.flags |= FILE_FLAG_RANDOM_ACCESS;
387 }
388
389 return result;
390 }
391
392
393
394 // If we open a text mode file for writing, and the file ends in Ctrl+Z, we need
395 // to remove the Ctrl+Z character so that appending will work. We do this by
396 // seeking to the end of the file, testing if the last character is a Ctrl+Z,
397 // truncating the file if it is, then rewinding back to the beginning.
truncate_ctrl_z_if_present(int const fh)398 static errno_t truncate_ctrl_z_if_present(int const fh) throw()
399 {
400 // No truncation is possible for devices and pipes:
401 if (_osfile(fh) & (FDEV | FPIPE))
402 return 0;
403
404 // No truncation is necessary for binary files:
405 if ((_osfile(fh) & FTEXT) == 0)
406 return 0;
407
408 // Find the end of the file:
409 __int64 const last_char_position = _lseeki64_nolock(fh, -1, SEEK_END);
410
411 // If the seek failed, either the file is empty or an error occurred.
412 // (It's not an error if the file is empty.)
413 if (last_char_position == -1)
414 {
415 if (_doserrno == ERROR_NEGATIVE_SEEK)
416 return 0;
417
418 return errno;
419 }
420
421 // Read the last character. If the read succeeds and the character
422 // is a Ctrl+Z, remove the character from the file by shortening:
423 wchar_t c = 0;
424 if (_read_nolock(fh, &c, 1) == 0 && c == 26)
425 {
426 if (_chsize_nolock(fh, last_char_position) == -1)
427 return errno;
428 }
429
430 // Now, rewind the file pointer back to the beginning:
431 if (_lseeki64_nolock(fh, 0, SEEK_SET) == -1)
432 return errno;
433
434 return 0;
435 }
436
437
438
439 // Computes the text mode to be used for a file, using a combination of the
440 // options passed into the open function and the BOM read from the file.
configure_text_mode(int const fh,file_options const options,int oflag,__crt_lowio_text_mode & text_mode)441 static errno_t configure_text_mode(
442 int const fh,
443 file_options const options,
444 int oflag,
445 __crt_lowio_text_mode& text_mode
446 ) throw()
447 {
448 // The text mode is ANSI by default:
449 text_mode = __crt_lowio_text_mode::ansi;
450
451 // If the file is open in binary mode, it gets the default text mode:
452 if ((_osfile(fh) & FTEXT) == 0)
453 return 0;
454
455 // Set the default text mode per the oflag. The BOM may change the default,
456 // if one is present. If oflag does not specify a text mode, use the _fmode
457 // default:
458 DWORD const text_mode_mask = (_O_TEXT | _O_WTEXT | _O_U16TEXT | _O_U8TEXT);
459 if ((oflag & text_mode_mask) == 0)
460 {
461 int fmode = 0;
462 _ERRCHECK(_get_fmode(&fmode));
463
464 if ((fmode & text_mode_mask) == 0)
465 oflag |= _O_TEXT; // Default to ANSI.
466 else
467 oflag |= fmode & text_mode_mask;
468 }
469
470 // Now oflags should be set to one of the text modes:
471 _ASSERTE((oflag & text_mode_mask) != 0);
472
473 switch (oflag & text_mode_mask)
474 {
475 case _O_TEXT:
476 text_mode = __crt_lowio_text_mode::ansi;
477 break;
478
479 case _O_WTEXT:
480 case _O_WTEXT | _O_TEXT:
481 if ((oflag & (_O_WRONLY | _O_CREAT | _O_TRUNC)) == (_O_WRONLY | _O_CREAT | _O_TRUNC))
482 text_mode = __crt_lowio_text_mode::utf16le;
483 break;
484
485 case _O_U16TEXT:
486 case _O_U16TEXT | _O_TEXT:
487 text_mode = __crt_lowio_text_mode::utf16le;
488 break;
489
490 case _O_U8TEXT:
491 case _O_U8TEXT | _O_TEXT:
492 text_mode = __crt_lowio_text_mode::utf8;
493 break;
494 }
495
496
497 // If the file hasn't been opened with the UNICODE flags then we have
498 // nothing to do: the text mode is the default mode that we just set:
499 if ((oflag & (_O_WTEXT | _O_U16TEXT | _O_U8TEXT)) == 0)
500 return 0;
501
502 // If this file refers to a device, we cannot check the BOM, so we have
503 // nothing to do: the text mode is the default mode that we just set:
504 if ((options.crt_flags & FDEV) != 0)
505 return 0;
506
507
508 // Determine whether we need to check or write the BOM, by testing the
509 // access with which the file was opened and whether the file already
510 // existed or was just created:
511 int check_bom = 0;
512 int write_bom = 0;
513 switch (options.access & GENERIC_READ_WRITE)
514 {
515 case GENERIC_READ:
516 check_bom = 1;
517 break;
518
519 case GENERIC_WRITE:
520 case GENERIC_READ_WRITE:
521 switch (options.create)
522 {
523 // If this file was opened, we will read the BOM if the file was opened
524 // with read/write access. We will write the BOM if and only if the
525 // file is empty:
526 case OPEN_EXISTING:
527 case OPEN_ALWAYS:
528 {
529 if (_lseeki64_nolock(fh, 0, SEEK_END) != 0)
530 {
531 if (_lseeki64_nolock(fh, 0, SEEK_SET) == -1)
532 return errno;
533
534 // If we have read access, then we need to check the BOM. Note
535 // that we've taken a shortcut here: if the file is empty, then
536 // we do not set this flag because the file doesn't have a BOM
537 // to be read.
538 check_bom = (options.access & GENERIC_READ) != 0;
539 }
540 else
541 {
542 write_bom = 1;
543 break;
544 }
545 break;
546 }
547
548 // If this is a new or truncated file, then we always write the BOM:
549 case CREATE_NEW:
550 case CREATE_ALWAYS:
551 case TRUNCATE_EXISTING:
552 {
553 write_bom = 1;
554 break;
555 }
556 }
557 break;
558 }
559
560 if (check_bom)
561 {
562 int bom = 0;
563 int const count = _read_nolock(fh, &bom, UTF8_BOMLEN);
564
565 // Intrernal validation: This branch should never be taken if write_bom
566 // is true and count > 0:
567 if (count > 0 && write_bom == 1)
568 {
569 _ASSERTE(0 && "Internal Error");
570 write_bom = 0;
571 }
572
573 switch (count)
574 {
575 case -1:
576 return errno;
577
578 case UTF8_BOMLEN:
579 if (bom == UTF8_BOM)
580 {
581 text_mode = __crt_lowio_text_mode::utf8;
582 break;
583 }
584
585 case UTF16_BOMLEN:
586 if((bom & BOM_MASK) == UTF16BE_BOM)
587 {
588 _ASSERTE(0 && "Only UTF-16 little endian & UTF-8 is supported for reads");
589 errno = EINVAL;
590 return errno;
591 }
592
593 if((bom & BOM_MASK) == UTF16LE_BOM)
594 {
595 // We have read three bytes, so we should seek back one byte:
596 if(_lseeki64_nolock(fh, UTF16_BOMLEN, SEEK_SET) == -1)
597 return errno;
598
599 text_mode = __crt_lowio_text_mode::utf16le;
600 break;
601 }
602
603 // Fall through to default case to lseek to beginning of file
604
605 default:
606 // The file has no BOM, so we seek back to the beginning:
607 if (_lseeki64_nolock(fh, 0, SEEK_SET) == -1)
608 return errno;
609
610 break;
611 }
612 }
613
614 if (write_bom)
615 {
616 // If we are creating a new file, we write a UTF-16LE or UTF8 BOM:
617 int bom_length = 0;
618 int bom = 0;
619 switch (text_mode)
620 {
621 case __crt_lowio_text_mode::utf16le:
622 {
623 bom = UTF16LE_BOM;
624 bom_length = UTF16_BOMLEN;
625 break;
626 }
627 case __crt_lowio_text_mode::utf8:
628 {
629 bom = UTF8_BOM;
630 bom_length = UTF8_BOMLEN;
631 break;
632 }
633 }
634
635 for (int total_written = 0; bom_length > total_written; )
636 {
637 char const* const bom_begin = reinterpret_cast<char const*>(&bom);
638
639 // Note that the call to write may write less than bom_length
640 // characters but not really fail. We retry until the write fails
641 // or we have written all of the characters:
642 int const written = _write(fh, bom_begin + total_written, bom_length - total_written);
643 if (written == -1)
644 return errno;
645
646 total_written += written;
647 }
648 }
649
650 return 0; // Success!
651 }
652
653
654
_wsopen_nolock(int * const punlock_flag,int * const pfh,wchar_t const * const path,int const oflag,int const shflag,int const pmode,int const secure)655 extern "C" errno_t __cdecl _wsopen_nolock(
656 int* const punlock_flag,
657 int* const pfh,
658 wchar_t const* const path,
659 int const oflag,
660 int const shflag,
661 int const pmode,
662 int const secure
663 )
664 {
665 UNREFERENCED_PARAMETER(secure);
666
667 // First, do the initial parse of the options. The only thing that can fail
668 // here is the parsing of the share options, in which case -1 is returned
669 // and errno is set.
670 file_options options = decode_options(oflag, shflag, pmode);
671 if (options.share == static_cast<DWORD>(-1))
672 {
673 _doserrno = 0;
674 *pfh = -1;
675 return errno;
676 }
677
678 // Allocate the CRT file handle. Note that if a handle is allocated, it is
679 // locked when it is returned by the allocation function. It is our caller's
680 // responsibility to unlock the file handle (we do not unlock it before
681 // returning).
682 *pfh = _alloc_osfhnd();
683 if (*pfh == -1)
684 {
685 _doserrno = 0;
686 *pfh = -1;
687 errno = EMFILE;
688 return errno;
689 }
690
691 // Beyond this point, do not change *pfh, even if an error occurs. Our
692 // caller requires the handle in order to release its lock.
693 *punlock_flag = 1;
694
695
696
697 SECURITY_ATTRIBUTES security_attributes;
698 security_attributes.nLength = sizeof(security_attributes);
699 security_attributes.lpSecurityDescriptor = nullptr;
700 security_attributes.bInheritHandle = (oflag & _O_NOINHERIT) == 0;
701
702
703 // Try to open or create the file:
704 HANDLE os_handle = create_file(path, &security_attributes, options);
705 if (os_handle == INVALID_HANDLE_VALUE)
706 {
707 if ((options.access & GENERIC_READ_WRITE) == GENERIC_READ_WRITE && (oflag & _O_WRONLY))
708 {
709 // The call may have failed because we may be trying to open
710 // something for reading that does not allow reading (e.g. a pipe or
711 // a device). So, we try again with just GENERIC_WRITE. If this
712 // succeeds, we will have to assume the default encoding because we
713 // will have no way to read the BOM.
714 options.access &= ~GENERIC_READ;
715
716 os_handle = create_file(path, &security_attributes, options);
717 }
718 }
719
720 if (os_handle == INVALID_HANDLE_VALUE)
721 {
722 // We failed to open the file. We need to free the CRT file handle, but
723 // we do not release the lock--our caller releases the lock.
724 _osfile(*pfh) &= ~FOPEN;
725 __acrt_errno_map_os_error(GetLastError());
726 return errno;
727 }
728
729 // Find out what type of file this is (e.g., file, device, pipe, etc.)
730 DWORD const file_type = GetFileType(os_handle);
731
732 if (file_type == FILE_TYPE_UNKNOWN)
733 {
734 DWORD const last_error = GetLastError();
735 __acrt_errno_map_os_error(last_error);
736
737 _osfile(*pfh) &= ~FOPEN;
738 CloseHandle(os_handle);
739
740 // If GetFileType returns FILE_TYPE_UNKNOWN but doesn't fail, the file
741 // type really is unknown. This function is not designed to handle
742 // unknown types of files, so we must return an error.
743 if (last_error == ERROR_SUCCESS)
744 errno = EACCES;
745
746 return errno;
747 }
748
749 if (file_type == FILE_TYPE_CHAR)
750 {
751 options.crt_flags |= FDEV;
752 }
753 else if (file_type == FILE_TYPE_PIPE)
754 {
755 options.crt_flags |= FPIPE;
756 }
757
758 // The file is open and valid. Set the OS handle:
759 __acrt_lowio_set_os_handle(*pfh, reinterpret_cast<intptr_t>(os_handle));
760
761
762 // Mark the handle as open, and store the flags we gathered so far:
763 options.crt_flags |= FOPEN;
764 _osfile(*pfh) = options.crt_flags;
765
766
767 // The text mode is set to ANSI by default. If we find a BOM, then we will
768 // reset this to the appropriate type (this check happens below).
769 _textmode(*pfh) = __crt_lowio_text_mode::ansi;
770
771
772 // If the text mode file is opened for writing and allows reading, remove
773 // any trailing Ctrl+Z character, if present, to ensure appending works:
774 if (oflag & _O_RDWR)
775 {
776 errno_t const result = truncate_ctrl_z_if_present(*pfh);
777 if (result != 0)
778 {
779 _close_nolock(*pfh);
780 return result;
781 }
782 }
783
784 // Configure the text mode:
785 __crt_lowio_text_mode text_mode = __crt_lowio_text_mode::ansi;
786 errno_t const text_mode_result = configure_text_mode(*pfh, options, oflag, text_mode);
787 if (text_mode_result != 0)
788 {
789 _close_nolock(*pfh);
790 return text_mode_result;
791 }
792
793 _textmode(*pfh) = text_mode;
794 _tm_unicode(*pfh) = (oflag & _O_WTEXT) != 0;
795
796
797 // Set FAPPEND flag if appropriate. Don't do this for devices or pipes:
798 if ((options.crt_flags & (FDEV | FPIPE)) == 0 && (oflag & _O_APPEND))
799 _osfile(*pfh) |= FAPPEND;
800
801
802 // Finally, if we were asked only to open the file with write access but we
803 // opened it with read and write access in order to read the BOM, close the
804 // file and re-open it with only write access:
805 if ((options.access & GENERIC_READ_WRITE) == GENERIC_READ_WRITE && (oflag & _O_WRONLY))
806 {
807 CloseHandle(os_handle);
808 options.access &= ~GENERIC_READ;
809 os_handle = create_file(path, &security_attributes, options);
810
811 if (os_handle == INVALID_HANDLE_VALUE)
812 {
813 // Note that we can't use the normal close function here because the
814 // file isn't really open anymore. We need only release the file
815 // handle by unsetting the FOPEN flag:
816 __acrt_errno_map_os_error(GetLastError());
817 _osfile(*pfh) &= ~FOPEN;
818 _free_osfhnd(*pfh);
819 return errno;
820 }
821 else
822 {
823 // We were able to open the file successfully, set the file
824 // handle in the _ioinfo structure, then we are done. All
825 // the options.crt_flags should have been set properly already.
826 _osfhnd(*pfh) = reinterpret_cast<intptr_t>(os_handle);
827 }
828 }
829
830 return 0; // Success!
831 }
832
833
834
_sopen_nolock(int * const punlock_flag,int * const pfh,char const * const path,int const oflag,int const shflag,int const pmode,int const secure)835 extern "C" errno_t __cdecl _sopen_nolock(
836 int* const punlock_flag,
837 int* const pfh,
838 char const* const path,
839 int const oflag,
840 int const shflag,
841 int const pmode,
842 int const secure
843 )
844 {
845 // At this point we know path is not null already
846 __crt_internal_win32_buffer<wchar_t> wide_path;
847
848 errno_t const cvt = __acrt_mbs_to_wcs_cp(path, wide_path, __acrt_get_utf8_acp_compatibility_codepage());
849
850 if (cvt != 0) {
851 return -1;
852 }
853
854 return _wsopen_nolock(punlock_flag, pfh, wide_path.data(), oflag, shflag, pmode, secure);
855 }
856
857
858
_sopen(char const * const path,int const oflag,int const shflag,...)859 extern "C" int __cdecl _sopen(char const* const path, int const oflag, int const shflag, ...)
860 {
861 va_list ap;
862 va_start(ap, shflag);
863 int const pmode = va_arg(ap, int);
864 va_end(ap);
865
866 // The last argument is 0 so thta the pmode is not validated in open_s:
867 int fh = -1;
868 errno_t const result = _sopen_dispatch(path, oflag, shflag, pmode, &fh, FALSE);
869 return result ? -1 : fh;
870 }
871
_wsopen(wchar_t const * const path,int const oflag,int const shflag,...)872 extern "C" int __cdecl _wsopen(wchar_t const* const path, int const oflag, int const shflag, ...)
873 {
874 va_list ap;
875 va_start(ap, shflag);
876 int const pmode = va_arg(ap, int);
877 va_end(ap);
878
879 // The last argument is 0 so thta the pmode is not validated in open_s:
880 int fh = -1;
881 errno_t const result = _wsopen_dispatch(path, oflag, shflag, pmode, &fh, FALSE);
882 return result ? -1 : fh;
883 }
884
885
886
_sopen_s(int * const pfh,char const * const path,int const oflag,int const shflag,int const pmode)887 extern "C" errno_t __cdecl _sopen_s(
888 int* const pfh,
889 char const* const path,
890 int const oflag,
891 int const shflag,
892 int const pmode
893 )
894 {
895 // The last argument is 1 so that pmode is validated in open_s:
896 return _sopen_dispatch(path, oflag, shflag, pmode, pfh, TRUE);
897 }
898
_wsopen_s(int * const pfh,wchar_t const * const path,int const oflag,int const shflag,int const pmode)899 extern "C" errno_t __cdecl _wsopen_s(
900 int* const pfh,
901 wchar_t const* const path,
902 int const oflag,
903 int const shflag,
904 int const pmode
905 )
906 {
907 // The last argument is 1 so that pmode is validated in open_s:
908 return _wsopen_dispatch(path, oflag, shflag, pmode, pfh, TRUE);
909 }
910