xref: /reactos/sdk/lib/ucrt/lowio/open.cpp (revision fe11f7a2)
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>
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 
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 
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>
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 
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 
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 
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 
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 
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 
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 
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 
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.
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.
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 
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 
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 
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 
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 
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 
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