1 /*===========================================================================
2 *
3 *                            PUBLIC DOMAIN NOTICE
4 *               National Center for Biotechnology Information
5 *
6 *  This software/database is a "United States Government Work" under the
7 *  terms of the United States Copyright Act.  It was written as part of
8 *  the author's official duties as a United States Government employee and
9 *  thus cannot be copyrighted.  This software/database is freely available
10 *  to the public for use. The National Library of Medicine and the U.S.
11 *  Government have not placed any restriction on its use or reproduction.
12 *
13 *  Although all reasonable efforts have been taken to ensure the accuracy
14 *  and reliability of the software and data, the NLM and the U.S.
15 *  Government do not and cannot warrant the performance or results that
16 *  may be obtained by using this software or data. The NLM and the U.S.
17 *  Government disclaim all warranties, express or implied, including
18 *  warranties of performance, merchantability or fitness for any particular
19 *  purpose.
20 *
21 *  Please cite the author in any work or product based on this material.
22 *
23 * ===========================================================================
24 *
25 */
26 
27 #include <kfs/extern.h>
28 
29 /*--------------------------------------------------------------------------
30  * forwards
31  */
32 struct KSysDir;
33 
34 #define UNICODE 1
35 #define _UNICODE 1
36 
37 #define KDIR_IMPL struct KSysDir
38 
39 #include "sysfile-priv.h"
40 #include <klib/namelist.h>
41 #include <klib/text.h>
42 #include <klib/rc.h>
43 #include <klib/log.h>
44 #include <klib/out.h>
45 #include <klib/debug.h>
46 #include <klib/klib-priv.h>
47 #include <klib/time.h>
48 
49 #include <sysalloc.h>
50 
51 #include <stdio.h>
52 #include <wchar.h>
53 #include <stdarg.h>
54 #include <assert.h>
55 #include <WINDOWS.H>
56 #include <WINNT.H>
57 
58 #include "lnk_tools.c"
59 
60 #include <os-native.h>
61 
62 #ifndef IO_REPARSE_TAG_SYMLINK
63 #define IO_REPARSE_TAG_SYMLINK 0xA000000C
64 #endif
65 
66 
67 /* Missing functions from our text library
68  * size is bytes; max_chars is number of elements
69  */
70 
71 /* utf16_utf32
72  *  converts UTF16 text to a single UTF32 character
73  *  returns the number of UTF16 words consumed, such that:
74  *    return > 0 means success
75  *    return == 0 means insufficient input
76  *    return < 0 means bad input or bad argument
77  */
78 static
utf16_utf32(uint32_t * dst,const wchar_t * begin,const wchar_t * end)79 int utf16_utf32 ( uint32_t *dst, const wchar_t *begin, const wchar_t *end )
80 {
81     uint32_t ch;
82 
83     if ( dst == NULL || begin == NULL || end == NULL )
84         return -1;
85 
86     if ( begin == end )
87         return 0;
88 
89     /* windows utf16 */
90 
91     ch = (uint32_t)(begin [0]);
92 
93     if ((ch < 0xD800) || (ch <= 0xE000))
94     {
95         *dst = ch;
96         return 1;
97     }
98     else
99     {
100         uint32_t ch;
101 
102         /* need at least 2 words */
103         if (begin >= end)
104             return -1;
105 
106         /* extreme checks */
107         if (((begin[0] & 0xFC00) != 0xD8) ||
108             ((begin[1] & 0xFC00) != 0xDC))
109             return -1;
110 
111         ch = (begin[0] & 0x03FF) << 10 |
112             (begin[1] & 0x03FF);
113         return 2;
114     }
115 }
116 
117 
118 /* utf32_utf16
119  *  converts a single UTF32 character to UTF16 text
120  *  returns the number of UTF16 words generated, such that:
121  *    return > 0 means success
122  *    return == 0 means insufficient output
123  *    return < 0 means bad character or bad argument
124  */
125 static
utf32_utf16(wchar_t * begin,wchar_t * end,uint32_t ch)126 int utf32_utf16 ( wchar_t *begin, wchar_t *end, uint32_t ch )
127 {
128     if (ch < 0x10000)
129     {
130         if ((ch <= 0xDFFF) && (ch >= 0xD800))
131             return -1;
132 
133         begin[0] = (uint16_t)ch;
134         return 1;
135     }
136     else if ((ch >= 0x10FFFF) || (end <= begin))
137         return -1;
138     else
139     {
140         uint32_t cch;
141 
142         cch = ch - 0x10000;
143         /* cch <= 0xFFFFF since ch < 0x10FFFF */
144 
145         begin[0] = 0xD800 | (cch >> 10); /* upper 10 bits */
146         begin[1] = 0xDC00 | (cch & 0x3FF); /* lower 10 bita */
147         return 2;
148     }
149 }
150 
151 
wstrcase_cmp(const wchar_t * a,size_t asize,const wchar_t * b,size_t bsize,uint32_t max_chars)152 static int wstrcase_cmp (const wchar_t * a, size_t asize,
153                          const wchar_t * b, size_t bsize,
154                          uint32_t max_chars)
155 {
156     uint32_t num_chars;
157     const wchar_t *aend, *bend;
158 
159     assert ( a != NULL && b != NULL );
160 
161     /* set up end limit triggers */
162     aend = a + asize;
163     bend = b + bsize;
164 
165     num_chars = 0;
166 
167     while ( a < aend && b < bend )
168     {
169         uint32_t ach, bch;
170 
171         /* read a character from a */
172         int len = utf16_utf32 ( & ach, a, aend );
173         if ( len <= 0 )
174         {
175             asize -= ( size_t ) ( aend - a );
176             break;
177         }
178         a += len;
179 
180         /* read a character from b */
181         len = utf16_utf32 ( & bch, b, bend );
182         if ( len <= 0 )
183         {
184             bsize -= ( size_t ) ( bend - b );
185             break;
186         }
187         b += len;
188 
189         /* compare characters with case */
190         if ( ach != bch )
191         {
192             /* only go lower case if they differ */
193             ach = towlower ( ( wint_t ) ach );
194             bch = towlower ( ( wint_t ) bch );
195 
196             if ( ach != bch )
197             {
198                 if ( ach < bch )
199                     return -1;
200                 return 1;
201             }
202         }
203 
204         /* if char count is sufficient, we're done */
205         if ( ++ num_chars == max_chars )
206             return 0;
207     }
208 
209     /* one or both reached end < max_chars */
210     if (asize < bsize)
211         return -1;
212     return asize > bsize;
213 }
214 
215 /*--------------------------------------------------------------------------
216  * KSysDirEnum
217  *  a Windows directory enumerator
218  */
219 typedef struct KSysDirEnum KSysDirEnum;
220 struct KSysDirEnum
221 {
222     HANDLE handle;
223     WIN32_FIND_DATAW fd;
224     int found;
225     bool first;
226 };
227 
228 /* Whack
229  */
230 static
KSysDirEnumWhack(KSysDirEnum * self)231 void KSysDirEnumWhack ( KSysDirEnum *self )
232 {
233     FindClose( self->handle );
234 }
235 
236 /* Init
237  */
238 static
KSysDirEnumInit(KSysDirEnum * self,const wchar_t * path)239 rc_t KSysDirEnumInit ( KSysDirEnum *self, const wchar_t *path )
240 {
241     uint32_t err;
242     rc_t rc;
243 
244     self -> first = true;
245     self -> handle = FindFirstFileW ( path, & self -> fd );
246     if ( self -> handle != INVALID_HANDLE_VALUE )
247     {
248         self -> found = 1;
249         return 0;
250     }
251 
252     self -> found = 0;
253     err = GetLastError ();
254     switch ( err )
255     {
256     case ERROR_FILE_NOT_FOUND:
257     case ERROR_PATH_NOT_FOUND:
258         rc = RC ( rcFS, rcDirectory, rcListing, rcPath, rcNotFound );
259         break;
260     default :
261         rc = RC ( rcFS, rcDirectory, rcListing, rcNoObj, rcUnknown );
262     }
263 
264     PLOGERR ( klogInfo,
265              ( klogInfo, rc, "error FindFirstFileW - $(E) - $(C)",
266               "E=%!,C=%u", err, err ) );
267 
268     return rc;
269 }
270 
271 
272 static
KSysDirEnumInitAll(KSysDirEnum * self,wchar_t * path,uint32_t path_length)273 rc_t KSysDirEnumInitAll ( KSysDirEnum *self, wchar_t *path, uint32_t path_length )
274 {
275     /* prepare the path for KSysDirEnumInit() */
276     path [ path_length + 0 ] = '\\';
277     path [ path_length + 1 ] = '*';
278     path [ path_length + 2 ] = '.';
279     path [ path_length + 3 ] = '*';
280     path [ path_length + 4 ] = 0;
281 
282     return KSysDirEnumInit ( self, path );
283 }
284 
285 /* Next
286  */
287 static
KSysDirEnumNext(const KSysDirEnum * cself)288 const wchar_t *KSysDirEnumNext ( const KSysDirEnum *cself )
289 {
290     KSysDirEnum* self = (KSysDirEnum*)cself;
291 
292     while( self->found )
293     {
294         if ( self -> first )
295             self -> first = false;
296         else
297             self->found = FindNextFileW( self->handle, &self->fd );
298 
299         if ( self->found )
300         {
301             /* filter out the '.' and '..' entries */
302             if ( self ->fd.cFileName[ 0 ] == '.' )
303             {
304                 switch ( self->fd.cFileName[ 1 ] )
305                 {
306                 case 0:
307                     continue;
308                 case '.':
309                     if ( self->fd.cFileName[ 2 ] == 0 )
310                         continue;
311                     break;
312                 }
313             }
314             return self->fd.cFileName;
315         }
316     }
317 
318     return NULL;
319 }
320 
321 
322 /*--------------------------------------------------------------------------
323  * KSysDirListing
324  *  a Windows directory listing
325  */
326 typedef VNamelist KSysDirListing;
327 
328 static
KSysDirListingSort(const void * a,const void * b)329 int KSysDirListingSort ( const void *a, const void *b )
330 {
331     size_t A,B,M;
332     A = wchar_string_size (a);
333     B = wchar_string_size (b);
334     /* close enough for max chars? */
335     M = (A>B) ? A : B;
336 
337     return wstrcase_cmp (a, A, b, B, ( uint32_t ) M);
338 }
339 
340 static
KSysDirListingInit(KSysDirListing * self,const wchar_t * path,const KDirectory * dir,bool (CC * f)(const KDirectory *,const char *,void *),void * data)341 rc_t KSysDirListingInit ( KSysDirListing *self, const wchar_t *path, const KDirectory *dir,
342     bool ( CC * f ) ( const KDirectory*, const char*, void* ), void *data )
343 {
344     KSysDirEnum list;
345     rc_t rc = KSysDirEnumInit ( & list, path );
346     if ( rc == 0 )
347     {
348         const wchar_t *name;
349         char utf8_name[ MAX_PATH ];
350         size_t utf8_size, utf16_size;
351 
352         while ( ( name = KSysDirEnumNext ( & list ) ) != NULL )
353         {
354             utf16_size = wchar_string_size ( name );
355             utf8_size = wchar_cvt_string_copy ( utf8_name, sizeof( utf8_name ), name, utf16_size );
356             if ( utf8_size >= sizeof( utf8_name ) )
357             {
358                 rc = RC(rcFS, rcDirectory, rcListing, rcName, rcExcessive );
359                 break;
360             }
361 
362             if ( f != NULL )
363             {
364                 if ( ! ( * f ) ( dir, utf8_name, data ) )
365                     continue;
366             }
367 
368             rc = VNamelistAppend( self, utf8_name );
369             if ( rc != 0 )
370             {
371                 break;
372             }
373 
374         }
375 
376         KSysDirEnumWhack ( & list );
377     }
378     return rc;
379 }
380 
381 /*--------------------------------------------------------------------------
382  * KSysDir
383  *  a Windows directory
384  */
385 typedef struct KSysDir KSysDir;
386 struct KSysDir
387 {
388     KDirectory dad;
389     uint32_t root;
390     uint32_t length;
391     wchar_t path [ MAX_PATH ];
392 };
393 
394 
395 /* helper function to translate a windows-error-code into rc-code */
396 static
translate_file_error(DWORD error,enum RCContext ctx)397 rc_t translate_file_error( DWORD error, enum RCContext ctx )
398 {
399     rc_t rc;
400     switch ( error )
401     {
402         case ERROR_FILE_NOT_FOUND :
403         case ERROR_PATH_NOT_FOUND :
404         case ERROR_INVALID_DRIVE :
405             rc = RC ( rcFS, rcDirectory, ctx, rcPath, rcNotFound ); break;
406 
407         case ERROR_ALREADY_EXISTS:
408         case ERROR_FILE_EXISTS :
409             rc = RC ( rcFS, rcDirectory, ctx, rcPath, rcExists ); break;
410 
411     /*    case ERROR_PATH_NOT_FOUND : */
412         case ERROR_INVALID_NAME :
413         case ERROR_BAD_PATHNAME :
414             rc = RC ( rcFS, rcDirectory, ctx, rcPath, rcInvalid ); break;
415 
416         case ERROR_ACCESS_DENIED :
417         case ERROR_INVALID_ACCESS :
418         case ERROR_SHARING_VIOLATION :
419         case ERROR_LOCK_VIOLATION :
420         case ERROR_PATH_BUSY :
421         case ERROR_WRITE_PROTECT :
422         case ERROR_DELETE_PENDING :
423             rc = RC ( rcFS, rcDirectory, ctx, rcDirectory, rcUnauthorized ); break;
424 
425         case ERROR_NOT_ENOUGH_MEMORY :
426         case ERROR_OUTOFMEMORY :
427             rc = RC ( rcFS, rcDirectory, ctx, rcMemory, rcExhausted ); break;
428 
429         case ERROR_TOO_MANY_OPEN_FILES :
430             rc = RC ( rcFS, rcDirectory, ctx, rcFileDesc, rcExhausted ); break;
431 
432         case ERROR_HANDLE_DISK_FULL :
433             rc = RC ( rcFS, rcDirectory, ctx, rcStorage, rcExhausted ); break;
434 
435         case ERROR_BUFFER_OVERFLOW :
436         case ERROR_FILENAME_EXCED_RANGE :
437             rc = RC ( rcFS, rcDirectory, ctx, rcPath, rcExcessive );
438 
439         default : RC ( rcFS, rcDirectory, ctx, rcNoObj, rcUnknown );
440     }
441     return rc;
442 }
443 
444 
445 /* helper */
446 
print_error_for(DWORD error,const wchar_t * path,const char * function,enum RCContext ctx,KLogLevel level)447 static rc_t print_error_for( DWORD error, const wchar_t * path, const char * function, enum RCContext ctx, KLogLevel level )
448 {
449     rc_t rc = translate_file_error( error, ctx );
450 #if _DEBUGGING
451     char buffer[ 4096 ];
452     size_t src_size, dst_size, len;
453     wchar_cvt_string_measure ( path, &src_size, &dst_size );
454     len = wchar_cvt_string_copy ( buffer, sizeof buffer, path, src_size );
455     buffer[ len ] = 0;
456     PLOGERR ( level,
457               ( level, rc, "error $(F) - $(E) - $(C) for $(D)",
458                 "F=%s,E=%!,C=%u,D=%s", function, error, error, buffer ) );
459 #endif
460     return rc;
461 }
462 
463 
wchar_2_char(const wchar_t * path,char * buffer,size_t buflen)464 static void wchar_2_char( const wchar_t * path, char * buffer, size_t buflen )
465 {
466     size_t src_size, dst_size, len;
467     wchar_cvt_string_measure ( path, &src_size, &dst_size );
468     len = wchar_cvt_string_copy ( buffer, buflen, path, src_size );
469     buffer[ len ] = 0;
470 }
471 
472 
473 static
KSysDirPathTypeFromFindData(WIN32_FIND_DATA * find_data,const wchar_t * path,const uint32_t type)474 uint32_t KSysDirPathTypeFromFindData ( WIN32_FIND_DATA *find_data,
475                                        const wchar_t * path,
476                                        const uint32_t type )
477 {
478     uint32_t res = type;
479 
480     if( ( find_data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) != 0 )
481     {
482         res = kptDir;
483     }
484     else if ( ( find_data->dwFileAttributes & FILE_ATTRIBUTE_DEVICE ) != 0 )
485     {
486         res = kptCharDev;
487     }
488 
489     /* add in alias bit */
490     if ( ( find_data->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT ) != 0 )
491     {
492         if ( ( find_data->dwReserved0 & IO_REPARSE_TAG_SYMLINK ) != 0 )
493             res |= kptAlias;
494     }
495     else
496     {
497         if ( has_lnk_extension( path ) ) /* lnk_tools.c */
498             if ( lnk_file_validate( path ) ) /* lnk_tools.c */
499                 res |= kptAlias;
500     }
501     return res;
502 }
503 
504 static
KSysDirResolvePathAndDetectPathType(const wchar_t * path)505 uint32_t KSysDirResolvePathAndDetectPathType ( const wchar_t *path )
506 {
507     uint32_t res = kptNotFound;
508     wchar_t *resolved;
509     if ( win_resolve_path( path, &resolved, 1 ) ) /* lnk_tools.c */
510     {
511         WIN32_FIND_DATA find_data;
512         HANDLE f_findfile = FindFirstFileW( resolved, &find_data );
513         if ( f_findfile != INVALID_HANDLE_VALUE )
514         {
515             FindClose( f_findfile );
516             res = KSysDirPathTypeFromFindData ( &find_data, resolved, kptFile );
517         }
518         else
519         {
520             uint32_t err = GetLastError();
521             switch( err )
522             {
523             case ERROR_BAD_NETPATH:
524             case ERROR_BAD_NET_NAME:
525                 /* see if the netpath is a server
526                    NB - our special wcsdup allocated extra space for this */
527                 wcscat ( resolved, L"\\*" );
528                 f_findfile = FindFirstFileW( resolved, &find_data );
529                 if ( f_findfile != INVALID_HANDLE_VALUE )
530                 {
531                     FindClose ( f_findfile );
532                     res = kptDir;
533                     break;
534                 }
535                 /* no break */
536             case ERROR_FILE_NOT_FOUND:
537             case ERROR_PATH_NOT_FOUND:
538                 res = kptNotFound;
539                 break;
540 
541             default:
542                 res = kptBadPath;
543             }
544         }
545         free( resolved );
546     }
547     return res;
548 }
549 
550 /* KSysDirPathType
551  *  returns a KPathType
552  *
553  *  "path" [ IN ] - NUL terminated string in directory-native character set
554  */
555 static
KSysDirFullFSPathType(const wchar_t * path)556 uint32_t KSysDirFullFSPathType ( const wchar_t * path )
557 {
558     WIN32_FIND_DATA find_data;
559     HANDLE f_findfile = FindFirstFileW( path, &find_data );
560     if ( f_findfile == INVALID_HANDLE_VALUE )
561     {
562         DWORD status = GetLastError ();
563         switch( status )
564         {
565         case ERROR_FILE_NOT_FOUND:
566         case ERROR_PATH_NOT_FOUND:
567         case ERROR_BAD_NETPATH:
568         case ERROR_BAD_NET_NAME:
569             /* try to follow the path, section by section
570                if a section cannot be found try to resolve it as
571                MS-shell-link ( .lnk file ) */
572             return KSysDirResolvePathAndDetectPathType ( path );
573         default:
574             DBGMSG ( DBG_KFS, DBG_FLAG_ANY, ( "FindFirstFileW: WARNING - unrecognized return code - %u.\n", status ) );
575             print_error_for( status, path, "FindFirstFileW", rcResolving, klogErr );
576             return kptBadPath;
577         }
578     }
579     FindClose( f_findfile );
580     return KSysDirPathTypeFromFindData ( &find_data, path, kptFile );
581 }
582 
583 
584 static
KSysDirFullPathType(const wchar_t * path)585 uint32_t KSysDirFullPathType ( const wchar_t *path )
586 {
587     /* recognize of odd, POSIX'ish patterns and handle them directly */
588     if ( path [ 0 ] == '/' || path [ 0 ] == '\\' )
589     {
590         /* looking exactly for "root" */
591         if ( path [ 1 ] == 0 )
592             return kptFakeRoot;
593     }
594 
595 	/* regognize this 'c:\' as a valid path...*/
596 	if ( iswalpha( path[ 0 ] ) && path [ 1 ] == ':' && path [ 2 ] == '\\' && path[ 3 ] == 0 )
597 	{
598 		uint32_t path_type = kptBadPath;
599 		uint32_t mask = 0;
600 		if ( path[ 0 ] >= 'A' && path[ 0 ] <= 'Z' )
601 		{
602 			mask = ( 1 << ( path[ 0 ] - 'A' ) );
603 		}
604 		else if ( path[ 0 ] >= 'a' && path[ 0 ] <= 'z' )
605 		{
606 			mask = ( 1 << ( path[ 0 ] - 'a' ) );
607 		}
608 		if ( mask > 0 )
609 		{
610 			DWORD drivebitmask = GetLogicalDrives();	/* each logical drive has its own bit set */
611 			if ( ( drivebitmask & mask ) == mask )
612 				path_type = kptDir;
613 		}
614 		return path_type;
615 	}
616 
617     /* let the file system tell us */
618     return KSysDirFullFSPathType ( path );
619 }
620 
621 
622 /* KSysDirMake
623  *  allocate an uninialized object
624  */
625 static
KSysDirMake(size_t path_size)626 KSysDir *KSysDirMake ( size_t path_size )
627 {
628     KSysDir *dir = malloc ( sizeof *dir - sizeof dir->path +
629         4 * sizeof dir -> path [ 0 ] + path_size );
630     return dir;
631 }
632 
633 
634 /* KSysDirDestroy
635  */
636 static
KSysDirDestroy(KSysDir * self)637 rc_t CC KSysDirDestroy ( KSysDir *self )
638 {
639     free ( self );
640     return 0;
641 }
642 
643 /* KSysDirCanonPath
644  */
645 static
KSysDirCanonPath(const KSysDir * self,enum RCContext ctx,wchar_t * path,uint32_t path_length)646 rc_t KSysDirCanonPath ( const KSysDir *self, enum RCContext ctx, wchar_t *path, uint32_t path_length )
647 {
648     wchar_t *low, *dst, *last, *end = path + path_length;
649 
650     if ( self -> root != 0 )
651         low = path + self -> root;
652     else if ( path [ 1 ] == ':' )
653         low = path + 2;
654     else
655         low = path;
656     dst = last = low;
657 
658     while( 1 )
659     {
660         wchar_t *src = wcschr ( last + 1, '\\' );
661         if ( src == NULL )
662             src = end;
663 
664         /* detect special sequences */
665         switch ( src - last )
666         {
667         case 1:
668             if ( last [ 1 ] == '\\' && last != path ) /* keep leading double slash */
669             {
670                 /* "\\\\" -> "\\" */
671                 last = src;
672             }
673             break;
674 
675         case 2:
676             if ( last [ 1 ] == '.' )
677             {
678                 /* skip over */
679                 last = src;
680                 if ( src != end )
681                     continue;
682             }
683             break;
684 
685         case 3:
686             if ( last [ 1 ] == '.' && last [ 2 ] == '.' )
687             {
688                 /* remove previous leaf in path */
689                 dst [ 0 ] = 0;
690                 dst = wcsrchr ( path, '\\' );
691                 if ( dst == NULL || dst < low )
692                     return RC( rcFS, rcDirectory, ctx, rcPath, rcInvalid );
693 
694                 last = src;
695                 if ( src != end )
696                     continue;
697             }
698             break;
699         }
700 
701         /* if rewriting, copy leaf */
702         assert ( src >= last );
703 
704         /* if rewriting, copy leaf */
705         if ( dst != last )
706             memmove ( dst, last, ( src - last ) * sizeof * dst );
707 
708         /* move destination ahead */
709         dst += src - last;
710 
711         /* if we're done, go */
712         if ( src == end )
713             break;
714 
715         /* find next separator */
716         last = src;
717     }
718 
719     /* NUL terminate if modified */
720     if ( dst != end )
721         *dst = 0;
722 
723     return 0;
724 }
725 
726 /* KSysDirMakePath
727  *  creates a full path from partial
728 
729  self ....... has the first (base) part of the path in wchar_t !!!
730  canon ...... if true the assembled path will be "canonilized" as last step
731  buffer ..... into this buffer the full-path will be assembled ( wchar_t !!! )
732  path_max ... the size of the buffer in bytes
733  path ....... the partial path in utf8, can contain string-subst-elements !!!
734  args ....... arguments to construct the partial path in utf8 ( can be NULL )
735  */
736 static
KSysDirMakeSimulatedFSPath(const KSysDir * self,enum RCContext ctx,bool canon,wchar_t * buffer,size_t path_max,const char * path,va_list args,bool fake_posix)737 rc_t KSysDirMakeSimulatedFSPath ( const KSysDir* self, enum RCContext ctx, bool canon,
738     wchar_t *buffer, size_t path_max, const char *path, va_list args, bool fake_posix )
739 {
740     int temp_size_in_bytes;
741     uint32_t i, temp_length_in_utf8_chars;
742     uint32_t buffer_length_in_wchars;
743     char temp_utf8_buffer [ MAX_PATH ];
744 
745     /* check if the given partial path is not NULL and not empty */
746     if( path == NULL )
747         return RC( rcFS, rcDirectory, ctx, rcPath, rcNull );
748     if ( path [ 0 ] == 0 )
749         return RC( rcFS, rcDirectory, ctx, rcPath, rcInvalid );
750 
751     /* We construct in temp_utf8_buffer the relative path the user has given:
752     If there are no args we copy with _snprintf else we use vsnprintf,
753     !!! the args are always utf8, the given path is in utf8 !!!
754     that is the reason for the temporary utf8-buffer */
755     temp_size_in_bytes = ( args == NULL ) ?
756         _snprintf ( temp_utf8_buffer, sizeof temp_utf8_buffer, "%s", path ):
757         vsnprintf( temp_utf8_buffer, sizeof temp_utf8_buffer, path, args );
758 
759     /* we check if _snprnitf/vsnprintf was sucessful */
760     if ( temp_size_in_bytes < 0 || temp_size_in_bytes >= sizeof temp_utf8_buffer )
761         return RC( rcFS, rcDirectory, ctx, rcPath, rcExcessive );
762 
763     /* we measure the number of utf8-chars we have in our temp-buffer
764         only for international chars in the temp-buffer there will be
765         path_length_in_utf8_chars != path_size_in_bytes */
766     temp_length_in_utf8_chars = string_len ( temp_utf8_buffer, temp_size_in_bytes );
767 
768     /* normally we don't receive native Windows paths here.
769        but there is a use below ( when creating native directory )
770        that feeds a Windows path, so deal with it here. */
771     if ( ( isalpha ( temp_utf8_buffer [ 0 ] ) && temp_utf8_buffer [ 1 ] == ':' ) ||
772          ( temp_utf8_buffer [ 0 ] == '\\' && temp_utf8_buffer [ 1 ] == '\\' )    ||
773          ( temp_utf8_buffer [ 0 ] == '/' && temp_utf8_buffer [ 1 ] == '/' ) )
774     {
775     /* in the case the path is a absolute path for windows (starting with "C:" for instance)
776         we completely ignore the path in self and use the given path only.
777         !!! except we are chrooted, in this case the given path is invalid
778         ( no abs. path for chrooted dir's ) */
779         if ( self -> root != 0 )
780             return RC ( rcFS, rcDirectory, ctx, rcPath, rcInvalid );
781 
782         /* we detected a drive or UNC path - require a further character */
783         if ( temp_utf8_buffer [ 2 ] == 0 )
784             return RC ( rcFS, rcDirectory, ctx, rcPath, rcInvalid );
785 
786         buffer_length_in_wchars = 0;
787     }
788 
789 
790     /**************************************************/
791     /* THESE ARE EXPECTED TO BE POSIX-STYLE PATHS NOW */
792     /**************************************************/
793 
794     /* relative path to directory */
795     else if ( temp_utf8_buffer [ 0 ] != '/' )
796     {
797         /* copy base of path from self */
798         assert ( self -> length >= 3 );
799         buffer_length_in_wchars = self -> length;
800     }
801     else
802     {
803         /* POSIX full path, should include drive letter or UNC slashes */
804 
805         /* get chroot'd path length */
806         buffer_length_in_wchars = self -> root;
807 
808         /* if the full path includes a drive letter */
809         if ( isalpha ( temp_utf8_buffer [ 1 ] ) && temp_utf8_buffer [ 2 ] == '/' )
810         {
811             /* fail if chroot'd */
812             if ( self -> root != 0 )
813                 return RC ( rcFS, rcDirectory, ctx, rcPath, rcInvalid );
814 
815             /* rewrite drive letter */
816             temp_utf8_buffer [ 0 ] = tolower ( temp_utf8_buffer [ 1 ] );
817             temp_utf8_buffer [ 1 ] = ':';
818         }
819         /* detect UNC path */
820         else if ( temp_utf8_buffer [ 1 ] == '/' )
821         {
822             /* fail if chroot'd */
823             if ( self -> root != 0 )
824                 return RC ( rcFS, rcDirectory, ctx, rcPath, rcInvalid );
825 
826             /* fail if just '//' */
827             if ( temp_utf8_buffer [ 2 ] == 0 )
828             {
829                 if ( ! fake_posix )
830                     return RC ( rcFS, rcDirectory, ctx, rcPath, rcInvalid );
831 
832                 temp_utf8_buffer [ 1 ] = 0;
833                 temp_length_in_utf8_chars = 1;
834                 temp_size_in_bytes = 1;
835             }
836         }
837         else if ( self -> root == 0 )
838         {
839             /* this is a "full" path that does not appear to be convertible
840                to a Windows full path, unless we are chroot'd */
841             if ( ! fake_posix )
842                 return RC ( rcFS, rcDirectory, ctx, rcPath, rcInvalid );
843 
844             /* allow path like "/C" */
845             if ( isalpha ( temp_utf8_buffer [ 1 ] ) && temp_utf8_buffer [ 2 ] == 0 )
846             {
847                 temp_utf8_buffer [ 0 ] = tolower ( temp_utf8_buffer [ 1 ] );
848                 temp_utf8_buffer [ 1 ] = ':';
849                 temp_utf8_buffer [ 2 ] = '/';
850                 temp_utf8_buffer [ 3 ] = 0;
851                 temp_length_in_utf8_chars = 3;
852                 temp_size_in_bytes = 3;
853             }
854         }
855         else
856         {
857             /* this needs to be a valid UNC or drive path */
858             assert ( self -> root >= 3 );
859         }
860     }
861 
862     /* check for buffer overrun */
863     if ( buffer_length_in_wchars + temp_length_in_utf8_chars >= path_max / sizeof * buffer )
864         return RC ( rcFS, rcDirectory, ctx, rcPath, rcExcessive );
865 
866     /* prepend UTF-16 directory path */
867     if ( buffer_length_in_wchars != 0 )
868     {
869         memmove ( buffer, self -> path, buffer_length_in_wchars * sizeof * buffer );
870 
871         /* if path is relative, expect trailing '\\'
872             if path is full, expect NO trailing '\\' */
873         assert ( ( buffer_length_in_wchars == self ->length &&
874                     buffer [ buffer_length_in_wchars - 1 ] == '\\' ) ||
875                  ( buffer_length_in_wchars == self ->root &&
876                    buffer [ buffer_length_in_wchars - 1 ] != '\\' ) );
877     }
878 
879     /* append the temp_utf8_buffer to the  user-supplied relative path */
880     buffer_length_in_wchars += (uint32_t)string_cvt_wchar_copy ( & buffer [ buffer_length_in_wchars ],
881         path_max - buffer_length_in_wchars * sizeof buffer [ 0 ],
882         temp_utf8_buffer, temp_size_in_bytes );
883     /* the job of the temp_utf8_buffer is done now... */
884 
885     /* detect exhausted buffer */
886     if ( buffer_length_in_wchars >= path_max / sizeof * buffer )
887         return RC ( rcFS, rcDirectory, ctx, rcPath, rcExcessive );
888 
889     /* must be either:
890        1) a full drive-letter path, or
891        2) an UNC path.
892        minimum path length is 3 */
893     assert ( buffer_length_in_wchars >= 3 || fake_posix );
894     assert ( buffer[ buffer_length_in_wchars ] == 0 );
895 
896     if ( buffer_length_in_wchars > 3 )
897     {
898         /* remove any trailing slash added by caller */
899         while ( buffer_length_in_wchars > 3 && buffer [ buffer_length_in_wchars - 1 ] == '/' )
900             buffer [ -- buffer_length_in_wchars ] = 0;
901     }
902 
903     /* convert forward to backward slashes */
904     for ( i = 0; i < buffer_length_in_wchars; ++ i )
905     {
906         if ( buffer [ i ] == '/' )
907             buffer [ i ] = '\\';
908     }
909 
910     /* if caller wants canonical representation
911        or I'm chrooted, rewrite */
912     if ( buffer_length_in_wchars > 2 && ( canon || self -> root > 2 ) )
913     {
914         return KSysDirCanonPath ( self, ctx, buffer, buffer_length_in_wchars );
915     }
916 
917     return 0;
918 }
919 
920 static
KSysDirMakePath(const KSysDir * self,enum RCContext ctx,bool canon,wchar_t * buffer,size_t path_max,const char * path,va_list args)921 rc_t KSysDirMakePath ( const KSysDir* self, enum RCContext ctx, bool canon,
922     wchar_t *buffer, size_t path_max, const char *path, va_list args )
923 {
924     return KSysDirMakeSimulatedFSPath ( self, ctx, canon, buffer, path_max, path, args, false );
925 }
926 
KSysDirOSPath(const KSysDir * self,wchar_t * real,size_t real_size,const char * path,va_list args)927 LIB_EXPORT rc_t KSysDirOSPath ( const KSysDir *self,
928     wchar_t *real, size_t real_size, const char *path, va_list args )
929 {
930     return KSysDirMakePath ( self, rcLoading, true, real, real_size, path, args );
931 }
932 
933 /* KSysDirInit - forward declaration
934  */
935 static
936 rc_t KSysDirInit ( KSysDir *self, enum RCContext ctx, uint32_t dad_root,
937                    const wchar_t *path, size_t path_size, uint32_t path_length,
938                    bool update, bool chroot );
939 
940 /* KSysDirList
941  *  create a directory listing
942  *
943  *  "list" [ OUT ] - return parameter for list object
944  *
945  *  "path" [ IN, NULL OKAY ] - optional parameter for target
946  *  directory. if NULL, interpreted to mean "."
947  */
948 static
KSysDirList(const KSysDir * self,KNamelist ** listp,bool (CC * f)(const KDirectory * dir,const char * name,void * data),void * data,const char * path,va_list args)949 rc_t CC KSysDirList ( const KSysDir *self, KNamelist **listp,
950     bool ( CC * f ) ( const KDirectory *dir, const char *name, void *data ), void *data,
951                    const char *path, va_list args )
952 {
953     KSysDir full;
954     rc_t rc = KSysDirMakePath ( self, rcListing, true, full.path, sizeof full.path, path, args );
955     if ( rc == 0 )
956     {
957         size_t size_in_bytes;
958         uint32_t len_in_chars = utf16_string_measure( full.path, &size_in_bytes );
959 
960         /* require space for a '\\*.*' and NUL */
961         if ( len_in_chars + 5 > sizeof full.path / sizeof full . path [ 0 ] )
962             rc = RC ( rcFS, rcDirectory, rcListing, rcPath, rcExcessive );
963         else
964         {
965             rc = KSysDirInit( &full, rcListing, self->root, NULL, size_in_bytes, len_in_chars, 0, 0 );
966             if ( rc == 0 )
967             {
968                 KSysDirListing *list;
969 
970                 len_in_chars = full.length;
971                 full . path [ len_in_chars + 0 ] = '*';
972                 full . path [ len_in_chars + 1 ] = '.';
973                 full . path [ len_in_chars + 2 ] = '*';
974                 full . path [ len_in_chars + 3 ] = 0;
975 
976                 rc = VNamelistMake ( &list, 5 );
977                 if ( rc == 0 )
978                 {
979                     rc = KSysDirListingInit( list, full.path, & full.dad, f, data );
980                     if ( rc != 0 )
981                     {
982                         VNamelistRelease ( list );
983                     }
984                     else
985                     {
986                         rc = VNamelistToNamelist ( list, listp );
987                         VNamelistRelease ( list );
988                     }
989                 }
990             }
991         }
992     }
993     return rc;
994 }
995 
996 static
KSysDirPathType(const KSysDir * self,const char * path,va_list args)997 uint32_t CC KSysDirPathType ( const KSysDir *self, const char *path, va_list args )
998 {
999     wchar_t full[ MAX_PATH ];
1000     rc_t rc = KSysDirMakePath( self, rcAccessing, false, full, sizeof full, path, args );
1001     if ( rc == 0 )
1002     {
1003         return KSysDirFullPathType( full );
1004     }
1005     return kptBadPath;
1006 }
1007 
1008 /* KSysDirVisit
1009  *  visit each path under designated directory,
1010  *  recursively if so indicated
1011  *
1012  *  "recurse" [ IN ] - if non-zero, recursively visit sub-directories
1013  *
1014  *  "f" [ IN ] and "data" [ IN, OPAQUE ] - function to execute
1015  *  on each path. receives a base directory and relative path
1016  *  for each entry, where each path is also given the leaf name
1017  *  for convenience. if "f" returns non-zero, the iteration will
1018  *  terminate and that value will be returned. NB - "dir" will not
1019  *  be the same as "self".
1020  *
1021  *  "path" [ IN ] - NUL terminated string in directory-native character set
1022  */
1023 typedef struct KSysDirVisitData KSysDirVisitData;
1024 struct KSysDirVisitData
1025 {
1026     rc_t ( CC * f ) ( KDirectory*, uint32_t, const char*, void* );
1027     void *data;
1028     KSysDir dir;
1029     bool recurse;
1030 };
1031 
1032 static
KSysDirVisitDir(KSysDirVisitData * pb)1033 rc_t KSysDirVisitDir ( KSysDirVisitData *pb )
1034 {
1035     /* get a directory listing */
1036     rc_t rc;
1037     KSysDirEnum listing;
1038     uint32_t path_length;
1039     size_t path_size;
1040 
1041     /* measure length and size of the given path, we will need both... */
1042     path_length = wchar_string_measure ( pb->dir.path, &path_size );
1043 
1044     /* add a trailing backslash (windows!) if it is not there... */
1045     if ( pb->dir.path[ path_length - 1 ] != '\\' )
1046     {
1047         /* check if there is space for another character */
1048         if ( ( path_size + sizeof pb->dir.path [ 0 ] ) >= sizeof pb->dir.path )
1049         {
1050             return RC( rcFS, rcDirectory, rcVisiting, rcPath, rcExcessive );
1051         }
1052         pb->dir.path[ path_length + 0 ] = '\\';
1053         pb->dir.path[ path_length + 1 ] = 0;
1054         ++ path_length;
1055         path_size += sizeof pb->dir.path[ 0 ];
1056         pb->dir.length = path_length;
1057     }
1058 
1059     /* check if there is space for 6 more bytes ( '*.*' ) */
1060     if ( ( path_size + 3 * sizeof pb->dir.path[ 0 ] ) >= sizeof pb->dir.path )
1061     {
1062         return RC( rcFS, rcDirectory, rcVisiting, rcPath, rcExcessive );
1063     }
1064     /* append '*.*' to make KSysDirEnumInit work under Windows! */
1065     pb -> dir . path [ path_length + 0 ] = '*';
1066     pb -> dir . path [ path_length + 1 ] = '.';
1067     pb -> dir . path [ path_length + 2 ] = '*';
1068     pb -> dir . path [ path_length + 3 ] = 0;
1069 
1070     rc = KSysDirEnumInit ( &listing, pb->dir.path );
1071     if( rc == 0 )
1072     {
1073         const wchar_t *name;
1074 
1075         /* truncate the appended '*.*' to visit the entries */
1076         pb -> dir . path [ path_length ] = 0;
1077 
1078         for ( name = KSysDirEnumNext( &listing );
1079               name != NULL;
1080               name = KSysDirEnumNext( &listing ) )
1081         {
1082             uint32_t type, name_length;
1083             size_t name_size;
1084             char temp_utf8_buffer [ MAX_PATH ];
1085 
1086             /* measure length and size of the element-name, we will need both... */
1087             name_length = wchar_string_measure ( name, &name_size );
1088             /* check if we have enought space for path and element-name */
1089             if ( path_size + name_size >= sizeof pb->dir.path )
1090             {
1091                 rc = RC( rcFS, rcDirectory, rcVisiting, rcPath, rcExcessive );
1092                 break;
1093             }
1094 
1095             /* append the element-name to the path */
1096             wcscpy ( &pb->dir.path[ path_length ], name );
1097             type = KSysDirFullPathType( pb->dir.path );
1098             if( type == kptBadPath )
1099             {
1100                 rc = RC( rcFS, rcDirectory, rcVisiting, rcPath, rcInvalid );
1101                 break;
1102             }
1103 
1104             /* the callback-function expects the name as utf8 !!! */
1105             wchar_cvt_string_copy ( temp_utf8_buffer, sizeof temp_utf8_buffer,
1106                                     name, name_size );
1107             rc = (*pb->f)( &pb->dir.dad, type, temp_utf8_buffer, pb->data );
1108             if ( rc != 0 )
1109                 break;
1110 
1111             /* if recursive visiting is requested and the element is a directory */
1112             if ( pb->recurse && ( type & ( kptAlias - 1 ) ) == kptDir )
1113             {
1114                 /* append the element-name-length temporary to the length of the path */
1115                 pb->dir.length += name_length;
1116                 /* call this function recursive */
1117                 rc = KSysDirVisitDir( pb );
1118                 /* restore the original path-length (for the caller function) */
1119                 pb->dir.length = path_length;
1120                 if ( rc != 0 )
1121                     break;
1122             }
1123 
1124         } /* for () */
1125 
1126         KSysDirEnumWhack( &listing );
1127     }
1128     return rc;
1129 }
1130 
1131 
1132 static
Enumerate_DriveLetters(const KSysDir * self,rc_t (CC * f)(KDirectory * dir,uint32_t type,const char * name,void * data),void * data)1133 rc_t Enumerate_DriveLetters( const KSysDir *self,
1134 	rc_t ( CC * f ) ( KDirectory *dir, uint32_t type, const char *name, void *data ), void *data )
1135 {
1136 	rc_t rc = 0;
1137 	DWORD drivebitmask = GetLogicalDrives();	/* each logical drive has its own bit set */
1138 	if ( drivebitmask == 0 )
1139 		rc = translate_file_error( GetLastError(), rcListing );
1140 	else
1141 	{
1142 		uint32_t i, n, mask = 1;
1143 		for ( i = 0; i < 26 && rc == 0; ++i, mask <<= 1 )
1144 		{
1145 			if ( ( drivebitmask & mask ) == mask )
1146 			{
1147 				char drive[ 5 ];
1148 				drive[ 0 ] = 'A' + i;
1149 				drive[ 1 ] = 0;
1150 				rc = f( ( KDirectory * ) self, kptDir, ( const char * )drive, data );
1151 			}
1152 		}
1153 	}
1154 	return rc;
1155 }
1156 
1157 
1158 static
KSysDirVisit(const KSysDir * self,bool recurse,rc_t (CC * f)(KDirectory * dir,uint32_t type,const char * name,void * data),void * data,const char * path,va_list args)1159 rc_t CC KSysDirVisit ( const KSysDir *self, bool recurse,
1160     rc_t ( CC * f ) ( KDirectory *dir, uint32_t type, const char *name, void *data ), void *data,
1161     const char *path, va_list args )
1162 {
1163     KSysDirVisitData pb;
1164     rc_t rc = KSysDirMakeSimulatedFSPath( self, rcVisiting, true, pb.dir.path, sizeof pb.dir.path, path, args, true );
1165     if ( rc == 0 )
1166     {
1167         size_t path_size;
1168         uint32_t path_length;
1169 
1170 		uint32_t path_type = KSysDirFullPathType( pb.dir.path );
1171         switch( path_type & ( kptAlias - 1 ) )
1172         {
1173             case kptNotFound:
1174                 return RC( rcFS, rcDirectory, rcVisiting, rcPath, rcNotFound );
1175             case kptBadPath:
1176                 return RC( rcFS, rcDirectory, rcVisiting, rcPath, rcInvalid );
1177             case kptDir:
1178                 break;
1179             case kptFakeRoot:
1180 				return Enumerate_DriveLetters( self, f, data );
1181 
1182 			/* call code to enumerate drives */
1183             default:
1184                 return RC( rcFS, rcDirectory, rcVisiting, rcPath, rcIncorrect );
1185         }
1186 
1187         path_length = utf16_string_measure( pb.dir.path, &path_size );
1188         rc = KSysDirInit ( & pb . dir, rcVisiting, self -> root,
1189                            NULL, path_size, path_length,
1190                            self -> dad . read_only ? 0 : 1, 0 );
1191         if ( rc == 0 )
1192         {
1193             pb . f = f;
1194             pb . data = data;
1195             pb . recurse = recurse;
1196             rc = KSysDirVisitDir ( & pb );
1197         }
1198     }
1199     return rc;
1200 }
1201 
1202 /* KSysDirRelativePath
1203  *  makes "path" relative to "root"
1204  *  both "root" and "path" MUST be absolute
1205  *  both "root" and "path" MUST be canonical, i.e. have no "./" or "../" sequences
1206  *  both root and path are in windows-native format!
1207  */
1208 static
KSysDirRelativePath(const KSysDir * self,enum RCContext ctx,const wchar_t * root,wchar_t * path,size_t path_max)1209 rc_t KSysDirRelativePath ( const KSysDir *self, enum RCContext ctx,
1210     const wchar_t *root, wchar_t *path, size_t path_max )
1211 {
1212     size_t psize;
1213     uint32_t backup, blength_in_chars, dst, diff_from_here;
1214 
1215     const wchar_t *r = root + self->root;
1216     const wchar_t *p = path + self->root;
1217 
1218     /* stop gap fix..  not actually comparing the utf16 values correctly */
1219     for ( ; towlower (*r) == towlower (*p); ++ r, ++ p )
1220     {
1221         /* disallow identical paths */
1222         if ( * r == 0 )
1223             return RC( rcFS, rcDirectory, ctx, rcPath, rcInvalid );
1224     }
1225 
1226     /* paths are identical up to "r","p"
1227        if "r" is within a leaf name, then no backup is needed
1228        by counting every '\\' from "r" to end, obtain backup count */
1229     for ( backup = 0; * r != 0; ++ r )
1230     {
1231         if ( * r == '\\' )
1232             ++ backup;
1233     }
1234 
1235     /* the number of characters to be inserted */
1236     blength_in_chars = backup * 3;
1237 
1238     /* align "p" to last directory separator */
1239     if ( p > path ) {
1240         while ( p [ -1 ] != '\\' ) -- p;
1241     }
1242 
1243     /* the size of the remaining relative path */
1244     psize = wcslen ( p );
1245     diff_from_here = ( uint32_t )( p - path );
1246 
1247     /* open up space if needed */
1248     if ( diff_from_here < blength_in_chars )
1249     {
1250         /* prevent overflow */
1251         if ( ( blength_in_chars + psize ) * sizeof( *path ) >= path_max )
1252             return RC( rcFS, rcDirectory, ctx, rcPath, rcExcessive );
1253         memmove ( & path[ blength_in_chars ], p, psize * ( sizeof *p ) );
1254     }
1255 
1256     /* insert backup sequences */
1257     for ( dst = 0; backup > 0; -- backup )
1258     {
1259         path [ dst++ ] = '.';
1260         path [ dst++ ] = '.';
1261         path [ dst++ ] = '\\';
1262     }
1263 
1264     /* close gap */
1265     if ( diff_from_here > blength_in_chars )
1266         wcscpy ( & path [ blength_in_chars ], p );
1267     path[ blength_in_chars + psize ] = 0;
1268 
1269     return 0;
1270 }
1271 
1272 /* KSysDirResolvePath
1273  *  resolves path to an absolute or directory-relative path
1274  *
1275  *  "absolute" [ IN ] - if non-zero, always give a path starting
1276  *  with '/'. NB - if the directory is chroot'd, the absolute path
1277  *  will still be relative to directory root.
1278  *
1279  *  "resolved" [ OUT ] and "rsize" [ IN ] - buffer for
1280  *  NUL terminated result path in directory-native character sets
1281  *  the resolved path will be directory relative
1282  *
1283  *  "path" [ IN ] - NUL terminated string in directory-native
1284  *  character set denoting target path. NB - need not exist.
1285  */
1286 static
KSysDirResolvePath(const KSysDir * self,bool absolute,char * resolved,size_t rsize,const char * path,va_list args)1287 rc_t CC KSysDirResolvePath ( const KSysDir *self, bool absolute,
1288     char *resolved, size_t rsize, const char *path, va_list args )
1289 {
1290     wchar_t temp [ MAX_PATH ];
1291     size_t temp_size;
1292     uint32_t temp_length;
1293 
1294     /* convert the utf8-input-parameter path into wchar_t */
1295     rc_t rc = KSysDirMakePath ( self, rcResolving, true, temp, sizeof temp, path, args );
1296     if ( rc != 0 )
1297         return rc;
1298 
1299     temp[ 0 ] = tolower( temp[ 0 ] ); /* this is important:
1300                                          otherwise the comparison for is_on_same_drive_letter fails
1301                                          AND
1302                                          KSysDirRelativePath() fails too! */
1303 
1304     temp_length = wchar_string_measure ( temp, &temp_size );
1305     if ( absolute )
1306     {
1307         /* test buffer capacity */
1308         if ( temp_length - self->root >= rsize )
1309             return RC ( rcFS, rcDirectory, rcResolving, rcBuffer, rcInsufficient );
1310     }
1311     else
1312     {
1313         /* we are on windows, only if the path has a drive letter and it is the same
1314            one as in KSysDir itself, we should try to create a relative path */
1315         wchar_t colon = ':';
1316         bool is_on_same_drive_letter = ( iswascii ( temp[ 0 ] ) && iswascii ( self->path[ 0 ] ) &&
1317                                          ( temp[ 1 ] == colon ) && ( self->path[ 1 ] == colon ) &&
1318                                          ( towlower ( temp[ 0 ] ) == towlower ( self->path[ 0 ] ) ) );
1319         if ( is_on_same_drive_letter )
1320         {
1321             rc = KSysDirRelativePath( self, rcResolving, self->path, temp, sizeof temp );
1322             if ( rc == 0 )
1323             {
1324                 uint32_t temp_length = wchar_string_measure ( temp, &temp_size );
1325                 if ( temp_length >= rsize )
1326                     return RC ( rcFS, rcDirectory, rcResolving, rcBuffer, rcInsufficient );
1327             }
1328         }
1329         else
1330         {
1331             /* treat it as if absolute were requested ( see above ) */
1332             if ( temp_length - self->root >= rsize )
1333                 return RC ( rcFS, rcDirectory, rcResolving, rcBuffer, rcInsufficient );
1334         }
1335     }
1336 
1337     if ( rc == 0 )
1338     {
1339         uint32_t i;
1340         /* convert it back to utf8 */
1341         utf16_cvt_string_copy ( resolved, rsize, temp, temp_size );
1342 
1343         /* convert it back to POSIX */
1344         if ( isalpha ( resolved[ 0 ] ) && resolved[ 1 ] == ':' )
1345         {
1346             /* rewrite drive letter */
1347             resolved[ 1 ] = tolower ( resolved [ 0 ] );
1348             resolved[ 0 ] = '/';
1349         }
1350 
1351         /* convert backward to forward slashes */
1352         for ( i = 0; resolved[ i ]; ++ i )
1353         {
1354             if ( resolved[ i ] == '\\' )
1355                 resolved[ i ] = '/';
1356         }
1357     }
1358 
1359     return rc;
1360 }
1361 
1362 /* KSysDirResolveAlias
1363  *  resolves an alias path to its immediate target
1364  *  NB - the resolved path may be yet another alias
1365  *
1366  *  "alias" [ IN ] - NUL terminated string in directory-native
1367  *  character set denoting an object presumed to be an alias.
1368  *
1369  *  "resolved" [ OUT ] and "rsize" [ IN ] - buffer for
1370  *  NUL terminated result path in directory-native character set
1371  */
1372 static
KSysDirResolveAlias(const KSysDir * self,bool absolute,char * resolved,size_t rsize,const char * alias,va_list args)1373 rc_t CC KSysDirResolveAlias ( const KSysDir *self, bool absolute,
1374                               char *resolved, size_t rsize,
1375                               const char *alias, va_list args )
1376 {
1377     KSysDir temp;
1378     size_t temp_size;
1379     uint32_t temp_length, path_type;
1380     wchar_t * w_resolved;
1381 
1382     rc_t rc = KSysDirMakePath( self, rcResolving, true, temp.path, sizeof temp.path, alias, args );
1383     if ( rc != 0 )
1384         return rc;
1385 
1386     temp_length = wchar_string_measure ( temp.path, &temp_size );
1387     path_type = KSysDirFullPathType ( temp.path );
1388     if ( path_type == kptFile || path_type == kptDir )
1389     {
1390         /* if the path points to a file or a dir, then there is no alias involved at all */
1391         if ( temp_size >= rsize )
1392         {
1393             return RC ( rcFS, rcDirectory, rcResolving, rcBuffer, rcInsufficient );
1394         }
1395         /* we have to convert temp.path back from wchar_t to char ! */
1396         wchar_cvt_string_copy ( resolved, rsize, temp.path, temp_size );
1397         return 0;
1398     }
1399 
1400 
1401     /* trying to attach a .lnk to the path, if it resolves it is a link... */
1402     if ( temp_size + 10 >= rsize )
1403     {
1404         return RC( rcFS, rcDirectory, rcResolving, rcBuffer, rcInsufficient );
1405     }
1406     temp.path[ temp_length + 0 ] = '.';
1407     temp.path[ temp_length + 1 ] = 'l';
1408     temp.path[ temp_length + 2 ] = 'n';
1409     temp.path[ temp_length + 3 ] = 'k';
1410     temp.path[ temp_length + 4 ] = 0;
1411 
1412     if ( lnk_file_resolve( temp.path, &w_resolved ) != LNK_RES_ERROR )
1413     {
1414         size_t w_size;
1415         uint32_t w_len;
1416 
1417         /* we have to copy the resolved path into temp to use KSysDirCanonPath() */
1418         w_len = wchar_string_measure ( w_resolved, &w_size );
1419         if ( w_size > sizeof temp.path )
1420         {
1421             free( w_resolved );
1422             return RC( rcFS, rcDirectory, rcResolving, rcBuffer, rcInsufficient );
1423         }
1424         wcscpy( temp.path, w_resolved );
1425         free( w_resolved );
1426 
1427         rc = KSysDirCanonPath( &temp, rcResolving, temp.path, w_len );
1428         if ( rc == 0 )
1429         {
1430             /* the path in full is an absolute path
1431                if outside of chroot, it's a bad link */
1432             if (wstrcase_cmp (temp.path, self->root + 1,
1433                               self->path, self->root + 1,self->root + 1) != 0)
1434                 return RC( rcFS, rcDirectory, rcResolving, rcLink, rcInvalid );
1435 
1436             /* this is the absolute path length */
1437             w_len = wchar_string_measure ( temp.path, &w_size );
1438 
1439             /* if not requesting absolute, make self relative */
1440             if( !absolute )
1441             {
1442                 rc = KSysDirRelativePath( self, rcResolving, self->path,
1443                                           temp.path, w_len );
1444                 if ( rc != 0 )
1445                     return rc;
1446                 w_len = wchar_string_measure ( temp.path, &w_size );
1447             }
1448             if ( ( size_t ) w_len >= rsize )
1449                 return RC(rcFS, rcDirectory, rcResolving, rcBuffer, rcInsufficient );
1450 
1451             w_len = wchar_string_measure ( &(temp.path[ self -> root ]), &w_size );
1452             wchar_cvt_string_copy ( resolved, rsize, &(temp.path[ self -> root ]), w_size );
1453         }
1454     }
1455 
1456 
1457 #if 0
1458     /* NEXT - attach ".lnk" to the path and see if it resolves
1459        if not, the supplied path simply does not exist */
1460         if ( ( wcslen( full.path ) + 5 ) >= rsize )
1461         {
1462             return RC( rcFS, rcDirectory, rcResolving, rcBuffer, rcInsufficient );
1463         }
1464         wcscpy( resolved, full.path );
1465         wcscat( resolved, L".lnk" );
1466         refnum = FindFirstFile( resolved, &info );
1467         if ( refnum == INVALID_HANDLE_VALUE )
1468         {
1469             wcscpy( resolved, L"\0" );
1470             return RC( rcFS, rcDirectory, rcResolving, rcPath, rcNotFound );
1471         }
1472         FindClose( refnum );
1473 
1474         {
1475 
1476             /* get a COM reference to the Explorer. we should be
1477                able to do this in C with no problem */
1478             HRESULT rslt = ERROR_INSUFFICIENT_BUFFER;
1479             IShellLink *shellLink;
1480             rslt = CoCreateInstance( &IID_IShellLink, 0, CLSCTX_INPROC_SERVER, &IID_IShellLink, &shellLink );
1481             if( !rslt )
1482             {
1483                 /* get a file interface that isn't attached to anything */
1484                 IPersistFile *persistFile;
1485                 rslt = shellLink->lpVtbl->QueryInterface( shellLink, &IID_IPersistFile, (void**)&persistFile );
1486                 if( !rslt )
1487                 {
1488                     /* now try to do the thing
1489                        the link name needs to be in Unicode */
1490                     rslt = persistFile->lpVtbl->Load(persistFile, resolved, STGM_READ );
1491                     if( !rslt )
1492                     {
1493                         /* Unicode is no longer necessary */
1494                         rslt = shellLink->lpVtbl->Resolve( shellLink, 0, SLR_NO_UI + SLR_ANY_MATCH );
1495                         if ( !rslt )
1496                         {
1497                             /* read what the path is, i.e. read the shortcut file */
1498                             rslt = shellLink->lpVtbl->GetPath( shellLink, resolved, rsize, &info, 0 );
1499                             if( rslt )
1500                             {
1501                                 wcscpy( resolved, L"\0" );
1502                             }
1503                         }
1504                     }
1505                     persistFile->lpVtbl->Release( persistFile );
1506                 }
1507                 shellLink->lpVtbl->Release( shellLink );
1508             }
1509             if ( wcslen( resolved ) == 0 )
1510             {
1511                 return RC( rcFS, rcDirectory, rcResolving, rcPath, rcInvalid );
1512             }
1513 
1514         }
1515 
1516         len = wcslen( resolved );
1517         if( resolved[0] == '/' )
1518         {
1519             full.size = 1;
1520             wcscpy( full.path, resolved );
1521         }
1522         else
1523         {
1524             wchar_t *f = wcsrchr( full.path, '/' );
1525             full.size = f - full.path + 1;
1526             if ( full.size + len >= sizeof full.path )
1527             {
1528                 return RC( rcFS, rcDirectory, rcResolving, rcBuffer, rcInsufficient );
1529             }
1530             wcscpy( f, resolved );
1531         }
1532         full.root = 0;
1533 
1534         rc = KSysDirCanonPath( &full, rcResolving, full.path, len );
1535         if ( rc == 0 )
1536         {
1537             size_t f, s;
1538 
1539             f = wchar_string_size (full.path);
1540             s = wchar_string_size (self->path);
1541             /* the path in full is an absolute path
1542                if outside of chroot, it's a bad link */
1543             if ( wstrcase_cmp (full.path, f, self->path, s, self->root + 1 ) != 0 )
1544             {
1545                 return RC( rcFS, rcDirectory, rcResolving, rcLink, rcInvalid );
1546             }
1547 
1548             /* this is the absolute path length */
1549             len = wchar_string_size( &full.path[self->root] );
1550 
1551             /* if not requesting absolute, make self relative */
1552             if( !absolute )
1553             {
1554             rc = KSysDirRelativePath( self, rcResolving, self->path, full.path, sizeof full.path /*len*/ );
1555                 if ( rc != 0 )
1556                     return rc;
1557                 len = wchar_string_size(full.path);
1558             }
1559             if ( ( size_t ) len >= rsize )
1560                 return RC(rcFS, rcDirectory, rcResolving, rcBuffer, rcInsufficient );
1561 
1562             wcscpy ( resolved, & full . path [ self -> root ] );
1563         }
1564 #endif
1565     return rc;
1566 }
1567 
1568 /* KSysDirRename
1569  *  rename an object accessible from directory, replacing
1570  *  any existing target object of the same type
1571  *
1572  *  "from" [ IN ] - NUL terminated string in directory-native
1573  *  character set denoting existing object
1574  *
1575  *  "to" [ IN ] - NUL terminated string in directory-native
1576  *  character set denoting existing object
1577  */
1578 static
KSysDirRename(KSysDir * self,bool force,const char * from,const char * to)1579 rc_t CC KSysDirRename ( KSysDir *self, bool force, const char *from, const char *to )
1580 {
1581     wchar_t current_name[ MAX_PATH ];
1582     rc_t rc = KSysDirMakePath ( self, rcRenaming, false, current_name, sizeof current_name, from, NULL );
1583     if ( rc == 0 )
1584     {
1585 
1586         wchar_t new_name[ MAX_PATH ];
1587         rc = KSysDirMakePath ( self, rcRenaming, false, new_name, sizeof new_name, to, NULL );
1588         if ( rc == 0 )
1589         {
1590             DWORD err = 0;
1591             uint32_t try = 0;
1592 
1593             do
1594             {
1595                 BOOL success = false;
1596                 if ( force ) {
1597                     DWORD dwFlags = MOVEFILE_REPLACE_EXISTING;
1598                     success = MoveFileEx ( current_name, new_name, dwFlags );
1599                 }
1600                 else {
1601                     success = MoveFileW ( current_name, new_name );
1602                 }
1603                 if ( success )
1604                 {
1605                     rc = 0;
1606                 }
1607                 else
1608                 {
1609                     err = GetLastError();
1610                     switch( err )
1611                     {
1612                         case ERROR_FILE_NOT_FOUND:
1613                         case ERROR_PATH_NOT_FOUND:
1614                         case ERROR_INVALID_DRIVE:
1615                             return RC ( rcFS, rcDirectory, rcRenaming, rcFile, rcNotFound );
1616                         case ERROR_ACCESS_DENIED:
1617                             try++;
1618                             KSleepMs( 500 ); /* sleep for a half a second */
1619                             rc = RC ( rcFS, rcDirectory, rcRenaming, rcFile, rcUnauthorized );
1620                             break;
1621                        case ERROR_SHARING_VIOLATION:
1622                            return RC ( rcFS, rcDirectory, rcRenaming, rcFile, rcBusy );
1623                         default:
1624                             return RC ( rcFS, rcDirectory, rcRenaming, rcNoObj, rcUnknown );
1625                     }
1626                 }
1627             } while ( err == ERROR_ACCESS_DENIED && try < 30 );
1628         }
1629     }
1630     return rc;
1631 }
1632 
1633 
1634 /* helper function for KSysDirCreateParents() */
1635 static
directory_exists(const wchar_t * path,bool * exists)1636 rc_t directory_exists( const wchar_t *path, bool *exists )
1637 {
1638     /* try it with CreateFileW() */
1639     *exists = win_path_exists( path );
1640     return 0;
1641 #if 0
1642     wchar_t temp[ MAX_PATH ];
1643     WIN32_FIND_DATA find_data;
1644     HANDLE h_find;
1645     size_t path_size;
1646     uint32_t path_length = utf16_string_measure( path, &path_size );
1647 
1648     *exists = false;
1649 
1650     if ( ( path_size + 10 ) > sizeof temp )
1651         return RC( rcFS, rcDirectory, rcCreating, rcMemory, rcExhausted );
1652 
1653     wcscpy( temp, path );
1654     if ( temp[ path_length - 1 ] != '\\' )
1655         temp[ path_length++ ] = '\\';
1656     temp[ path_length + 0 ] = '*';
1657     temp[ path_length + 1 ] = '.';
1658     temp[ path_length + 2 ] = '*';
1659     temp[ path_length + 3 ] = 0;
1660 
1661     h_find = FindFirstFileW( temp, &find_data );
1662     if ( h_find != INVALID_HANDLE_VALUE )
1663     {
1664         *exists = true;
1665         FindClose( h_find );
1666     }
1667 
1668     return 0;
1669 #endif
1670 }
1671 
1672 
1673 static
1674 rc_t KSysDirRemoveEntry ( wchar_t *path, size_t path_max, bool force );
1675 
1676 
1677 static
KSysDirEmptyDir(wchar_t * path,size_t path_max,bool force)1678 rc_t KSysDirEmptyDir ( wchar_t *path, size_t path_max, bool force )
1679 {
1680     rc_t rc;
1681     KSysDirEnum list;
1682     size_t path_size;
1683     uint32_t path_length = wchar_string_measure ( path, &path_size );
1684 
1685     if ( ( path_size + 10 ) > path_max )
1686         return RC( rcFS, rcDirectory, rcListing, rcMemory, rcExhausted );
1687 
1688     rc = KSysDirEnumInitAll ( & list, path, path_length );
1689     if ( rc != 0 )
1690     {
1691         rc = ResetRCContext ( rc, rcFS, rcDirectory, rcClearing );
1692     }
1693     else
1694     {
1695         const wchar_t *leaf;
1696 
1697         /* we keep only the appended '\\' for the loop... */
1698         path_length++;
1699         path_size += sizeof *path;
1700 
1701         for ( leaf = KSysDirEnumNext( &list );
1702               leaf != NULL;
1703               leaf = KSysDirEnumNext( &list ) )
1704         {
1705             size_t leaf_size;
1706             uint32_t leaf_length = wchar_string_measure ( leaf, &leaf_size );
1707             if ( path_size + leaf_size >= path_max )
1708             {
1709                 rc = RC ( rcFS, rcDirectory, rcClearing, rcPath, rcExcessive );
1710                 break;
1711             }
1712 
1713             /* wcscpy adds termination, so wprintf is safe to call */
1714             wcscpy ( & path [ path_length ], leaf );
1715 
1716             rc = KSysDirRemoveEntry ( path, path_max, force );
1717             if ( rc != 0 )
1718             {
1719                 rc = ResetRCContext ( rc, rcFS, rcDirectory, rcClearing );
1720                 break;
1721             }
1722         }
1723         KSysDirEnumWhack ( & list );
1724         /* restore the original path... */
1725         path [ path_length - 1 ] = 0;
1726     }
1727     return rc;
1728 }
1729 
1730 
1731 /* KSysDirClearDir
1732  *  remove all directory contents
1733  *
1734  *  "path" [ IN ] - NUL terminated string in directory-native
1735  *  character set denoting target directory
1736  *
1737  *  "force" [ IN ] - if non-zero and directory entry is a
1738  *  sub-directory, remove recursively
1739  */
1740 static
KSysDirClearDir(KSysDir * self,bool force,const char * path,va_list args)1741 rc_t CC KSysDirClearDir ( KSysDir *self, bool force, const char *path, va_list args )
1742 {
1743     wchar_t dir_name [ MAX_PATH ];
1744     rc_t rc = KSysDirMakePath ( self, rcClearing, false, dir_name, sizeof dir_name, path, args );
1745     if ( rc == 0 )
1746         rc = KSysDirEmptyDir ( dir_name, sizeof dir_name, force );
1747     return rc;
1748 }
1749 
1750 
1751 /* KSysDirRemove
1752  *  remove an accessible object from its directory
1753  *
1754  *  "path" [ IN ] - NUL terminated string in directory-native
1755  *  character set denoting target object
1756  *
1757  *  "force" [ IN ] - if non-zero and target is a directory,
1758  *  remove recursively
1759  */
1760 static
KSysDirRemoveEntry(wchar_t * path,size_t path_max,bool force)1761 rc_t KSysDirRemoveEntry ( wchar_t *path, size_t path_max, bool force )
1762 {
1763     if ( !DeleteFileW( path ) )
1764     {
1765         DWORD file_error = GetLastError();
1766 
1767         switch ( file_error )
1768         {
1769         case ERROR_PATH_NOT_FOUND :
1770             return 0;
1771 /*
1772         case ERROR_ACCESS_DENIED :
1773             !!! Do not use this error code here, it occurs if path is not a file, but
1774             a directory instead. Handling it here would prevent the remaining code
1775             from beeing executed !!!
1776             return RC( rcFS, rcDirectory, rcRemoving, rcDirectory, rcUnauthorized );
1777 */
1778 
1779         default :
1780 #if _DEBUGGING && 0
1781     OUTMSG (( "DeleteFileW returned '%#X'\n", file_error ));
1782 #endif
1783             break;
1784         }
1785 
1786         /* we have not been able to delete it as a file,
1787            we try to delete it as a directory... */
1788         if ( !RemoveDirectoryW( path ) )
1789         {
1790             rc_t rc;
1791             DWORD error = GetLastError();
1792 
1793             /* find out if the reason is that it is not empty and force = true --->
1794                in this case delete all files and directories in it
1795                and then try again... */
1796             switch ( error )
1797             {
1798             case ERROR_DIR_NOT_EMPTY :
1799                 if ( force )
1800                 {
1801                     rc = KSysDirEmptyDir ( path, path_max, force );
1802                     if ( rc == 0 )
1803                     {
1804                         if ( !RemoveDirectoryW( path ) )
1805                         {
1806                             rc = RC ( rcFS, rcDirectory, rcRemoving, rcDirectory, rcUnauthorized );
1807                             print_error_for( error, path, "RemoveDirectoryW", rcRemoving, klogErr );
1808                         }
1809                     }
1810                     return rc;
1811                 }
1812                 else
1813                     rc = RC ( rcFS, rcDirectory, rcRemoving, rcDirectory, rcUnauthorized );
1814                 break;
1815 
1816             case ERROR_ACCESS_DENIED :
1817                 rc = RC ( rcFS, rcDirectory, rcRemoving, rcDirectory, rcUnauthorized );
1818                 break;
1819 
1820             case ERROR_DIRECTORY: /* not a directory */
1821                 /* looks like it was a file after all; report the original error */
1822                 error = file_error;
1823                 print_error_for( file_error, path, "DeleteFileW", rcRemoving, klogInfo);
1824                 return RC ( rcFS, rcDirectory, rcRemoving, rcDirectory, rcUnauthorized );
1825 
1826             default :
1827                 rc = RC ( rcFS, rcDirectory, rcCreating, rcNoObj, rcUnknown );
1828                 break;
1829             }
1830 
1831             print_error_for( error, path, "RemoveDirectoryW", rcRemoving, klogInfo);
1832             return rc;
1833         }
1834     }
1835     return 0;
1836 }
1837 
1838 
1839 static
KSysDirRemove(KSysDir * self,bool force,const char * path,va_list args)1840 rc_t CC KSysDirRemove ( KSysDir *self, bool force, const char *path, va_list args )
1841 {
1842     wchar_t dir_name [ MAX_PATH ];
1843     rc_t rc = KSysDirMakePath ( self, rcRemoving, false, dir_name, sizeof dir_name, path, args );
1844     if ( rc == 0 )
1845         rc = KSysDirRemoveEntry ( dir_name, sizeof dir_name, force );
1846     return rc;
1847 }
1848 
1849 /* KSysDirAccess
1850  *  get access to object
1851  *
1852  *  "access" [ OUT ] - return parameter for Unix access mode
1853  *
1854  *  "path" [ IN ] - NUL terminated string in directory-native
1855  *  character set denoting target object
1856  */
1857 
1858 #define DEFAULT_WIN_ACCESS   0555
1859 #define DEFAULT_WRITE_ACCESS 0222
1860 
1861 
1862 /* FromMSDN */
1863 #define UNIX_EPOCH_IN_WIN       116444736000000000
1864 #define UINX_TIME_UNITS_IN_WIN  10000000
1865 static __inline__
KTimeToWinTime(KTime_t unix,LPFILETIME win)1866 void KTimeToWinTime ( KTime_t unix, LPFILETIME win )
1867 {
1868     uint64_t ll = ( ( unix * UINX_TIME_UNITS_IN_WIN ) + UNIX_EPOCH_IN_WIN );
1869     win->dwLowDateTime = (DWORD)ll;
1870     win->dwHighDateTime = ll >> 32;
1871 }
1872 
1873 
1874 static __inline__
WinTimeToKTime(LPFILETIME win)1875 KTime_t WinTimeToKTime ( LPFILETIME win )
1876 {
1877     uint64_t ll = (uint64_t)win->dwLowDateTime + ((int64_t)win->dwHighDateTime << 32);
1878 
1879 /*     DBGMSG(DBG_KFS,DBG_FLAG(DBG_KFS_DIR),("%s %x %x %lx %lx\n", */
1880 /*                                           __func__,win->dwLowDateTime,win->dwHighDateTime, */
1881 /*                                           ll,( ll - UNIX_EPOCH_IN_WIN ) / 10000000)); */
1882 
1883     /* if its negative, so be it */
1884     return ( ll - UNIX_EPOCH_IN_WIN ) / UINX_TIME_UNITS_IN_WIN;
1885 }
1886 
1887 
1888 static __inline
get_attributes(const wchar_t * wpath,uint32_t * access,KTime_t * date)1889 rc_t get_attributes ( const wchar_t * wpath, uint32_t * access, KTime_t * date )
1890 {
1891     WIN32_FIND_DATA fd;
1892     rc_t rc;
1893     HANDLE h = FindFirstFile ( wpath, &fd );
1894     if ( h != INVALID_HANDLE_VALUE )
1895     {
1896         if ( access != NULL )
1897         {
1898 /* TBD - track user's main group and group Everyone */
1899             *access = DEFAULT_WIN_ACCESS |
1900                 (((fd.dwFileAttributes & FILE_ATTRIBUTE_READONLY) == FILE_ATTRIBUTE_READONLY)
1901                  ? 0 : DEFAULT_WRITE_ACCESS);
1902         }
1903         if ( date != NULL )
1904         {
1905             *date = WinTimeToKTime ( &fd.ftLastWriteTime );
1906         }
1907         FindClose ( h );
1908         return 0;
1909     }
1910 
1911 /* TBD check values in error */
1912     if ( access != NULL )
1913         *access = 0;
1914     if ( date != NULL )
1915         *date = 0;
1916 
1917     rc = print_error_for( GetLastError(), wpath, "FindFirstFile", rcAccessing, klogErr );
1918     return rc;
1919 }
1920 
1921 
1922 static
KSysDirVAccess(const KSysDir * self,uint32_t * access,const char * path,va_list args)1923 rc_t CC KSysDirVAccess ( const KSysDir *self,
1924     uint32_t *access, const char *path, va_list args )
1925 {
1926     wchar_t winpath [ MAX_PATH ];
1927     rc_t rc = KSysDirMakePath ( self, rcAccessing, false, winpath, sizeof winpath, path, args );
1928     if ( rc == 0 )
1929         rc = get_attributes ( winpath, access, NULL );
1930     return rc;
1931 }
1932 
1933 /* KSysDirSetAccess
1934  *  set access to object a la Unix "chmod"
1935  *
1936  *  "path" [ IN ] - NUL terminated string in directory-native
1937  *  character set denoting target object
1938  *
1939  *  "access" [ IN ] and "mask" [ IN ] - definition of change
1940  *  where "access" contains new bit values and "mask defines
1941  *  which bits should be changed.
1942  *
1943  *  "recurse" [ IN ] - if non zero and "path" is a directory,
1944  *  apply changes recursively.
1945  */
1946 static
1947 rc_t KSysDirChangeAccess ( char *path, size_t path_max,
1948     uint32_t access, uint32_t mask, bool recurse );
1949 
1950 
1951 static
KSysDirChangeDirAccess(char * path,size_t path_max,uint32_t access,uint32_t mask)1952 rc_t KSysDirChangeDirAccess ( char *path, size_t path_max,
1953     uint32_t access, uint32_t mask )
1954 {
1955     /*
1956     KSysDirEnum list;
1957     rc_t rc = KSysDirEnumInit ( & list, path );
1958     if ( rc == 0 )
1959     {
1960         bool eperm = false;
1961         size_t path_size = strlen ( path );
1962         path [ path_size ] = '/';
1963         if ( ++ path_size == path_max )
1964             rc = RC(rcFS, rcDirectory, rcUpdating, rcBuffer, rcInsufficient );
1965         else
1966         {
1967             const char *leaf;
1968             while ( ( leaf = KSysDirEnumNext ( & list ) ) != NULL )
1969             {
1970                 size_t leaf_size = strlen ( leaf );
1971                 if ( path_size + leaf_size >= path_max )
1972                 {
1973                     rc = RC(rcFS, rcDirectory, rcUpdating, rcBuffer, rcInsufficient );
1974                     break;
1975                 }
1976 
1977                 strcpy ( & path [ path_size ], leaf );
1978                 rc = KSysDirChangeAccess ( path, path_max, access, mask, 1 );
1979                 if ( rc != 0 )
1980                 {
1981                     if ( GetRCState ( rc ) != rcUnauthorized )
1982                         break;
1983                     eperm = true;
1984                     rc = 0;
1985                 }
1986             }
1987 
1988             path [ path_size - 1 ] = 0;
1989         }
1990 
1991         KSysDirEnumWhack ( & list );
1992 
1993         if ( rc == 0 && eperm )
1994             rc = RC(rcFS, rcDirectory, rcUpdating, rcDirectory, rcUnauthorized );
1995     }
1996     return rc;
1997     */
1998     return 0;
1999 }
2000 
2001 
2002 static
KSysDirChangeEntryAccess(char * path,size_t path_max,uint32_t access,uint32_t mask,uint32_t st_mode)2003 rc_t KSysDirChangeEntryAccess ( char *path, size_t path_max,
2004     uint32_t access, uint32_t mask, uint32_t st_mode )
2005 {
2006 #if 0
2007     /* keep old bits
2008        we have no chmod in Windows - leave it blank... */
2009     access &= mask;
2010     access |= st_mode & ~ mask;
2011 
2012     if ( chmod ( path, access & 07777 ) != 0 )
2013         switch ( errno )
2014         {
2015         case EPERM:
2016         case EACCES:
2017         case EROFS:
2018             return RC(rcFS, rcDirectory, rcUpdating, rcDirectory, rcUnauthorized );
2019         case ENOTDIR:
2020         case ELOOP:
2021             return RC(rcFS, rcDirectory, rcUpdating, rcPath, rcInvalid );
2022         case ENAMETOOLONG:
2023             return RC(rcFS, rcDirectory, rcUpdating, rcPath, rcExcessive );
2024         case ENOENT:
2025             return RC(rcFS, rcDirectory, rcUpdating, rcPath, rcNotFound );
2026         case ENOMEM:
2027             return RC(rcFS, rcDirectory, rcUpdating, rcMemory, rcExhausted );
2028         default:
2029             return RC(rcFS, rcDirectory, rcUpdating, rcNoObj, rcUnknown );
2030         }
2031 #endif
2032     return 0;
2033 }
2034 
2035 
2036 static
KSysDirChangeAccess(char * path,size_t path_max,uint32_t access,uint32_t mask,bool recurse)2037 rc_t KSysDirChangeAccess ( char *path, size_t path_max,
2038     uint32_t access, uint32_t mask, bool recurse )
2039 {
2040     /*
2041     struct stat st;
2042     if ( stat ( path, & st ) != 0 ) switch ( errno )
2043     {
2044     case ENOENT:
2045         return RC(rcFS, rcDirectory, rcUpdating, rcPath, rcNotFound );
2046     case ENOTDIR:
2047     case ELOOP:
2048         return RC(rcFS, rcDirectory, rcUpdating, rcPath, rcInvalid );
2049     case ENAMETOOLONG:
2050         return RC(rcFS, rcDirectory, rcUpdating, rcPath, rcExcessive );
2051     case EACCES:
2052         return RC(rcFS, rcDirectory, rcUpdating, rcDirectory, rcUnauthorized );
2053     case ENOMEM:
2054         return RC(rcFS, rcDirectory, rcUpdating, rcMemory, rcExhausted );
2055     default:
2056         return RC(rcFS, rcDirectory, rcUpdating, rcNoObj, rcUnknown );
2057     }
2058 
2059     if ( recurse && S_ISDIR ( st . st_mode ) )
2060     {
2061         rc_t rc;
2062         uint32_t enable = access & mask;
2063         if ( enable != 0 )
2064         {
2065             rc = KSysDirChangeEntryAccess ( path, path_max,
2066                 access, enable, st . st_mode );
2067             if ( rc != 0 )
2068                 return rc;
2069         }
2070 
2071        rc = KSysDirChangeDirAccess ( path, path_max, access, mask );
2072         if ( rc == 0 )
2073         {
2074             uint32_t disable = ~ access & mask;
2075             if ( disable != 0 )
2076             {
2077                 rc = KSysDirChangeEntryAccess ( path, path_max,
2078                     access, disable, st . st_mode | enable );
2079             }
2080         }
2081         return rc;
2082     }
2083 
2084     return KSysDirChangeEntryAccess ( path, path_max,
2085          access, mask, st . st_mode );
2086     */
2087     return 0;
2088 }
2089 
2090 
2091 static
KSysDirSetAccess(KSysDir * self,bool recurse,uint32_t access,uint32_t mask,const char * path,va_list args)2092 rc_t CC KSysDirSetAccess ( KSysDir *self, bool recurse,
2093     uint32_t access, uint32_t mask, const char *path, va_list args )
2094 {
2095     rc_t rc = 0;
2096     /*
2097     char full[MAX_PATH];
2098     rc_t rc = KSysDirMakePath ( self, rcUpdating, false, full, sizeof full, path, args );
2099     if ( rc == 0 )
2100     {
2101         if ( mask == 0 )
2102             mask = 07777;
2103 
2104         rc = KSysDirChangeAccess ( full, sizeof full,
2105             access, mask & 07777, recurse );
2106     }
2107     */
2108     return rc;
2109 }
2110 
2111 
2112 /* make_dir()
2113  *  helper function that encapsulates the OS-specific call
2114  *  to create a directory - the return codes are used by the
2115  *  caller-functions to decide what to do in case of a error...
2116  *  the callers are: KSysDirCreateParents() and KSysDirCreateDir()
2117  *  special on windows: path is wchar_t and we ignore access !!!
2118  *  TBD: translate access into a windows security descriptor...
2119  *       find out the other possible ERROR_* 's produced
2120  */
2121 static
make_dir(const wchar_t * path,uint32_t access)2122 rc_t make_dir ( const wchar_t *path, uint32_t access )
2123 {
2124     rc_t rc = 0;
2125     /* try to create the directory */
2126     if ( !CreateDirectoryW ( path, NULL ) )
2127     {
2128         DWORD error = GetLastError();
2129         rc = translate_file_error( error, rcCreating );
2130 /*
2131         Do not print an error code here, it is valid that this can happen!
2132         rc = print_error_for( error, path, "CreateDirectoryW", rcCreating, klogErr );
2133 */
2134     }
2135     return rc;
2136 }
2137 
2138 
2139 #if OLD_CREATE_PARENTS
2140 static
check_and_make(wchar_t * path,uint32_t access)2141 rc_t check_and_make( wchar_t *path, uint32_t access )
2142 {
2143     bool exists;
2144     rc_t rc = directory_exists( path, &exists );
2145     if ( rc == 0 && !exists )
2146     {
2147         rc = make_dir ( path, access );
2148     }
2149     return rc;
2150 }
2151 #endif
2152 
2153 
2154 /* KSysDirCreateParents
2155  *  creates missing parent directories
2156  *  Windows special: path is wide-char, separator is back-slash,
2157  *  starts with drive-letter...
2158  */
2159 static
KSysDirCreateParents(const KSysDir * self,wchar_t * path,uint32_t access,bool strip)2160 rc_t KSysDirCreateParents ( const KSysDir *self, wchar_t *path, uint32_t access, bool strip )
2161 {
2162 #if ! OLD_CREATE_PARENTS
2163     rc_t rc;
2164     size_t len;
2165     wchar_t *p, *par = path;
2166 
2167     /* if directory is chroot'd, skip past root and slash */
2168     if ( self -> root != 0 )
2169         par += self -> root + 1;
2170     else
2171     {
2172         /* skip drive letter */
2173         if ( path [ 1 ] == ':' )
2174             par += 2;
2175     /* skip slashes, network or otherwise */
2176         while ( par [ 0 ] == '\\' )
2177             ++ par;
2178     }
2179 
2180     len = wcslen ( par );
2181 
2182     if ( ! strip )
2183         p = par + len;
2184     else
2185     {
2186         p = wcsrchr ( par, '\\' );
2187         if ( p == NULL )
2188             return 0;
2189         len = p - par;
2190     }
2191 
2192     while ( 1 )
2193     {
2194         /* crop string */
2195         p [ 0 ] = 0;
2196 
2197         /* try to create directory */
2198         rc = make_dir ( path, access );
2199         if ( GetRCState ( rc ) != rcNotFound )
2200             break;
2201 
2202         /* back up some more */
2203         p = wcsrchr ( par, '\\' );
2204         if ( p == NULL )
2205         {
2206             p = par + wcslen ( par );
2207             break;
2208         }
2209     }
2210 
2211     par += len;
2212     assert ( p != NULL );
2213 
2214     /* create directories from here */
2215     if ( rc == 0 ) while ( p < par )
2216     {
2217         p [ 0 ] = '\\';
2218         rc = make_dir ( path, access );
2219         if ( rc != 0 || ++ p >= par )
2220             break;
2221         p += wcslen ( p );
2222     }
2223 
2224     /* repair stripped path */
2225     if ( strip )
2226         par [ 0 ] = '\\';
2227 
2228     return rc;
2229 
2230 #else
2231 
2232     rc_t rc;
2233     wchar_t *separator = path;
2234     bool finished;
2235 
2236     do
2237     {
2238         /* find the next separator */
2239         separator = wcschr( separator + 1, '\\' );
2240 
2241         /* we are finished, if not found */
2242         finished = (bool)( separator == NULL );
2243         if ( !finished )
2244         {
2245             /* temporary terminate at the separator */
2246             *separator = 0;
2247             rc = check_and_make( path, access );
2248             finished = (bool)( rc != 0 );
2249             /* put the terminator back in place... */
2250             *separator = '\\';
2251         }
2252     } while ( !finished );
2253 
2254     /* finally test and make the whole path... */
2255     rc = check_and_make( path, access );
2256 
2257     return rc;
2258 #endif
2259 }
2260 
2261 /* KSysDirCreateAlias
2262  *  creates a path alias according to create mode
2263  *
2264  *  "targ" [ IN ] - NUL terminated string in directory-native
2265  *  character set denoting target object
2266  *
2267  *  "alias" [ IN ] - NUL terminated string in directory-native
2268  *  character set denoting target alias
2269  *
2270  *  "access" [ IN ] - standard Unix directory access mode
2271  *  used when "mode" has kcmParents set and alias path does
2272  *  not exist.
2273  *
2274  *  "mode" [ IN ] - a creation mode ( see explanation above ).
2275  */
2276 static
KSysDirCreateAlias(KSysDir * self,uint32_t access,KCreateMode mode,const char * targ,const char * alias)2277 rc_t CC KSysDirCreateAlias ( KSysDir *self, uint32_t access, KCreateMode mode,
2278                              const char *targ, const char *alias )
2279 {
2280     wchar_t w_target[ MAX_PATH ];
2281     rc_t rc = KSysDirMakePath ( self, rcCreating, true, w_target, sizeof w_target, targ, NULL );
2282     if ( rc == 0 )
2283     {
2284         wchar_t w_alias[ MAX_PATH ];
2285         rc = KSysDirMakePath ( self, rcCreating, true, w_alias, sizeof w_alias, alias, NULL );
2286         if ( rc == 0 )
2287         {
2288             bool alias_ok = true;
2289             if ( ! has_lnk_extension( w_alias ) ) /* lnk_tools.c */
2290                 alias_ok = add_lnk_extension( w_alias, sizeof w_alias ); /* lnk_tools.c */
2291 
2292             if ( lnk_file_exists( w_alias ) )
2293             {
2294                 DeleteFileW( w_alias );
2295                 alias_ok = ( ! lnk_file_exists( w_alias ) );
2296             }
2297 
2298             if ( alias_ok )
2299             {
2300                 /* if "alias" is relative or "self" is chroot'd,
2301                    "w_alias" must be made relative */
2302                 if ( alias [ 0 ] != '/' || self -> root != 0 )
2303                 {
2304                     rc = KSysDirRelativePath ( self, rcCreating, w_alias, w_target, sizeof w_target );
2305                     if ( rc != 0 )
2306                         return rc;
2307                 }
2308                 if ( win_CreateLink( w_target, w_alias, NULL ) ) /* lnk_tools.c */
2309                     rc = 0;
2310                 else
2311                     rc = translate_file_error( GetLastError (), rcCreating );
2312             }
2313             else
2314                 rc = RC ( rcFS, rcDirectory, rcCreating, rcMemory, rcExhausted );
2315         }
2316     }
2317     return rc;
2318 }
2319 
2320 
2321 /* CreateLink ( v1.5 )
2322  *  creates a new link (also known as a hard link).
2323  *
2324  *  "access" [ IN ] - standard Unix directory access mode
2325  *  used when "mode" has kcmParents set and alias path does
2326  *  not exist.
2327  *
2328  *  "mode" [ IN ] - a creation mode ( see explanation above ).
2329  *
2330  *  "oldpath" [ IN ] - NUL terminated string in directory-native
2331  *  character set denoting existing object. THE PATH IS GIVEN RELATIVE
2332  *  TO DIRECTORY ( "self" ), NOT LINK ( "newpath" )!
2333  *
2334  *  "newpath" [ IN ] - NUL terminated string in directory-native
2335  *  character set denoting a new link.
2336  */
2337 static
KSysDirCreateLink(KSysDir * self,uint32_t access,KCreateMode mode,const char * oldpath,const char * newpath)2338 rc_t KSysDirCreateLink ( KSysDir * self, uint32_t access, KCreateMode mode,
2339     const char *oldpath, const char *newpath )
2340     {
2341     wchar_t w_target[ MAX_PATH ];
2342     rc_t rc = KSysDirMakePath ( self, rcCreating, true,
2343         w_target, sizeof w_target, oldpath, NULL );
2344     if ( rc == 0 )
2345     {
2346         wchar_t w_alias[ MAX_PATH ];
2347         rc = KSysDirMakePath ( self, rcCreating, true,
2348             w_alias, sizeof w_alias, newpath, NULL );
2349         if ( rc == 0 )
2350         {
2351             /* if "self" is chroot'd, "w_alias" must be made relative */
2352             if ( self -> root != 0 )
2353             {
2354                 rc = KSysDirRelativePath ( self, rcCreating, w_alias,
2355                     w_target, sizeof w_target );
2356                 if ( rc != 0 )
2357                     return rc;
2358             }
2359             if (CreateSymbolicLinkA( w_alias, w_target, 0x0 ) )
2360                 rc = 0;
2361             else
2362                 rc = translate_file_error( GetLastError (), rcCreating );
2363         }
2364     }
2365     return rc;
2366 }
2367 
2368 
2369 /* KSysDirOpenFileRead
2370  *  opens an existing file with read-only access
2371  *
2372  *  "f" [ OUT ] - return parameter for newly opened file
2373  *
2374  *  "path" [ IN ] - NUL terminated string in directory-native
2375  *  character set denoting target file
2376  */
2377 static
KSysDirOpenFileRead(const KSysDir * self,const KFile ** f,const char * path,va_list args)2378 rc_t CC KSysDirOpenFileRead ( const KSysDir *self,
2379     const KFile **f, const char *path, va_list args )
2380 {
2381     wchar_t file_name[ MAX_PATH ];
2382     rc_t rc = KSysDirMakePath( self, rcOpening, false, file_name, sizeof file_name, path, args );
2383     if ( rc == 0 )
2384     {
2385         HANDLE file_handle = CreateFileW( file_name, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
2386                                 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
2387         if ( file_handle == INVALID_HANDLE_VALUE )
2388         {
2389             rc = print_error_for( GetLastError(), file_name, "CreateFileW", rcOpening, klogInfo );
2390         }
2391         else
2392         {
2393             char buffer[ MAX_PATH ];
2394             wchar_2_char( file_name, buffer, sizeof buffer );
2395             rc = KSysFileMake ( ( KSysFile** ) f, file_handle, buffer, true, false );
2396             if ( rc != 0 )
2397                 CloseHandle ( file_handle );
2398         }
2399     }
2400     return rc;
2401 }
2402 
2403 /* KSysDirOpenFileWrite
2404  *  opens an existing file with write access
2405  *
2406  *  "f" [ OUT ] - return parameter for newly opened file
2407  *
2408  *  "path" [ IN ] - NUL terminated string in directory-native
2409  *  character set denoting target file
2410  *
2411  *  "update" [ IN ] - if non-zero, open in read/write mode
2412  *  otherwise, open in write-only mode
2413  */
2414 static
KSysDirOpenFileWrite(KSysDir * self,KFile ** f,bool update,const char * path,va_list args)2415 rc_t CC KSysDirOpenFileWrite ( KSysDir *self,
2416     KFile **f, bool update, const char *path, va_list args )
2417 {
2418     wchar_t file_name[ MAX_PATH ];
2419     rc_t rc = KSysDirMakePath ( self, rcOpening, false, file_name, sizeof file_name, path, args );
2420     if ( rc == 0 )
2421     {
2422         DWORD dwDesiredAccess = update ? GENERIC_READ | GENERIC_WRITE : GENERIC_WRITE;
2423         HANDLE file_handle = CreateFileW( file_name, dwDesiredAccess, FILE_SHARE_READ, NULL,
2424                                 OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
2425 
2426         if ( file_handle == INVALID_HANDLE_VALUE )
2427         {
2428             rc = print_error_for( GetLastError(), file_name, "CreateFileW", rcAccessing, klogErr );
2429 
2430         }
2431         else
2432         {
2433             char buffer[ MAX_PATH ];
2434             wchar_2_char( file_name, buffer, sizeof buffer );
2435             rc = KSysFileMake ( ( KSysFile** ) f, file_handle, buffer, update, true );
2436             if ( rc != 0 )
2437                 CloseHandle ( file_handle );
2438         }
2439     }
2440     return rc;
2441 }
2442 
2443 /* KSysDirOpenFileWrite
2444  *  opens an existing file with write access
2445  *
2446  *  "f" [ OUT ] - return parameter for newly opened file
2447  *
2448  *  "path" [ IN ] - NUL terminated string in directory-native
2449  *  character set denoting target file
2450  *
2451  *  "update" [ IN ] - if non-zero, open in read/write mode
2452  *  otherwise, open in write-only mode
2453  */
2454 static
KSysDirOpenFileSharedWrite(KSysDir * self,KFile ** f,bool update,const char * path,va_list args)2455 rc_t CC KSysDirOpenFileSharedWrite ( KSysDir *self,
2456     KFile **f, bool update, const char *path, va_list args )
2457 {
2458     wchar_t file_name[ MAX_PATH ];
2459     rc_t rc = KSysDirMakePath ( self, rcOpening, false, file_name, sizeof file_name, path, args );
2460     if ( rc == 0 )
2461     {
2462         DWORD dwDesiredAccess = update ? GENERIC_READ | GENERIC_WRITE : GENERIC_WRITE;
2463         HANDLE file_handle = CreateFileW( file_name, dwDesiredAccess, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
2464                                 OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
2465 
2466         if ( file_handle == INVALID_HANDLE_VALUE )
2467         {
2468             rc = print_error_for( GetLastError(), file_name, "CreateFileW", rcAccessing, klogErr );
2469 
2470         }
2471         else
2472         {
2473             char buffer[ MAX_PATH ];
2474             wchar_2_char( file_name, buffer, sizeof buffer );
2475             rc = KSysFileMake ( ( KSysFile** ) f, file_handle, buffer, update, true );
2476             if ( rc != 0 )
2477                 CloseHandle ( file_handle );
2478         }
2479     }
2480     return rc;
2481 }
2482 
2483 /* KSysDirCreateFile
2484  *  opens a file with write access
2485  *
2486  *  "f" [ OUT ] - return parameter for newly opened file
2487  *
2488  *  "path" [ IN ] - NUL terminated string in directory-native
2489  *  character set denoting target file
2490  *
2491  *  "access" [ IN ] - standard Unix access mode, e.g. 0664
2492  *
2493  *  "update" [ IN ] - if non-zero, open in read/write mode
2494  *  otherwise, open in write-only mode
2495  *
2496  *  "mode" [ IN ] - a creation mode ( see explanation above ).
2497  */
2498 static
KSysDirCreateFile(KSysDir * self,KFile ** f,bool update,uint32_t access,KCreateMode cmode,const char * path_fmt,va_list args)2499 rc_t CC KSysDirCreateFile ( KSysDir *self, KFile **f, bool update,
2500     uint32_t access, KCreateMode cmode, const char *path_fmt, va_list args )
2501 {
2502     wchar_t file_name[ MAX_PATH ];
2503     rc_t rc = KSysDirMakePath( self, rcCreating, true, file_name, sizeof file_name, path_fmt, args );
2504     if ( rc == 0 )
2505     {
2506         HANDLE file_handle;
2507         DWORD dwDesiredAccess = update ? GENERIC_READ | GENERIC_WRITE : GENERIC_WRITE;
2508         DWORD dwCreationDisposition = CREATE_ALWAYS;
2509         DWORD dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
2510         DWORD dwShareMode = FILE_SHARE_READ;
2511 
2512         switch ( cmode & kcmValueMask )
2513         {
2514         case kcmOpen : /* open if it exists, create if it does not exist */
2515             dwCreationDisposition = OPEN_ALWAYS;
2516             break;
2517 
2518         case kcmInit : /* always create, if it already exists truncate to zero */
2519             dwCreationDisposition = CREATE_ALWAYS;
2520             break;
2521 
2522         case kcmCreate : /* create and open only if does not already exist */
2523             dwCreationDisposition = CREATE_NEW;
2524             break;
2525         case kcmSharedAppend :
2526             dwCreationDisposition = OPEN_ALWAYS;
2527             dwDesiredAccess = FILE_APPEND_DATA;
2528             dwFlagsAndAttributes |= FILE_FLAG_WRITE_THROUGH;
2529             dwShareMode |= FILE_SHARE_WRITE;
2530             break;
2531         }
2532 
2533         file_handle = CreateFileW ( file_name, dwDesiredAccess, dwShareMode,
2534             NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL );
2535         while ( file_handle == INVALID_HANDLE_VALUE )
2536         {
2537             DWORD error;
2538 
2539             if ( ( cmode & kcmParents ) != 0 )
2540             {
2541                 /* maybe there were missing parent directories */
2542                 uint32_t dir_access = access |
2543                     ( ( access & 0444 ) >> 2 ) | ( ( access & 0222 ) >> 1 );
2544                 KSysDirCreateParents ( self, file_name, dir_access, true );
2545 
2546                 /* try creating the file again */
2547                 file_handle = CreateFileW ( file_name, dwDesiredAccess, dwShareMode,
2548                     NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL );
2549                 if ( file_handle != INVALID_HANDLE_VALUE )
2550                     break;
2551             }
2552 
2553             error = GetLastError();
2554             rc = translate_file_error( error, rcCreating );
2555 
2556             /* disabled 12/12/2012 : it prints an error message, if vdb tries to open
2557                the same reference-object twice via http. The lock-file for the 2nd try
2558                does already exist. This is not an error, just a condition. */
2559 
2560             /*
2561             PLOGERR ( klogErr,
2562                       ( klogErr, rc, "error CreateFileW - $(E) - $(C)",
2563                         "E=%!,C=%u", error, error ) );
2564             */
2565 
2566             /* Unix code has a special case when creating an empty file, which is
2567                to say, creating a directory entry without needing to write to file */
2568             return rc;
2569         }
2570 
2571         {
2572             char buffer[ MAX_PATH ];
2573             char path[4096];
2574             int size = ( args == NULL) ?
2575                 snprintf  ( path, sizeof path, "%s", path_fmt) :
2576                 vsnprintf ( path, sizeof path, path_fmt, args );
2577             if ( size < 0 || size >= ( int ) sizeof path )
2578                 rc = RC ( rcFS, rcFile, rcCreating, rcPath, rcExcessive );
2579             else
2580             {
2581                 wchar_2_char( file_name, buffer, sizeof buffer );
2582                 rc = KSysFileMake ( ( KSysFile** ) f, file_handle, path, update, true );
2583             }
2584             if ( rc != 0 )
2585                 CloseHandle ( file_handle );
2586         }
2587     }
2588     return rc;
2589 }
2590 
2591 /* KSysDirFileSize
2592  *  returns size in bytes of target file
2593  *
2594  *  "path" [ IN ] - NUL terminated string in directory-native
2595  *  character set denoting target file
2596  *
2597  *  "size" [ OUT ] - return parameter for file size
2598  */
2599 static
KSysDirFileSize(const KSysDir * self,uint64_t * size,const char * path,va_list args)2600 rc_t CC KSysDirFileSize ( const KSysDir *self,
2601     uint64_t *size, const char *path, va_list args )
2602 {
2603     wchar_t file_name[ MAX_PATH ];
2604     rc_t rc = KSysDirMakePath( self, rcAccessing, false, file_name, sizeof file_name, path, args );
2605     if ( rc == 0 )
2606     {
2607         WIN32_FILE_ATTRIBUTE_DATA file_data;
2608         if ( GetFileAttributesEx ( file_name, GetFileExInfoStandard, &file_data ) )
2609         {
2610             *size = file_data.nFileSizeHigh;
2611             *size <<= 32;
2612             *size |= file_data.nFileSizeLow;
2613         }
2614         else
2615         {
2616             rc = print_error_for( GetLastError(), file_name, "GetFileAttributesEx", rcAccessing, klogErr );
2617         }
2618     }
2619     return rc;
2620 }
2621 
2622 /* KSysDirSetFileSize
2623  *  sets size in bytes of target file
2624  *
2625  *  "path" [ IN ] - NUL terminated string in directory-native
2626  *  character set denoting target file
2627  *
2628  *  "size" [ IN ] - new file size
2629  */
2630 static
KSysDirSetFileSize(KSysDir * self,uint64_t size,const char * path,va_list args)2631 rc_t CC KSysDirSetFileSize ( KSysDir *self,
2632     uint64_t size, const char *path, va_list args )
2633 {
2634     wchar_t file_name[ MAX_PATH ];
2635     rc_t rc = KSysDirMakePath ( self, rcUpdating, false, file_name, sizeof file_name, path, args );
2636     if ( rc == 0 )
2637     {
2638         HANDLE file_handle = CreateFileW( file_name, GENERIC_READ | GENERIC_WRITE, 0, NULL,
2639                                 OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
2640         if ( file_handle != INVALID_HANDLE_VALUE )
2641         {
2642             DWORD file_pos_low, file_pos_high, file_set_res;
2643 
2644             file_pos_low = (DWORD)( size & 0xFFFFFFFF );
2645             size >>= 32;
2646             file_pos_high = (DWORD)( size & 0xFFFFFFFF );
2647             file_set_res = SetFilePointer ( file_handle, file_pos_low, (PLONG)&file_pos_high, FILE_BEGIN );
2648             if ( file_set_res != INVALID_SET_FILE_POINTER )
2649             {
2650                 if ( SetEndOfFile ( file_handle ) )
2651                     rc = 0; /* success !!! */
2652                 else
2653                     rc = translate_file_error( GetLastError(), rcUpdating );
2654             }
2655             CloseHandle ( file_handle );
2656         }
2657         else
2658         {
2659             rc = print_error_for( GetLastError(), file_name, "CreateFileW", rcUpdating, klogErr );
2660         }
2661     }
2662     return rc;
2663 }
2664 
2665 /* KSysDirOpenDirRead
2666  * KSysDirOpenDirUpdate
2667  *  opens a sub-directory
2668  *
2669  *  "path" [ IN ] - NUL terminated string in directory-native
2670  *  character set denoting target directory
2671  *
2672  *  "chroot" [ IN ] - if non-zero, the new directory becomes
2673  *  chroot'd and will interpret paths beginning with '/'
2674  *  relative to itself.
2675  */
2676 static
KSysDirOpenDirRead(const KSysDir * self,const KDirectory ** subp,bool chroot,const char * path,va_list args)2677 rc_t CC KSysDirOpenDirRead ( const KSysDir *self,
2678      const KDirectory **subp, bool chroot, const char *path, va_list args )
2679 {
2680     wchar_t dir_name[ MAX_PATH ];
2681     rc_t rc = KSysDirMakePath ( self, rcOpening, true, dir_name, sizeof dir_name, path, args );
2682     if ( rc == 0 )
2683     {
2684         int t;
2685         KSysDir *sub;
2686 
2687         size_t dir_size;
2688         uint32_t dir_length = utf16_string_measure( dir_name, &dir_size );
2689         uint32_t length_org = dir_length;
2690         while ( dir_length > 0 && dir_name [ dir_length - 1 ] == '/' )
2691             dir_name [ -- dir_length ] = 0;
2692         if ( dir_length != length_org )
2693             dir_length = utf16_string_measure( dir_name, &dir_size );
2694 
2695         t = KSysDirFullPathType ( dir_name ) & ( kptAlias - 1 );
2696         if ( t == kptNotFound )
2697             return RC ( rcFS, rcDirectory, rcOpening, rcPath, rcNotFound );
2698         if ( t != kptDir )
2699             return RC(rcFS, rcDirectory, rcOpening, rcPath, rcIncorrect );
2700 
2701         sub = KSysDirMake ( dir_size );
2702         if ( sub == NULL )
2703             rc = RC(rcFS, rcDirectory, rcOpening, rcMemory, rcExhausted );
2704         else
2705         {
2706             rc = KSysDirInit ( sub, rcOpening, self -> root, dir_name,
2707                                dir_size, dir_length, false, chroot );
2708             if ( rc == 0 )
2709             {
2710                 * subp = & sub -> dad;
2711                 return 0;
2712             }
2713 
2714             free ( sub );
2715         }
2716     }
2717     return rc;
2718 }
2719 
2720 static
KSysDirOpenDirUpdate(KSysDir * self,KDirectory ** subp,bool chroot,const char * path,va_list args)2721 rc_t CC KSysDirOpenDirUpdate ( KSysDir *self,
2722     KDirectory **subp, bool chroot, const char *path, va_list args )
2723 {
2724     wchar_t dir_name[ MAX_PATH ];
2725     rc_t rc = KSysDirMakePath ( self, rcOpening, true, dir_name, sizeof dir_name, path, args );
2726     if ( rc == 0 )
2727     {
2728         KSysDir *sub;
2729 
2730         size_t dir_size;
2731         uint32_t dir_length = utf16_string_measure( dir_name, &dir_size );
2732         uint32_t length_org = dir_length;
2733         while ( dir_length > 0 && dir_name [ dir_length - 1 ] == '/' )
2734             dir_name [ -- dir_length ] = 0;
2735         if ( dir_length != length_org )
2736             dir_length = utf16_string_measure( dir_name, &dir_size );
2737 
2738         switch ( KSysDirFullPathType ( dir_name ) )
2739         {
2740         case kptNotFound:
2741             return RC( rcFS, rcDirectory, rcOpening, rcPath, rcNotFound );
2742         case kptBadPath:
2743             return RC( rcFS, rcDirectory, rcOpening, rcPath, rcInvalid );
2744         case kptDir:
2745         case kptDir | kptAlias:
2746             break;
2747         default:
2748             return RC( rcFS, rcDirectory, rcOpening, rcPath, rcIncorrect );
2749         }
2750 
2751         sub = KSysDirMake ( dir_size );
2752         if ( sub == NULL )
2753             rc = RC( rcFS, rcDirectory, rcOpening, rcMemory, rcExhausted );
2754         else
2755         {
2756             rc = KSysDirInit ( sub, rcOpening, self -> root, dir_name,
2757                                dir_size, dir_length, true, chroot );
2758             if ( rc == 0 )
2759             {
2760                 * subp = & sub -> dad;
2761                 return 0;
2762             }
2763 
2764             free ( sub );
2765         }
2766     }
2767     return rc;
2768 }
2769 
2770 /* KSysDirCreateDir
2771  *  create a sub-directory
2772  *
2773  *  "path" [ IN ] - NUL terminated string in directory-native
2774  *  character set denoting target directory
2775  *
2776  *  "access" [ IN ] - standard Unix directory permissions
2777  *
2778  *  "mode" [ IN ] - a creation mode ( see explanation above ).
2779  */
2780 static
KSysDirCreateDir(KSysDir * self,uint32_t access,KCreateMode mode,const char * path,va_list args)2781 rc_t CC KSysDirCreateDir ( KSysDir *self,
2782     uint32_t access, KCreateMode mode, const char *path, va_list args )
2783 {
2784     wchar_t dir_name[ MAX_PATH ];
2785     rc_t rc = KSysDirMakePath ( self, rcCreating, true, dir_name, sizeof dir_name, path, args );
2786     if ( rc == 0 )
2787     {
2788         if ( ( mode & kcmValueMask ) == kcmCreate )
2789         {
2790             switch ( KSysDirFullPathType ( dir_name ) )
2791             {
2792             case kptNotFound:
2793                 break;
2794             case kptBadPath:
2795                 return RC(rcFS, rcDirectory, rcCreating, rcPath, rcInvalid );
2796             case kptDir:
2797                 return RC(rcFS, rcDirectory, rcCreating, rcDirectory, rcExists );
2798             default:
2799                 return RC(rcFS, rcDirectory, rcCreating, rcPath, rcIncorrect );
2800             }
2801         }
2802         rc = make_dir ( dir_name, access );
2803         if ( rc != 0 )
2804         {
2805             switch ( GetRCState ( rc ) )
2806             {
2807             case rcExists:
2808                 rc = 0;
2809                 if ( ( mode & kcmValueMask ) == kcmInit )
2810                     rc = KSysDirEmptyDir ( dir_name, sizeof dir_name, 1 );
2811                 break;
2812             case rcNotFound:
2813                 if ( ( mode & kcmParents ) != 0 )
2814                     rc = KSysDirCreateParents ( self, dir_name, access, false );
2815                 break;
2816             }
2817         }
2818     }
2819     return rc;
2820 }
2821 
2822 /* KSysDirDate
2823  *  get access to object
2824  *
2825  *  "date" [ OUT ] - return parameter for Unix access mode
2826  *
2827  *  "path" [ IN ] - NUL terminated string in directory-native
2828  *  character set denoting target object
2829  */
2830 static
KSysDirVDate(const KSysDir * self,KTime_t * date,const char * path,va_list args)2831 rc_t CC KSysDirVDate ( const KSysDir *self,
2832     KTime_t * date, const char *path, va_list args )
2833 {
2834     wchar_t full [ MAX_PATH ];
2835     rc_t rc = KSysDirMakePath ( self, rcAccessing, false, full, sizeof full, path, args );
2836     if ( rc == 0 )
2837     {
2838         rc = get_attributes ( full, NULL, date );
2839     }
2840     return rc;
2841 }
2842 
2843 
2844 static
change_item_date(wchar_t * path,LPFILETIME win_time,bool dir_flag)2845 rc_t change_item_date( wchar_t *path, LPFILETIME win_time, bool dir_flag )
2846 {
2847     rc_t rc;
2848     HANDLE file_handle;
2849 
2850     if ( dir_flag )
2851         file_handle = CreateFileW( path, GENERIC_READ | GENERIC_WRITE, 0, NULL,
2852                                    OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL );
2853     else
2854         file_handle = CreateFileW( path, GENERIC_READ | GENERIC_WRITE, 0, NULL,
2855                                    OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
2856     if ( file_handle == INVALID_HANDLE_VALUE )
2857     {
2858         rc = print_error_for( GetLastError(), path, "CreateFileW", rcUpdating, klogErr );
2859     }
2860     else
2861     {
2862         if ( SetFileTime ( file_handle, NULL, NULL, win_time ) )
2863         {
2864             rc = 0;
2865         }
2866         else
2867         {
2868             rc = print_error_for( GetLastError(), path, "SetFileTime", rcUpdating, klogErr );
2869         }
2870         CloseHandle ( file_handle );
2871     }
2872 
2873     return rc;
2874 }
2875 
2876 
2877 static
change_dir_date(wchar_t * path,size_t path_max,LPFILETIME win_time,bool recurse)2878 rc_t change_dir_date( wchar_t *path, size_t path_max, LPFILETIME win_time, bool recurse )
2879 {
2880     KSysDirEnum list;
2881     const wchar_t *leaf;
2882     size_t path_size;
2883     uint32_t path_length;
2884 
2885     rc_t rc = change_item_date( path, win_time, true );
2886     if ( rc != 0 || !recurse )
2887         return rc;
2888 
2889     path_length = wchar_string_measure ( path, &path_size );
2890     if ( ( path_size + 10 ) > path_max )
2891         return RC( rcFS, rcDirectory, rcListing, rcMemory, rcExhausted );
2892 
2893     rc = KSysDirEnumInitAll ( & list, path, path_length );
2894     if ( rc != 0 )
2895         return ResetRCContext ( rc, rcFS, rcDirectory, rcUpdating );
2896 
2897     /* we keep only the appended '\\' for the loop... */
2898     path_length++;
2899     path_size += sizeof *path;
2900 
2901     for ( leaf = KSysDirEnumNext( &list );
2902           leaf != NULL && rc == 0;
2903           leaf = KSysDirEnumNext( &list ) )
2904     {
2905         size_t leaf_size;
2906         int32_t path_type;
2907         uint32_t leaf_length = wchar_string_measure ( leaf, &leaf_size );
2908         if ( path_size + leaf_size >= path_max )
2909             rc = RC ( rcFS, rcDirectory, rcUpdating, rcPath, rcExcessive );
2910         else
2911         {
2912 
2913             /* wcscpy adds termination, so wprintf is safe to call */
2914             wcscpy ( & path [ path_length ], leaf );
2915 
2916             path_type = KSysDirFullPathType ( path );
2917             switch( path_type )
2918             {
2919             case kptFile : rc = change_item_date( path, win_time, false );
2920                            break;
2921             case kptDir  : rc = change_dir_date( path, path_max, win_time, true );
2922                            break;
2923             }
2924         }
2925     }
2926 
2927     KSysDirEnumWhack ( & list );
2928     /* restore the original path... */
2929     path [ path_length - 1 ] = 0;
2930 
2931     return rc;
2932 }
2933 
2934 
2935 static
KSysDirChangeDate(wchar_t * path,size_t path_max,KTime_t date,bool recurse)2936 rc_t KSysDirChangeDate ( wchar_t *path, size_t path_max,
2937             KTime_t date, bool recurse )
2938 {
2939     FILETIME win_time;
2940     int32_t path_type;
2941     rc_t rc;
2942 
2943     KTimeToWinTime ( date, &win_time );
2944     path_type = KSysDirFullPathType ( path );
2945     switch( path_type )
2946     {
2947     case kptFile : rc = change_item_date( path, &win_time, false );
2948                    break;
2949 
2950     case kptDir  : rc = change_dir_date( path, path_max, &win_time, recurse );
2951                    break;
2952 
2953     default      : rc = RC( rcFS, rcDirectory, rcUpdating, rcNoObj, rcUnsupported );
2954                    break;
2955     }
2956     return rc;
2957 }
2958 
2959 
2960 /*
2961     struct stat st;
2962     struct utimbuf u;
2963 
2964     if ( stat ( path, & st ) != 0 ) switch ( errno )
2965     {
2966     case ENOENT:
2967         return RC ( rcFS, rcDirectory, rcUpdating, rcPath, rcNotFound );
2968     case ENOTDIR:
2969     case ELOOP:
2970         return RC ( rcFS, rcDirectory, rcUpdating, rcPath, rcInvalid );
2971     case ENAMETOOLONG:
2972         return RC ( rcFS, rcDirectory, rcUpdating, rcPath, rcExcessive );
2973     case EACCES:
2974         return RC ( rcFS, rcDirectory, rcUpdating, rcDirectory, rcUnauthorized );
2975     case ENOMEM:
2976         return RC ( rcFS, rcDirectory, rcUpdating, rcMemory, rcExhausted );
2977     default:
2978         return RC ( rcFS, rcDirectory, rcUpdating, rcNoObj, rcUnknown );
2979     }
2980     u . actime = u . modtime = date;
2981 
2982     if ( recurse && S_ISDIR ( st . st_mode ) )
2983     {
2984         rc_t rc;
2985 
2986     rc = KSysDirChangeEntryDate ( path, path_max, & u );
2987     if ( rc != 0 )
2988         return rc;
2989 
2990     rc = KSysDirChangeDirDate ( path, path_max, date );
2991         if ( rc == 0 )
2992         {
2993         rc = KSysDirChangeEntryDate ( path, path_max, & u  );
2994         }
2995         return rc;
2996     }
2997 
2998     return  KSysDirChangeEntryDate ( path, path_max, & u );
2999 */
3000 
3001 static
KSysDirVSetDate(KSysDir * self,bool recurse,KTime_t date,const char * path,va_list args)3002 rc_t CC KSysDirVSetDate ( KSysDir * self, bool recurse,
3003     KTime_t date, const char *path, va_list args )
3004 {
3005     wchar_t full [ MAX_PATH ];
3006     rc_t rc = KSysDirMakePath ( self, rcUpdating, false, full, sizeof full, path, args );
3007     if ( rc == 0 )
3008     {
3009         rc = KSysDirChangeDate ( full, sizeof full, date, recurse );
3010     }
3011     return rc;
3012 }
3013 
3014 static
KSysDirGetSysdir(const KSysDir * cself)3015 KSysDir *CC KSysDirGetSysdir ( const KSysDir *cself )
3016 {
3017     return ( KSysDir* ) cself;
3018 }
3019 
3020 
3021 /* FileLocator
3022  *  returns a 64-bit key pertinent only to the particular file
3023  *  system device holding that file.
3024  *
3025  *  It can be used as a form of sort key except that it is not
3026  *  guaranteed to be unique.
3027  *
3028  *  "locator" [ OUT ] - return parameter for file locator
3029  *
3030  *  "path" [ IN ] - NUL terminated string in directory-native
3031  *  character set denoting target file
3032  */
3033 static
KSysDirFileLocator_v1(const KSysDir_v1 * self,uint64_t * locator,const char * path,va_list args)3034 rc_t CC KSysDirFileLocator_v1 ( const KSysDir_v1 *self,
3035     uint64_t *locator, const char *path, va_list args )
3036 {
3037     /* TBD - could return an inode or equivalent */
3038     assert ( locator != NULL );
3039     * locator = 0;
3040     return RC ( rcFS, rcDirectory, rcAccessing, rcFunction, rcUnsupported );
3041 }
3042 
3043 /* FilePhysicalSize
3044  *  returns physical allocated size in bytes of target file.  It might
3045  * or might not differ from FileSize
3046  *
3047  *  "size" [ OUT ] - return parameter for file size
3048  *
3049  *  "path" [ IN ] - NUL terminated string in directory-native
3050  *  character set denoting target file
3051  */
3052 static
KSysDirFilePhysicalSize_v1(const KSysDir_v1 * self,uint64_t * size,const char * path,va_list args)3053 rc_t CC KSysDirFilePhysicalSize_v1 ( const KSysDir_v1 *self,
3054     uint64_t *size, const char *path, va_list args )
3055 {
3056     /* TBD - can be completed */
3057     assert ( size != NULL );
3058     * size = 0;
3059     return RC ( rcFS, rcDirectory, rcAccessing, rcFunction, rcUnsupported );
3060 }
3061 
3062 /* FileContiguous
3063  *  returns true if the file is "contiguous".  Chunked or sparse files are not
3064  *  contiguous while most data files are.  Virtual generated files would likely
3065  *  not be contiguous.
3066  *
3067  *  "contiguous" [ OUT ] - return parameter for file contiguous
3068  *
3069  *  "path" [ IN ] - NUL terminated string in directory-native
3070  *  character set denoting target file
3071  */
3072 static
KSysDirFileContiguous_v1(const KSysDir_v1 * self,bool * contiguous,const char * path,va_list args)3073 rc_t CC KSysDirFileContiguous_v1 ( const KSysDir_v1 *self,
3074     bool *contiguous, const char *path, va_list args )
3075 {
3076     assert ( contiguous != NULL );
3077     * contiguous = true;
3078     return 0;
3079 }
3080 
3081 static KDirectory_vt_v1 vtKSysDir =
3082 {
3083     /* version 1.5 */
3084     1, 5,
3085 
3086     /* start minor version 0*/
3087     KSysDirDestroy,
3088     KSysDirList,
3089 
3090     /* the following two messages map to the same method, requiring type casting */
3091     ( rc_t ( CC * )  ( const KSysDir*, bool,
3092         rc_t ( CC * ) ( const KDirectory*, uint32_t, const char*, void* ), void*,
3093         const char*, va_list ) ) KSysDirVisit,
3094     ( rc_t ( CC * )  ( KSysDir*, bool,
3095         rc_t ( CC * ) ( KDirectory*, uint32_t, const char*, void* ), void*,
3096         const char*, va_list ) ) KSysDirVisit,
3097 
3098     KSysDirPathType,
3099     KSysDirResolvePath,
3100     KSysDirResolveAlias,
3101     KSysDirRename,
3102     KSysDirRemove,
3103     KSysDirClearDir,
3104     KSysDirVAccess,
3105     KSysDirSetAccess,
3106     KSysDirCreateAlias,
3107     KSysDirOpenFileRead,
3108     KSysDirOpenFileWrite,
3109     KSysDirCreateFile,
3110     KSysDirFileSize,
3111     KSysDirSetFileSize,
3112     KSysDirOpenDirRead,
3113     KSysDirOpenDirUpdate,
3114     KSysDirCreateDir,
3115     NULL, /* we don't track files*/
3116     /* end minor version 0 */
3117 
3118     /* start minor version 1 */
3119     KSysDirVDate,
3120     KSysDirVSetDate,
3121     KSysDirGetSysdir,
3122     /* end minor version 1 */
3123 
3124     /* start minor version 2 */
3125     KSysDirFileLocator_v1,
3126     /* end minor version 2 */
3127 
3128     /* start minor version 3 */
3129     KSysDirFilePhysicalSize_v1,
3130     KSysDirFileContiguous_v1,
3131     /* end minor version 3 */
3132 
3133     /* start minor version 4 */
3134     KSysDirOpenFileSharedWrite,
3135     /* end minor version 4 */
3136 
3137     /* start minor version 5 */
3138     KSysDirCreateLink,
3139     /* end minor version 5 */
3140 };
3141 
3142 /* KSysDirInit
3143  */
3144 #if TRACK_REFERENCES
3145 static
convert_wide_path(const wchar_t * path,const size_t path_size)3146 const char *convert_wide_path ( const wchar_t *path, const size_t path_size )
3147 {
3148     /* copy wide string to static */
3149     static char static_path [ MAX_PATH ];
3150     wchar_cvt_string_copy ( static_path, sizeof static_path, path, path_size );
3151 
3152     return static_path;
3153 }
3154 #else
3155 #define convert_wide_path( path, path_size ) "ignore"
3156 #endif
3157 
3158 static
KSysDirInit(KSysDir * self,enum RCContext ctx,uint32_t dad_root,const wchar_t * path,size_t path_size,uint32_t path_length,bool update,bool chroot)3159 rc_t KSysDirInit ( KSysDir *self, enum RCContext ctx, uint32_t dad_root,
3160                    const wchar_t *path, size_t path_size, uint32_t path_length,
3161                    bool update, bool chroot )
3162 {
3163     rc_t rc;
3164     if ( path == NULL )
3165     {
3166         rc = KDirectoryInit( &self->dad, (const KDirectory_vt*)&vtKSysDir,
3167                             "KSysDir", NULL, update );
3168     }
3169     else
3170     {
3171         rc = KDirectoryInit( &self->dad, (const KDirectory_vt*)&vtKSysDir,
3172                              "KSysDir", convert_wide_path ( path, path_size ), update );
3173     }
3174 
3175     if ( rc != 0 )
3176     {
3177         return ResetRCContext ( rc, rcFS, rcDirectory, ctx );
3178     }
3179 
3180     if ( path != NULL )
3181     {
3182         memmove( self->path, path, path_size );
3183     }
3184 
3185     self->root = chroot ? path_length : dad_root;
3186     self->length = path_length + 1;
3187     self->path[ path_length ] = '\\';
3188     self->path[ path_length + 1 ] = 0;
3189     return 0;
3190 }
3191 
3192 
3193 /* MakeFromRealPath
3194  *  creates a KDirectory from a Windows path
3195  */
KDirectoryMakeFromRealPath(KDirectory ** dirp,const wchar_t * real,bool update,bool chroot)3196 rc_t KDirectoryMakeFromRealPath ( KDirectory **dirp, const wchar_t *real, bool update, bool chroot )
3197 {
3198     rc_t rc;
3199     size_t size;
3200     uint32_t length = wchar_string_measure ( real, & size );
3201     if ( length + 4 > MAX_PATH )
3202         rc = RC ( rcFS, rcDirectory, rcCreating, rcPath, rcExcessive );
3203     else
3204     {
3205         KSysDir *dir = KSysDirMake ( size );
3206         if ( dir == NULL )
3207             rc = RC ( rcFS, rcDirectory, rcAccessing, rcMemory, rcExhausted );
3208         else
3209         {
3210             rc = KSysDirInit ( dir, rcAccessing, 0, real, size, length, update, chroot );
3211             if ( rc == 0 )
3212             {
3213                 * dirp = & dir -> dad;
3214                 return 0;
3215             }
3216             KSysDirDestroy ( dir );
3217         }
3218     }
3219 
3220     * dirp = NULL;
3221     return rc;
3222 }
3223 
3224 /* KDirectoryNativeDir
3225  *  returns a native file-system directory node reference
3226  *  the directory root will be "/" and set to the native
3227  *  idea of current working directory
3228  *
3229  *  NB - the returned reference will be non-const, allowing
3230  *  modification operations to be attempted. these operations
3231  *  may still fail if the underlying FS disallows them.
3232  *
3233  *  "dir" [ OUT ] - return parameter for native directory
3234  */
3235 extern rc_t CC ReportCWD ( const ReportFuncs *f, uint32_t indent );
3236 extern rc_t CC ReportRedirect ( KWrtHandler* handler,
3237     const char* filename, bool* to_file, bool finalize );
3238 
KDirectoryNativeDir(KDirectory ** dirp)3239 LIB_EXPORT rc_t CC KDirectoryNativeDir ( KDirectory **dirp )
3240 {
3241     rc_t rc;
3242 
3243     static bool latch;
3244     if ( ! latch )
3245     {
3246         ReportInitKFS ( ReportCWD, ReportRedirect );
3247         latch = true;
3248     }
3249 
3250     if ( dirp == NULL )
3251         rc = RC ( rcFS, rcDirectory, rcAccessing, rcParam, rcNull );
3252     else
3253     {
3254         wchar_t wd [ MAX_PATH ];
3255         DWORD error;
3256         DWORD wd_len = GetCurrentDirectoryW ( sizeof wd / sizeof wd [ 0 ], wd );
3257         if ( wd_len != 0 )
3258             return KDirectoryMakeFromRealPath ( dirp, wd, true, false );
3259 
3260         error = GetLastError();
3261         switch ( error )
3262         {
3263         case ERROR_ACCESS_DENIED:
3264             rc = RC ( rcFS, rcDirectory, rcAccessing, rcDirectory, rcUnauthorized );
3265             break;
3266         default:
3267             rc = RC ( rcFS, rcDirectory, rcAccessing, rcNoObj, rcUnknown );
3268         }
3269         PLOGERR ( klogErr,
3270                   ( klogErr, rc, "error GetCurrentDirectoryW - $(E) - $(C)",
3271                     "E=%!,C=%u", error, error ) );
3272 
3273         * dirp = NULL;
3274     }
3275 
3276     return rc;
3277 }
3278 
3279 
3280 /* RealPath
3281  *  exposes functionality of system directory
3282  */
KSysDirRealPath(struct KSysDir const * self,char * real,size_t bsize,const char * path,...)3283 LIB_EXPORT rc_t CC KSysDirRealPath ( struct KSysDir const *self,
3284     char *real, size_t bsize, const char *path, ... )
3285 {
3286     rc_t rc;
3287     va_list args;
3288 
3289     va_start ( args, path );
3290     rc = KSysDirVRealPath ( self, real, bsize, path, args );
3291     va_end ( args );
3292 
3293     return rc;
3294 }
3295 
KSysDirVRealPath(struct KSysDir const * self,char * real,size_t bsize,const char * path,va_list args)3296 LIB_EXPORT rc_t CC KSysDirVRealPath ( struct KSysDir const *self,
3297     char *real, size_t bsize, const char *path, va_list args )
3298 {
3299     /* Windows is ... challenged when it comes to answering
3300        this question. What is needed is to 1) convert the path
3301        to a Windows-style wchar path, then 2) resolve each of
3302        its components, etc. to come up with a real path, then
3303        3) rewrite the path as a UTF-8 POSIX path */
3304     return KSysDirResolvePath ( self, true, real, bsize, path, args );
3305 }
3306 
KDirectoryGetDiskFreeSpace_v1(const KDirectory * self,uint64_t * free_bytes_available,uint64_t * total_number_of_bytes)3307 LIB_EXPORT rc_t CC KDirectoryGetDiskFreeSpace_v1 ( const KDirectory * self,
3308     uint64_t * free_bytes_available, uint64_t * total_number_of_bytes )
3309 {
3310     if ( self == NULL )
3311         return RC ( rcFS, rcDirectory, rcAccessing, rcSelf, rcNull );
3312     else {
3313         KSysDir_v1 * dir = ( KSysDir_v1 * ) self;
3314 
3315 	LPCTSTR lpszMultibyte = dir -> path;
3316 
3317 	unsigned __int64 i64FreeBytesToCaller = 0;
3318 	unsigned __int64 i64TotalBytes = 0;
3319 	unsigned __int64 i64FreeBytes = 0;
3320 
3321 	if ( GetDiskFreeSpaceEx (lpszMultibyte,
3322 	    ( PULARGE_INTEGER ) & i64FreeBytesToCaller,
3323             ( PULARGE_INTEGER ) & i64TotalBytes,
3324             ( PULARGE_INTEGER ) & i64FreeBytes ) )
3325 	{
3326             if ( free_bytes_available != NULL ) {
3327                 * free_bytes_available  = i64FreeBytes;
3328             }
3329             if ( total_number_of_bytes != NULL ) {
3330                 * total_number_of_bytes = i64TotalBytes;
3331             }
3332             return 0;
3333         }
3334 
3335         return RC ( rcFS, rcDirectory, rcAccessing, rcError, rcUnknown );
3336     }
3337 }
3338