@@ -621,7 +621,7 @@ int mingw_mkdir(const char *path, int mode)
621
621
int ret ;
622
622
wchar_t wpath [MAX_LONG_PATH ];
623
623
624
- if (!is_valid_win32_path (path )) {
624
+ if (!is_valid_win32_path (path , 0 )) {
625
625
errno = EINVAL ;
626
626
return -1 ;
627
627
}
@@ -723,21 +723,21 @@ int mingw_open (const char *filename, int oflags, ...)
723
723
mode = va_arg (args , int );
724
724
va_end (args );
725
725
726
- if (!is_valid_win32_path (filename )) {
726
+ if (!is_valid_win32_path (filename , ! create )) {
727
727
errno = create ? EINVAL : ENOENT ;
728
728
return -1 ;
729
729
}
730
730
731
- if (filename && !strcmp (filename , "/dev/null" ))
732
- filename = "nul" ;
733
-
734
731
if ((oflags & O_APPEND ) && !is_local_named_pipe_path (filename ))
735
732
open_fn = mingw_open_append ;
736
733
else
737
734
open_fn = _wopen ;
738
735
739
- if (xutftowcs_long_path (wfilename , filename ) < 0 )
736
+ if (filename && !strcmp (filename , "/dev/null" ))
737
+ wcscpy (wfilename , L"nul" );
738
+ else if (xutftowcs_long_path (wfilename , filename ) < 0 )
740
739
return -1 ;
740
+
741
741
fd = open_fn (wfilename , oflags , mode );
742
742
743
743
if (fd < 0 && (oflags & O_ACCMODE ) != O_RDONLY && errno == EACCES ) {
@@ -794,16 +794,18 @@ FILE *mingw_fopen (const char *filename, const char *otype)
794
794
int hide = needs_hiding (filename );
795
795
FILE * file ;
796
796
wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
797
- if (!is_valid_win32_path (filename )) {
797
+ if (filename && !strcmp (filename , "/dev/null" ))
798
+ wcscpy (wfilename , L"nul" );
799
+ else if (!is_valid_win32_path (filename , 1 )) {
798
800
int create = otype && strchr (otype , 'w' );
799
801
errno = create ? EINVAL : ENOENT ;
800
802
return NULL ;
801
- }
802
- if (filename && !strcmp (filename , "/dev/null" ))
803
- filename = "nul" ;
804
- if (xutftowcs_long_path (wfilename , filename ) < 0 ||
805
- xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
803
+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
806
804
return NULL ;
805
+
806
+ if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
807
+ return NULL ;
808
+
807
809
if (hide && !access (filename , F_OK ) && set_hidden_flag (wfilename , 0 )) {
808
810
error ("could not unhide %s" , filename );
809
811
return NULL ;
@@ -821,16 +823,18 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
821
823
int hide = needs_hiding (filename );
822
824
FILE * file ;
823
825
wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
824
- if (!is_valid_win32_path (filename )) {
826
+ if (filename && !strcmp (filename , "/dev/null" ))
827
+ wcscpy (wfilename , L"nul" );
828
+ else if (!is_valid_win32_path (filename , 1 )) {
825
829
int create = otype && strchr (otype , 'w' );
826
830
errno = create ? EINVAL : ENOENT ;
827
831
return NULL ;
828
- }
829
- if (filename && !strcmp (filename , "/dev/null" ))
830
- filename = "nul" ;
831
- if (xutftowcs_long_path (wfilename , filename ) < 0 ||
832
- xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
832
+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
833
+ return NULL ;
834
+
835
+ if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
833
836
return NULL ;
837
+
834
838
if (hide && !access (filename , F_OK ) && set_hidden_flag (wfilename , 0 )) {
835
839
error ("could not unhide %s" , filename );
836
840
return NULL ;
@@ -3191,14 +3195,16 @@ static void setup_windows_environment(void)
3191
3195
setenv ("LC_CTYPE" , "C" , 1 );
3192
3196
}
3193
3197
3194
- int is_valid_win32_path (const char * path )
3198
+ int is_valid_win32_path (const char * path , int allow_literal_nul )
3195
3199
{
3200
+ const char * p = path ;
3196
3201
int preceding_space_or_period = 0 , i = 0 , periods = 0 ;
3197
3202
3198
3203
if (!protect_ntfs )
3199
3204
return 1 ;
3200
3205
3201
3206
skip_dos_drive_prefix ((char * * )& path );
3207
+ goto segment_start ;
3202
3208
3203
3209
for (;;) {
3204
3210
char c = * (path ++ );
@@ -3213,7 +3219,83 @@ int is_valid_win32_path(const char *path)
3213
3219
return 1 ;
3214
3220
3215
3221
i = periods = preceding_space_or_period = 0 ;
3216
- continue ;
3222
+
3223
+ segment_start :
3224
+ switch (* path ) {
3225
+ case 'a' : case 'A' : /* AUX */
3226
+ if (((c = path [++ i ]) != 'u' && c != 'U' ) ||
3227
+ ((c = path [++ i ]) != 'x' && c != 'X' )) {
3228
+ not_a_reserved_name :
3229
+ path += i ;
3230
+ continue ;
3231
+ }
3232
+ break ;
3233
+ case 'c' : case 'C' : /* COM<N>, CON, CONIN$, CONOUT$ */
3234
+ if ((c = path [++ i ]) != 'o' && c != 'O' )
3235
+ goto not_a_reserved_name ;
3236
+ c = path [++ i ];
3237
+ if (c == 'm' || c == 'M' ) { /* COM<N> */
3238
+ if (!isdigit (path [++ i ]))
3239
+ goto not_a_reserved_name ;
3240
+ } else if (c == 'n' || c == 'N' ) { /* CON */
3241
+ c = path [i + 1 ];
3242
+ if ((c == 'i' || c == 'I' ) &&
3243
+ ((c = path [i + 2 ]) == 'n' ||
3244
+ c == 'N' ) &&
3245
+ path [i + 3 ] == '$' )
3246
+ i += 3 ; /* CONIN$ */
3247
+ else if ((c == 'o' || c == 'O' ) &&
3248
+ ((c = path [i + 2 ]) == 'u' ||
3249
+ c == 'U' ) &&
3250
+ ((c = path [i + 3 ]) == 't' ||
3251
+ c == 'T' ) &&
3252
+ path [i + 4 ] == '$' )
3253
+ i += 4 ; /* CONOUT$ */
3254
+ } else
3255
+ goto not_a_reserved_name ;
3256
+ break ;
3257
+ case 'l' : case 'L' : /* LPT<N> */
3258
+ if (((c = path [++ i ]) != 'p' && c != 'P' ) ||
3259
+ ((c = path [++ i ]) != 't' && c != 'T' ) ||
3260
+ !isdigit (path [++ i ]))
3261
+ goto not_a_reserved_name ;
3262
+ break ;
3263
+ case 'n' : case 'N' : /* NUL */
3264
+ if (((c = path [++ i ]) != 'u' && c != 'U' ) ||
3265
+ ((c = path [++ i ]) != 'l' && c != 'L' ) ||
3266
+ (allow_literal_nul &&
3267
+ !path [i + 1 ] && p == path ))
3268
+ goto not_a_reserved_name ;
3269
+ break ;
3270
+ case 'p' : case 'P' : /* PRN */
3271
+ if (((c = path [++ i ]) != 'r' && c != 'R' ) ||
3272
+ ((c = path [++ i ]) != 'n' && c != 'N' ))
3273
+ goto not_a_reserved_name ;
3274
+ break ;
3275
+ default :
3276
+ continue ;
3277
+ }
3278
+
3279
+ /*
3280
+ * So far, this looks like a reserved name. Let's see
3281
+ * whether it actually is one: trailing spaces, a file
3282
+ * extension, or an NTFS Alternate Data Stream do not
3283
+ * matter, the name is still reserved if any of those
3284
+ * follow immediately after the actual name.
3285
+ */
3286
+ i ++ ;
3287
+ if (path [i ] == ' ' ) {
3288
+ preceding_space_or_period = 1 ;
3289
+ while (path [++ i ] == ' ' )
3290
+ ; /* skip all spaces */
3291
+ }
3292
+
3293
+ c = path [i ];
3294
+ if (c && c != '.' && c != ':' && c != '/' && c != '\\' )
3295
+ goto not_a_reserved_name ;
3296
+
3297
+ /* contains reserved name */
3298
+ return 0 ;
3217
3299
case '.' :
3218
3300
periods ++ ;
3219
3301
/* fallthru */
0 commit comments