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