@@ -404,7 +404,7 @@ int mingw_mkdir(const char *path, int mode)
404404 int ret ;
405405 wchar_t wpath [MAX_PATH ];
406406
407- if (!is_valid_win32_path (path )) {
407+ if (!is_valid_win32_path (path , 0 )) {
408408 errno = EINVAL ;
409409 return -1 ;
410410 }
@@ -490,21 +490,21 @@ int mingw_open (const char *filename, int oflags, ...)
490490 mode = va_arg (args , int );
491491 va_end (args );
492492
493- if (!is_valid_win32_path (filename )) {
493+ if (!is_valid_win32_path (filename , ! create )) {
494494 errno = create ? EINVAL : ENOENT ;
495495 return -1 ;
496496 }
497497
498- if (filename && !strcmp (filename , "/dev/null" ))
499- filename = "nul" ;
500-
501498 if ((oflags & O_APPEND ) && !is_local_named_pipe_path (filename ))
502499 open_fn = mingw_open_append ;
503500 else
504501 open_fn = _wopen ;
505502
506- if (xutftowcs_path (wfilename , filename ) < 0 )
503+ if (filename && !strcmp (filename , "/dev/null" ))
504+ wcscpy (wfilename , L"nul" );
505+ else if (xutftowcs_path (wfilename , filename ) < 0 )
507506 return -1 ;
507+
508508 fd = open_fn (wfilename , oflags , mode );
509509
510510 if (fd < 0 && (oflags & O_ACCMODE ) != O_RDONLY && errno == EACCES ) {
@@ -561,16 +561,18 @@ FILE *mingw_fopen (const char *filename, const char *otype)
561561 int hide = needs_hiding (filename );
562562 FILE * file ;
563563 wchar_t wfilename [MAX_PATH ], wotype [4 ];
564- if (!is_valid_win32_path (filename )) {
564+ if (filename && !strcmp (filename , "/dev/null" ))
565+ wcscpy (wfilename , L"nul" );
566+ else if (!is_valid_win32_path (filename , 1 )) {
565567 int create = otype && strchr (otype , 'w' );
566568 errno = create ? EINVAL : ENOENT ;
567569 return NULL ;
568- }
569- if (filename && !strcmp (filename , "/dev/null" ))
570- filename = "nul" ;
571- if (xutftowcs_path (wfilename , filename ) < 0 ||
572- xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
570+ } else if (xutftowcs_path (wfilename , filename ) < 0 )
573571 return NULL ;
572+
573+ if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
574+ return NULL ;
575+
574576 if (hide && !access (filename , F_OK ) && set_hidden_flag (wfilename , 0 )) {
575577 error ("could not unhide %s" , filename );
576578 return NULL ;
@@ -588,16 +590,18 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
588590 int hide = needs_hiding (filename );
589591 FILE * file ;
590592 wchar_t wfilename [MAX_PATH ], wotype [4 ];
591- if (!is_valid_win32_path (filename )) {
593+ if (filename && !strcmp (filename , "/dev/null" ))
594+ wcscpy (wfilename , L"nul" );
595+ else if (!is_valid_win32_path (filename , 1 )) {
592596 int create = otype && strchr (otype , 'w' );
593597 errno = create ? EINVAL : ENOENT ;
594598 return NULL ;
595- }
596- if (filename && !strcmp (filename , "/dev/null" ))
597- filename = "nul" ;
598- if (xutftowcs_path (wfilename , filename ) < 0 ||
599- xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
599+ } else if (xutftowcs_path (wfilename , filename ) < 0 )
600+ return NULL ;
601+
602+ if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
600603 return NULL ;
604+
601605 if (hide && !access (filename , F_OK ) && set_hidden_flag (wfilename , 0 )) {
602606 error ("could not unhide %s" , filename );
603607 return NULL ;
@@ -2529,14 +2533,16 @@ static void setup_windows_environment(void)
25292533 }
25302534}
25312535
2532- int is_valid_win32_path (const char * path )
2536+ int is_valid_win32_path (const char * path , int allow_literal_nul )
25332537{
2538+ const char * p = path ;
25342539 int preceding_space_or_period = 0 , i = 0 , periods = 0 ;
25352540
25362541 if (!protect_ntfs )
25372542 return 1 ;
25382543
25392544 skip_dos_drive_prefix ((char * * )& path );
2545+ goto segment_start ;
25402546
25412547 for (;;) {
25422548 char c = * (path ++ );
@@ -2551,7 +2557,83 @@ int is_valid_win32_path(const char *path)
25512557 return 1 ;
25522558
25532559 i = periods = preceding_space_or_period = 0 ;
2554- continue ;
2560+
2561+ segment_start :
2562+ switch (* path ) {
2563+ case 'a' : case 'A' : /* AUX */
2564+ if (((c = path [++ i ]) != 'u' && c != 'U' ) ||
2565+ ((c = path [++ i ]) != 'x' && c != 'X' )) {
2566+ not_a_reserved_name :
2567+ path += i ;
2568+ continue ;
2569+ }
2570+ break ;
2571+ case 'c' : case 'C' : /* COM<N>, CON, CONIN$, CONOUT$ */
2572+ if ((c = path [++ i ]) != 'o' && c != 'O' )
2573+ goto not_a_reserved_name ;
2574+ c = path [++ i ];
2575+ if (c == 'm' || c == 'M' ) { /* COM<N> */
2576+ if (!isdigit (path [++ i ]))
2577+ goto not_a_reserved_name ;
2578+ } else if (c == 'n' || c == 'N' ) { /* CON */
2579+ c = path [i + 1 ];
2580+ if ((c == 'i' || c == 'I' ) &&
2581+ ((c = path [i + 2 ]) == 'n' ||
2582+ c == 'N' ) &&
2583+ path [i + 3 ] == '$' )
2584+ i += 3 ; /* CONIN$ */
2585+ else if ((c == 'o' || c == 'O' ) &&
2586+ ((c = path [i + 2 ]) == 'u' ||
2587+ c == 'U' ) &&
2588+ ((c = path [i + 3 ]) == 't' ||
2589+ c == 'T' ) &&
2590+ path [i + 4 ] == '$' )
2591+ i += 4 ; /* CONOUT$ */
2592+ } else
2593+ goto not_a_reserved_name ;
2594+ break ;
2595+ case 'l' : case 'L' : /* LPT<N> */
2596+ if (((c = path [++ i ]) != 'p' && c != 'P' ) ||
2597+ ((c = path [++ i ]) != 't' && c != 'T' ) ||
2598+ !isdigit (path [++ i ]))
2599+ goto not_a_reserved_name ;
2600+ break ;
2601+ case 'n' : case 'N' : /* NUL */
2602+ if (((c = path [++ i ]) != 'u' && c != 'U' ) ||
2603+ ((c = path [++ i ]) != 'l' && c != 'L' ) ||
2604+ (allow_literal_nul &&
2605+ !path [i + 1 ] && p == path ))
2606+ goto not_a_reserved_name ;
2607+ break ;
2608+ case 'p' : case 'P' : /* PRN */
2609+ if (((c = path [++ i ]) != 'r' && c != 'R' ) ||
2610+ ((c = path [++ i ]) != 'n' && c != 'N' ))
2611+ goto not_a_reserved_name ;
2612+ break ;
2613+ default :
2614+ continue ;
2615+ }
2616+
2617+ /*
2618+ * So far, this looks like a reserved name. Let's see
2619+ * whether it actually is one: trailing spaces, a file
2620+ * extension, or an NTFS Alternate Data Stream do not
2621+ * matter, the name is still reserved if any of those
2622+ * follow immediately after the actual name.
2623+ */
2624+ i ++ ;
2625+ if (path [i ] == ' ' ) {
2626+ preceding_space_or_period = 1 ;
2627+ while (path [++ i ] == ' ' )
2628+ ; /* skip all spaces */
2629+ }
2630+
2631+ c = path [i ];
2632+ if (c && c != '.' && c != ':' && c != '/' && c != '\\' )
2633+ goto not_a_reserved_name ;
2634+
2635+ /* contains reserved name */
2636+ return 0 ;
25552637 case '.' :
25562638 periods ++ ;
25572639 /* fallthru */
0 commit comments