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 #define TRACK_REFERENCES 0
28
29 /*--------------------------------------------------------------------------
30 * forwards
31 */
32 struct KSysDir_v1;
33 struct KSysDirListing;
34
35 #define KDIR_IMPL struct KSysDir_v1
36 #define KNAMELIST_IMPL struct KSysDirListing
37
38 #include <kfs/extern.h>
39 #include "sysdir-priv.h"
40 #include "sysfile-priv.h"
41
42 #include <klib/debug.h> /* DBGMSG */
43 #include <klib/impl.h>
44 #include <klib/klib-priv.h>
45 #include <klib/log.h>
46 #include <klib/out.h>
47 #include <klib/rc.h>
48 #include <klib/sort.h>
49
50 #include <sysalloc.h>
51
52 #include "os-native.h"
53
54 #ifndef __USE_UNIX98
55 #define __USE_UNIX98 1
56 #endif
57 #include <unistd.h>
58
59 /* old Sun includes won't define PATH_MAX */
60 #ifndef __XOPEN_OR_POSIX
61 #define __XOPEN_OR_POSIX 1
62 #endif
63
64 #include <limits.h>
65
66 /* now they won't define lstat */
67 #undef __XOPEN_OR_POSIX
68
69 #include <stdlib.h>
70 #include <stdio.h>
71 #include <string.h>
72 #include <errno.h>
73 #include <fcntl.h>
74 #include <dirent.h>
75 #include <sys/stat.h>
76 #include <sys/types.h>
77 #include <sys/statvfs.h> /* statvfs */
78 #include <utime.h>
79 #include <assert.h>
80
81
82 /*--------------------------------------------------------------------------
83 * KSysDirEnum
84 * a Unix directory enumerator
85 */
86 typedef struct KSysDirEnum KSysDirEnum;
87 struct KSysDirEnum
88 {
89 DIR *dir;
90 };
91
92 /* Whack
93 */
94 static
KSysDirEnumWhack(KSysDirEnum * self)95 void KSysDirEnumWhack ( KSysDirEnum *self )
96 {
97 closedir ( self -> dir );
98 }
99
100 /* Init
101 */
102 static
KSysDirEnumInit(KSysDirEnum * self,const char * path)103 rc_t KSysDirEnumInit ( KSysDirEnum *self, const char *path )
104 {
105 self -> dir = opendir ( path );
106 if ( self -> dir != NULL )
107 return 0;
108
109 switch ( errno )
110 {
111 case EACCES:
112 return RC ( rcFS, rcDirectory, rcListing, rcDirectory, rcUnauthorized );
113 case EMFILE:
114 case ENFILE:
115 return RC ( rcFS, rcDirectory, rcListing, rcFileDesc, rcExhausted );
116 case ENOENT:
117 return RC ( rcFS, rcDirectory, rcListing, rcPath, rcNotFound );
118 case ENOMEM:
119 return RC ( rcFS, rcDirectory, rcListing, rcMemory, rcExhausted );
120 case ENOTDIR:
121 return RC ( rcFS, rcDirectory, rcListing, rcPath, rcIncorrect );
122 }
123
124 return RC ( rcFS, rcDirectory, rcListing, rcNoObj, rcUnknown );
125 }
126
127 /* Next
128 */
129 static
KSysDirEnumNext(const KSysDirEnum * self)130 const char *KSysDirEnumNext ( const KSysDirEnum *self )
131 {
132 while ( 1 )
133 {
134 struct dirent *e = readdir ( self -> dir );
135 if ( e == NULL )
136 break;
137
138 if ( e -> d_name [ 0 ] == '.' )
139 {
140 switch ( e -> d_name [ 1 ] )
141 {
142 case 0:
143 continue;
144 case '.':
145 if ( e -> d_name [ 2 ] == 0 )
146 continue;
147 break;
148 }
149 }
150
151 return e -> d_name;
152 }
153
154 return NULL;
155 }
156
157
158 /*--------------------------------------------------------------------------
159 * KSysDirListing
160 * a Unix directory listing
161 */
162 typedef struct KSysDirListing KSysDirListing;
163 struct KSysDirListing
164 {
165 KNamelist dad;
166 const char **namelist;
167 int cnt;
168 };
169
170 /* Whack
171 */
172 static
KSysDirListingWhack(const KSysDirListing * self)173 rc_t KSysDirListingWhack ( const KSysDirListing *self )
174 {
175 int i;
176 for ( i = 0; i < self -> cnt; ++ i )
177 free ( ( void* ) self -> namelist [ i ] );
178 free ( self -> namelist );
179 return 0;
180 }
181
182 static
KSysDirListingDestroy(KSysDirListing * self)183 rc_t KSysDirListingDestroy ( KSysDirListing *self )
184 {
185 rc_t rc = KSysDirListingWhack ( self );
186 if ( rc == 0 )
187 free ( self );
188 return rc;
189 }
190
191 /* Count
192 */
193 static
KSysDirListingCount(const KSysDirListing * self,uint32_t * count)194 rc_t KSysDirListingCount ( const KSysDirListing *self, uint32_t *count )
195 {
196 * count = self -> cnt;
197 return 0;
198 }
199
200 /* Get
201 */
202 static
KSysDirListingGet(const KSysDirListing * self,uint32_t idx,const char ** name)203 rc_t KSysDirListingGet ( const KSysDirListing *self, uint32_t idx, const char **name )
204 {
205 if ( idx >= ( uint32_t ) self -> cnt )
206 return RC ( rcFS, rcNamelist, rcAccessing, rcParam, rcExcessive );
207 * name = self -> namelist [ idx ];
208 return 0;
209 }
210
211 /* Init
212 */
213 static KNamelist_vt_v1 vtKSysDirListing =
214 {
215 /* version 1.0 */
216 1, 0,
217
218 /* start minor version 0 methods */
219 KSysDirListingDestroy,
220 KSysDirListingCount,
221 KSysDirListingGet
222 /* end minor version 0 methods */
223 };
224
225 static
KSysDirListingSort(const void * a,const void * b,void * ignored)226 int64_t KSysDirListingSort ( const void *a, const void *b, void * ignored )
227 {
228 return strcmp ( * ( const char** ) a, * ( const char** ) b );
229 }
230
231 static
KSysDirListingInit(KSysDirListing * self,const char * path,const KDirectory_v1 * dir,bool (* f)(const KDirectory_v1 *,const char *,void *),void * data)232 rc_t KSysDirListingInit ( KSysDirListing *self, const char *path, const KDirectory_v1 *dir,
233 bool ( * f ) ( const KDirectory_v1*, const char*, void* ), void *data )
234 {
235 rc_t rc;
236
237 self -> namelist = NULL;
238 self -> cnt = 0;
239
240 rc = KNamelistInit ( & self -> dad,
241 ( const KNamelist_vt* ) & vtKSysDirListing );
242 if ( rc == 0 )
243 {
244 KSysDirEnum list;
245 rc = KSysDirEnumInit ( & list, path );
246 if ( rc == 0 )
247 {
248 uint32_t len = 512;
249 self -> namelist = malloc ( len * sizeof self -> namelist [ 0 ] );
250 if ( self -> namelist == NULL )
251 rc = RC ( rcFS, rcDirectory, rcListing, rcMemory, rcExhausted );
252 else
253 {
254 void *r;
255 const char *name;
256 while ( ( name = KSysDirEnumNext ( & list ) ) != NULL )
257 {
258 if ( f != NULL )
259 {
260 if ( ! ( * f ) ( dir, name, data ) )
261 continue;
262 }
263
264 if ( self -> cnt == len )
265 {
266 len += len;
267 r = realloc ( self -> namelist,
268 len * sizeof self -> namelist [ 0 ] );
269 if ( r == NULL )
270 {
271 rc = RC ( rcFS, rcDirectory, rcListing, rcMemory, rcExhausted );
272 break;
273 }
274 self -> namelist = r;
275 }
276
277 self -> namelist [ self -> cnt ] = malloc ( strlen ( name ) + 1 );
278 if ( self -> namelist [ self -> cnt ] == NULL )
279 {
280 rc = RC ( rcFS, rcDirectory, rcListing, rcMemory, rcExhausted );
281 break;
282 }
283 strcpy ( ( char* ) self -> namelist [ self -> cnt ], name );
284 ++ self -> cnt;
285 }
286
287 if ( rc == 0 )
288 {
289 r = realloc ( self -> namelist,
290 self -> cnt * sizeof self -> namelist [ 0 ] );
291 if ( r != NULL )
292 {
293 self -> namelist = r;
294 ksort ( r, self -> cnt, sizeof self -> namelist [ 0 ], KSysDirListingSort, NULL );
295 }
296 else if ( self -> cnt != 0 )
297 {
298 rc = RC ( rcFS, rcDirectory, rcListing, rcMemory, rcExhausted );
299 }
300 else
301 {
302 self -> namelist = r;
303 }
304 }
305
306 if ( rc != 0 )
307 {
308 KSysDirListingWhack ( self );
309 self -> namelist = NULL;
310 self -> cnt = 0;
311 }
312 }
313
314 KSysDirEnumWhack ( & list );
315 }
316 }
317 return rc;
318 }
319
320 /*--------------------------------------------------------------------------
321 * KSysDir
322 * a Unix directory
323 */
324 struct KSysDir_v1
325 {
326 KDirectory_v1 dad;
327 uint32_t root;
328 uint32_t size;
329 char path [ PATH_MAX ];
330 };
331
332 /* KSysDirMake
333 * allocate an uninialized object
334 */
335 static
KSysDirMake_v1(size_t path_size)336 KSysDir_v1 *KSysDirMake_v1 ( size_t path_size )
337 {
338 KSysDir_v1 *dir = malloc ( ( sizeof * dir - sizeof dir -> path + 2 ) + path_size );
339 return dir;
340 }
341
342 /* KSysDirDestroy
343 */
344 static
KSysDirDestroy_v1(KSysDir_v1 * self)345 rc_t KSysDirDestroy_v1 ( KSysDir_v1 * self )
346 {
347 free ( self );
348 return 0;
349 }
350
351 /* KSysDirInit
352 */
353 static
354 rc_t KSysDirInit_v1 ( KSysDir_v1 * self, enum RCContext ctx, uint32_t dad_root,
355 const char *path, uint32_t path_size, bool update, bool chroot );
356
357
358 /* KSysDirCanonPath
359 */
360 static
KSysDirCanonPath_v1(const KSysDir_v1 * self,enum RCContext ctx,char * path,size_t psize)361 rc_t KSysDirCanonPath_v1 ( const KSysDir_v1 * self, enum RCContext ctx, char *path, size_t psize )
362 {
363 char *low, *dst, *last, *end = path + psize;
364 low = dst = last = path + self -> root;
365
366 while ( 1 )
367 {
368 char *src = strchr ( last + 1, '/' );
369 if ( src == NULL )
370 src = end;
371
372 /* detect special sequences */
373 switch ( src - last )
374 {
375 case 1:
376 if ( last [ 1 ] == '/' )
377 {
378 /* "//" -> "/" */
379 last = src;
380 }
381 break;
382
383 case 2:
384 if ( last [ 1 ] == '.' )
385 {
386 /* skip over "./" */
387 last = src;
388 if ( src != end )
389 continue;
390 }
391 break;
392
393 case 3:
394 if ( last [ 1 ] == '.' && last [ 2 ] == '.' )
395 {
396 /* remove previous leaf in path */
397 dst [ 0 ] = 0;
398 dst = strrchr ( path, '/' );
399 if ( dst == NULL || dst < low )
400 return RC ( rcFS, rcDirectory, ctx, rcPath, rcInvalid );
401
402 last = src;
403 if ( src != end )
404 continue;
405 }
406 break;
407 }
408
409 /* if rewriting, copy leaf */
410 assert ( src >= last );
411
412 if ( dst != last )
413 memmove ( dst, last, src - last );
414
415 /* move destination ahead */
416 dst += src - last;
417
418 /* if we're done, go */
419 if ( src == end )
420 break;
421
422 /* find next separator */
423 last = src;
424 }
425
426 /* NUL terminate if modified */
427 if ( dst != end )
428 * dst = 0;
429
430 return 0;
431 }
432
433 /* KSysDirMakePath
434 * creates a full path from partial
435 */
KSysDirMakePath_v1(const KSysDir_v1 * self,enum RCContext ctx,bool canon,char * buffer,size_t path_max,const char * path,va_list args)436 rc_t KSysDirMakePath_v1 ( const KSysDir_v1 * self, enum RCContext ctx, bool canon,
437 char *buffer, size_t path_max, const char *path, va_list args )
438 {
439 int psize;
440 size_t bsize;
441
442 if ( path == NULL )
443 return RC ( rcFS, rcDirectory, ctx, rcPath, rcNull );
444 if ( path [ 0 ] == 0 )
445 return RC ( rcFS, rcDirectory, ctx, rcPath, rcInvalid );
446
447 if ( /*args != NULL &&*/ path [ 0 ] == '%' )
448 {
449 psize = vsnprintf ( buffer, path_max, path, args );
450 if ( psize < 0 || psize >= path_max )
451 return RC ( rcFS, rcDirectory, ctx, rcPath, rcExcessive );
452 if ( buffer [ 0 ] != '/' )
453 {
454 bsize = self -> size;
455 if ( bsize + psize >= path_max )
456 return RC ( rcFS, rcDirectory, ctx, rcPath, rcExcessive );
457 memmove ( buffer + bsize, buffer, psize + 1 );
458 assert ( self -> path [ bsize - 1 ] == '/' );
459 memmove ( buffer, self -> path, bsize );
460 }
461 else if ( ( bsize = self -> root ) != 0 )
462 {
463 if ( bsize + psize >= path_max )
464 return RC ( rcFS, rcDirectory, ctx, rcPath, rcExcessive );
465 memmove ( buffer + bsize, buffer, psize + 1 );
466 assert ( self -> path [ bsize - 1 ] != '/' );
467 memmove ( buffer, self -> path, bsize );
468 }
469 }
470 else
471 {
472 if ( path [ 0 ] != '/' )
473 {
474 assert ( self -> path [ self -> size - 1 ] == '/' );
475 memmove ( buffer, self -> path, bsize = self -> size );
476 }
477 else if ( ( bsize = self -> root ) != 0 )
478 {
479 assert ( self -> path [ bsize - 1 ] != '/' );
480 memmove ( buffer, self -> path, bsize );
481 }
482
483 /* if ( args == NULL )
484 psize = snprintf ( buffer + bsize, path_max - bsize, "%s", path );
485 else*/
486 psize = vsnprintf ( buffer + bsize, path_max - bsize, path, args );
487
488 if ( psize < 0 || bsize + psize >= path_max )
489 return RC ( rcFS, rcDirectory, ctx, rcPath, rcExcessive );
490 }
491
492 /* remove trailing slashes; keep the leading slash */
493 while ( bsize + psize > 1 && buffer [ bsize + psize - 1] == '/' )
494 buffer [ bsize + -- psize ] = 0;
495
496 if ( psize > 0 && ( canon || self -> root != 0 ) )
497 return KSysDirCanonPath_v1 ( self, ctx, buffer, bsize + psize );
498
499 return 0;
500 }
501
502
KSysDirMakePath_v1_noargs(const KSysDir_v1 * self,enum RCContext ctx,bool canon,char * buffer,size_t path_max,const char * path,...)503 static rc_t KSysDirMakePath_v1_noargs(const KSysDir_v1 * self, enum RCContext ctx, bool canon,
504 char *buffer, size_t path_max, const char *path, ...)
505 {
506 va_list vl;
507 va_start( vl, path );
508 rc_t ret = KSysDirMakePath_v1( self, ctx, canon, buffer, path_max, path, vl );
509 va_end(vl);
510 return ret;
511 }
512
513 /* RealPath
514 * returns a real OS path
515 */
KSysDirVRealPath(const KSysDir_v1 * self,char * real,size_t bsize,const char * path,va_list args)516 rc_t KSysDirVRealPath ( const KSysDir_v1 * self,
517 char *real, size_t bsize, const char *path, va_list args )
518 {
519 char full [ PATH_MAX ];
520 rc_t rc = KSysDirMakePath_v1 ( self, rcLoading, false,
521 full, sizeof full, path, args );
522 assert ( bsize >= PATH_MAX );
523 if ( rc == 0 && realpath ( full, real ) == NULL )
524 {
525 switch ( errno )
526 {
527 case EACCES:
528 return RC ( rcFS, rcDylib, rcLoading, rcDirectory, rcUnauthorized );
529 case ENOTDIR:
530 case EINVAL:
531 case ELOOP:
532 return RC ( rcFS, rcDylib, rcLoading, rcPath, rcInvalid );
533 case EIO:
534 return RC ( rcFS, rcDylib, rcLoading, rcTransfer, rcUnknown );
535 case ENAMETOOLONG:
536 return RC ( rcFS, rcDylib, rcLoading, rcPath, rcExcessive );
537 case ENOENT:
538 return RC ( rcFS, rcDylib, rcLoading, rcPath, rcNotFound );
539 default:
540 return RC ( rcFS, rcDylib, rcLoading, rcNoObj, rcUnknown );
541 }
542 }
543
544 return rc;
545 }
546
KSysDirRealPath_v1(const KSysDir_v1 * self,char * real,size_t bsize,const char * path,...)547 rc_t KSysDirRealPath_v1 ( const KSysDir_v1 * self,
548 char *real, size_t bsize, const char *path, ... )
549 {
550 rc_t rc;
551 va_list args;
552
553 va_start ( args, path );
554 rc = KSysDirVRealPath ( self, real, bsize, path, args );
555 va_end ( args );
556
557 return rc;
558 }
559
560 /* KSysDirList
561 * create a directory listing
562 *
563 * "list" [ OUT ] - return parameter for list object
564 *
565 * "path" [ IN, NULL OKAY ] - optional parameter for target
566 * directory. if NULL, interpreted to mean "."
567 */
568 static
KSysDirList_v1(const KSysDir_v1 * self,KNamelist ** listp,bool (* f)(const KDirectory_v1 * dir,const char * name,void * data),void * data,const char * path,va_list args)569 rc_t KSysDirList_v1 ( const KSysDir_v1 * self, KNamelist **listp,
570 bool ( * f ) ( const KDirectory_v1 *dir, const char *name, void *data ), void *data,
571 const char *path, va_list args )
572 {
573 KSysDir_v1 full;
574 rc_t rc = KSysDirMakePath_v1 ( self, rcListing, true,
575 full . path, sizeof full . path, path, args );
576 if ( rc == 0 )
577 {
578 rc = KSysDirInit_v1 ( & full, rcListing, self -> root,
579 NULL, strlen ( full . path ), 0, 0 );
580 if ( rc == 0 )
581 {
582 KSysDirListing *list = malloc ( sizeof * list );
583 if ( list == NULL )
584 rc = RC ( rcFS, rcDirectory, rcListing, rcMemory, rcExhausted );
585 else
586 {
587 rc = KSysDirListingInit ( list,
588 full . path, & full . dad, f, data );
589 if ( rc != 0 )
590 free ( list );
591 else
592 * listp = & list -> dad;
593 }
594 }
595 }
596 return rc;
597 }
598
599
600 /* KSysDirPathType
601 * returns a KPathType
602 *
603 * "path" [ IN ] - NUL terminated string in directory-native character set
604 */
605 static
KSysDirFullPathType_v1(const char * path)606 uint32_t KSysDirFullPathType_v1 ( const char *path )
607 {
608 struct stat st;
609 int type, alias;
610
611 if ( lstat ( path, & st ) != 0 ) switch ( errno )
612 {
613 case ENOENT:
614 return kptNotFound;
615 default:
616 return kptBadPath;
617 }
618
619 alias = 0;
620
621 if ( S_ISLNK ( st . st_mode ) )
622 {
623 alias = kptAlias;
624
625 if ( stat ( path, & st ) != 0 ) switch ( errno )
626 {
627 case ENOENT:
628 return kptNotFound | alias;
629 default:
630 return kptBadPath | alias;
631 }
632 }
633
634 /* not a bad assumption */
635 type = kptFile;
636
637 /* overrides */
638 if ( S_ISDIR ( st . st_mode ) )
639 type = kptDir;
640 else if ( S_ISCHR ( st . st_mode ) )
641 type = kptCharDev;
642 else if ( S_ISBLK ( st . st_mode ) )
643 type = kptBlockDev;
644 else if ( S_ISFIFO ( st . st_mode ) )
645 type = kptFIFO;
646 else if ( S_ISSOCK ( st . st_mode ) )
647 type = kptFIFO;
648
649 /* add in alias bit */
650 return type | alias;
651 }
652
653 static
KSysDirPathType_v1(const KSysDir_v1 * self,const char * path,va_list args)654 uint32_t KSysDirPathType_v1 ( const KSysDir_v1 * self, const char *path, va_list args )
655 {
656 char full [ PATH_MAX ];
657 rc_t rc = KSysDirMakePath_v1 ( self, rcAccessing, false, full, sizeof full, path, args );
658 if ( rc == 0 )
659 return KSysDirFullPathType_v1 ( full );
660 return kptBadPath;
661 }
662
663 /* KSysDirVisit
664 * visit each path under designated directory,
665 * recursively if so indicated
666 *
667 * "recur" [ IN ] - if non-zero, recursively visit sub-directories
668 *
669 * "f" [ IN ] and "data" [ IN, OPAQUE ] - function to execute
670 * on each path. receives a base directory and relative path
671 * for each entry, where each path is also given the leaf name
672 * for convenience. if "f" returns non-zero, the iteration will
673 * terminate and that value will be returned. NB - "dir" will not
674 * be the same as "self".
675 *
676 * "path" [ IN ] - NUL terminated string in directory-native character set
677 */
678 typedef struct KSysDirVisitData KSysDirVisitData;
679 struct KSysDirVisitData
680 {
681 rc_t ( * f ) ( KDirectory_v1*, uint32_t, const char*, void* );
682 void *data;
683 KSysDir_v1 dir;
684 bool recur;
685 };
686
687 static
KSysDirVisitDir(KSysDirVisitData * pb)688 rc_t KSysDirVisitDir ( KSysDirVisitData *pb )
689 {
690 /* get a directory listing */
691 KSysDirEnum listing;
692 rc_t rc = KSysDirEnumInit ( & listing, pb -> dir . path );
693 if ( rc == 0 )
694 {
695 const char *name;
696 uint32_t size = pb -> dir . size;
697
698 /* complete directory path */
699 pb -> dir . path [ size ] = '/';
700 if ( ++ size >= sizeof pb -> dir . path )
701 rc = RC ( rcFS, rcDirectory, rcVisiting, rcPath, rcExcessive );
702 else for ( pb -> dir . size = size, name = KSysDirEnumNext ( & listing );
703 name != NULL; name = KSysDirEnumNext ( & listing ) )
704 {
705 uint32_t type, len = strlen ( name );
706 if ( size + len >= sizeof pb -> dir . path )
707 {
708 rc = RC ( rcFS, rcDirectory, rcVisiting, rcPath, rcExcessive );
709 break;
710 }
711 strcpy ( & pb -> dir . path [ size ], name );
712
713 type = KSysDirFullPathType_v1 ( pb -> dir . path );
714 if ( type == kptBadPath )
715 {
716 rc = RC ( rcFS, rcDirectory, rcVisiting, rcPath, rcInvalid );
717 break;
718 }
719
720 rc = ( * pb -> f ) ( & pb -> dir . dad, type, name, pb -> data );
721 if ( rc != 0 )
722 break;
723
724 if ( pb -> recur && ( type & ( kptAlias - 1 ) ) == kptDir )
725 {
726 pb -> dir . size += len;
727 rc = KSysDirVisitDir ( pb );
728 pb -> dir . size = size;
729 if ( rc != 0 )
730 break;
731 }
732 }
733
734
735 KSysDirEnumWhack ( & listing );
736 }
737 return rc;
738 }
739
740 static
KSysDirVisit_v1(const KSysDir_v1 * self,bool recur,rc_t (* f)(KDirectory_v1 * dir,uint32_t type,const char * name,void * data),void * data,const char * path,va_list args)741 rc_t KSysDirVisit_v1 ( const KSysDir_v1 * self, bool recur,
742 rc_t ( * f ) ( KDirectory_v1 *dir, uint32_t type, const char *name, void *data ), void *data,
743 const char *path, va_list args )
744 {
745 KSysDirVisitData pb;
746 rc_t rc = KSysDirMakePath_v1 ( self, rcVisiting, true,
747 pb . dir . path, sizeof pb . dir . path, path, args );
748 if ( rc == 0 )
749 {
750 uint32_t path_size;
751
752 switch ( KSysDirFullPathType_v1 ( pb . dir . path ) & ( kptAlias - 1 ) )
753 {
754 case kptNotFound:
755 return RC ( rcFS, rcDirectory, rcVisiting, rcPath, rcNotFound );
756 case kptBadPath:
757 return RC ( rcFS, rcDirectory, rcVisiting, rcPath, rcInvalid );
758 case kptDir:
759 break;
760 default:
761 return RC ( rcFS, rcDirectory, rcVisiting, rcPath, rcIncorrect );
762 }
763
764 path_size = strlen ( pb . dir . path );
765 while ( path_size > 1 && path_size > self -> root && pb . dir . path [ path_size - 1 ] == '/' )
766 -- path_size;
767
768 rc = KSysDirInit_v1 ( & pb . dir, rcVisiting, self -> root,
769 NULL, path_size, self -> dad . read_only ? 0 : 1, 0 );
770 if ( rc == 0 )
771 {
772 pb . f = f;
773 pb . data = data;
774 pb . recur = recur;
775 pb . dir . path [ -- pb . dir . size ] = 0;
776 rc = KSysDirVisitDir ( & pb );
777 }
778 }
779 return rc;
780 }
781
782 /* KSysDirRelativePath
783 * makes "path" relative to "root"
784 * both "root" and "path" MUST be absolute
785 * both "root" and "path" MUST be canonical, i.e. have no "./" or "../" sequences
786 */
787 static
KSysDirRelativePath_v1(const KSysDir_v1 * self,enum RCContext ctx,const char * root,char * path,size_t path_max)788 rc_t KSysDirRelativePath_v1 ( const KSysDir_v1 * self, enum RCContext ctx,
789 const char *root, char *path, size_t path_max )
790 {
791 int backup;
792 size_t bsize, psize;
793
794 const char *r = root + self -> root;
795 const char *p = path + self -> root;
796
797 assert ( r != NULL && r [ 0 ] == '/' );
798 assert ( p != NULL && p [ 0 ] == '/' );
799
800 for ( ; * r == * p; ++ r, ++ p )
801 {
802 /* disallow identical paths */
803 if ( * r == 0 )
804 return RC ( rcFS, rcDirectory, ctx, rcPath, rcInvalid );
805 }
806
807 /* paths are identical up to "r","p"
808 if "r" is within a leaf name, then no backup is needed
809 by counting every '/' from "r" to end, obtain backup count */
810 for ( backup = 0; * r != 0; ++ r )
811 {
812 if ( * r == '/' )
813 ++ backup;
814 }
815
816 /* the number of bytes to be inserted */
817 bsize = backup * 3;
818
819 /* align "p" to last directory separator */
820 while ( p [ -1 ] != '/' ) -- p;
821
822 /* the size of the remaining relative path */
823 psize = strlen ( p );
824
825 /* open up space if needed */
826 if ( p - path < bsize )
827 {
828 /* prevent overflow */
829 if ( bsize + psize >= path_max )
830 return RC ( rcFS, rcDirectory, ctx, rcPath, rcExcessive );
831 memmove ( path + bsize, p, psize + 1 /* 1 for '\0'*/ );
832 }
833
834 /* insert backup sequences */
835 for ( bsize = 0; backup > 0; bsize += 3, -- backup )
836 memmove ( & path [ bsize ], "../", 3 );
837
838 /* close gap */
839 if ( p - path > bsize )
840 memmove ( & path [ bsize ], p, strlen ( p ) + 1 );
841
842 return 0;
843 }
844
845 /* KSysDirResolvePath
846 * resolves path to an absolute or directory-relative path
847 *
848 * "absolute" [ IN ] - if non-zero, always give a path starting
849 * with '/'. NB - if the directory is chroot'd, the absolute path
850 * will still be relative to directory root.
851 *
852 * "resolved" [ OUT ] and "rsize" [ IN ] - buffer for
853 * NUL terminated result path in directory-native character set
854 * the resolved path will be directory relative
855 *
856 * "path" [ IN ] - NUL terminated string in directory-native
857 * character set denoting target path. NB - need not exist.
858 */
859 static
KSysDirResolvePath_v1(const KSysDir_v1 * self,bool absolute,char * resolved,size_t rsize,const char * path,va_list args)860 rc_t KSysDirResolvePath_v1 ( const KSysDir_v1 * self, bool absolute,
861 char *resolved, size_t rsize, const char *path, va_list args )
862 {
863 char full [ PATH_MAX ];
864 rc_t rc = KSysDirMakePath_v1 ( self, rcResolving, true, full, sizeof full, path, args );
865 if ( rc == 0 )
866 {
867 uint32_t path_size = strlen ( full );
868
869 /*
870 PLOGMSG(klogDebug, (klogDebug, "KSysDirResolvePath_v1 = '$(res)'", "res=%s", full));
871 */
872 DBGMSG(DBG_KFS, DBG_FLAG(DBG_KFS_DIR),
873 ("KSysDirResolvePath_v1 = '%s'\n", full));
874
875 if ( absolute )
876 {
877 /* test buffer capacity */
878 if ( path_size - self -> root >= rsize )
879 return RC ( rcFS, rcDirectory, rcResolving, rcBuffer, rcInsufficient );
880
881 /* ready to go */
882 strcpy ( resolved, & full [ self -> root ] );
883 /* assert ( resolved [ 0 ] == '/' ); */
884 }
885 else
886 {
887 rc = KSysDirRelativePath_v1 ( self, rcResolving, self -> path, full, sizeof full /*path_size*/ );
888 if ( rc == 0 )
889 {
890 path_size = strlen ( full );
891 if ( path_size >= rsize )
892 return RC ( rcFS, rcDirectory, rcResolving, rcBuffer, rcInsufficient );
893 strcpy ( resolved, full );
894 }
895 }
896 }
897 return rc;
898 }
899
900 /* KSysDirResolveAlias
901 * resolves an alias path to its immediate target
902 * NB - the resolved path may be yet another alias
903 *
904 * "alias" [ IN ] - NUL terminated string in directory-native
905 * character set denoting an object presumed to be an alias.
906 *
907 * "resolved" [ OUT ] and "rsize" [ IN ] - buffer for
908 * NUL terminated result path in directory-native character set
909 */
910 static
KSysDirResolveAlias_v1(const KSysDir_v1 * self,bool absolute,char * resolved,size_t rsize,const char * alias,va_list args)911 rc_t KSysDirResolveAlias_v1 ( const KSysDir_v1 * self, bool absolute,
912 char *resolved, size_t rsize, const char *alias, va_list args )
913 {
914 KSysDir_v1 full;
915 rc_t rc = KSysDirMakePath_v1 ( self, rcResolving, true,
916 full . path, sizeof full . path, alias, args );
917 if ( rc == 0 )
918 {
919 char link [ PATH_MAX ];
920 int len = readlink ( full . path, link, sizeof link );
921 if ( len < 0 ) switch ( errno )
922 {
923 case ENOENT:
924 return RC ( rcFS, rcDirectory, rcResolving, rcPath, rcNotFound );
925 case ENOTDIR:
926 return RC ( rcFS, rcDirectory, rcResolving, rcPath, rcIncorrect );
927 case ENAMETOOLONG:
928 case ELOOP:
929 return RC ( rcFS, rcDirectory, rcResolving, rcPath, rcInvalid );
930 case EACCES:
931 return RC ( rcFS, rcDirectory, rcResolving, rcDirectory, rcUnauthorized );
932 case ENOMEM:
933 return RC ( rcFS, rcDirectory, rcResolving, rcMemory, rcExhausted );
934 case EIO:
935 return RC ( rcFS, rcDirectory, rcResolving, rcTransfer, rcUnknown );
936 default:
937 return RC ( rcFS, rcDirectory, rcResolving, rcNoObj, rcUnknown );
938 }
939
940 if ( ( size_t ) len == sizeof link )
941 return RC ( rcFS, rcDirectory, rcResolving, rcBuffer, rcInsufficient );
942 link [ len ] = 0;
943
944 if ( link [ 0 ] == '/' )
945 {
946 full . size = 1;
947 strcpy ( full . path, link );
948 }
949 else
950 {
951 char *f = strrchr ( full . path, '/' );
952 assert ( f != NULL );
953 full . size = ++f - full . path;
954 if ( full . size + len >= sizeof full . path )
955 return RC ( rcFS, rcDirectory, rcResolving, rcBuffer, rcInsufficient );
956 strcpy ( f, link );
957 }
958
959 full . root = 0;
960
961 /* rc = KSysDirCanonPath ( & full, rcResolving, full . path, len ); */
962 rc = KSysDirCanonPath_v1 ( & full, rcResolving, full . path, full . size + len);
963 if ( rc == 0 )
964 {
965 /* the path in full is an absolute path
966 if outside of chroot, it's a bad link */
967 if ( memcmp ( full . path, self -> path, self -> root + 1 ) != 0 )
968 return RC ( rcFS, rcDirectory, rcResolving, rcLink, rcInvalid );
969
970 /* this is the absolute path length */
971 len = strlen ( & full . path [ self -> root ] );
972
973 /* if not requesting absolute, make self relative */
974 if ( ! absolute )
975 {
976 rc = KSysDirRelativePath_v1 ( self, rcResolving, self -> path, full . path, sizeof full.path/*len*/ );
977 if ( rc != 0 )
978 return rc;
979 len = strlen ( full . path );
980 }
981
982 if ( ( size_t ) len >= rsize )
983 return RC ( rcFS, rcDirectory, rcResolving, rcBuffer, rcInsufficient );
984
985 strcpy ( resolved, & full . path [ self -> root ] );
986 }
987 }
988 return rc;
989 }
990
991
992 /* KSysDirRename
993 * rename an object accessible from directory, replacing
994 * any existing target object of the same type
995 *
996 * "from" [ IN ] - NUL terminated string in directory-native
997 * character set denoting existing object
998 *
999 * "to" [ IN ] - NUL terminated string in directory-native
1000 * character set denoting existing object
1001 */
1002 static rc_t KSysDirVAccess ( const KSysDir_v1 * self, uint32_t *access, const char *path,
1003 va_list args );
KSysDirVAccess_noargs(const KSysDir_v1 * self,uint32_t * access,const char * path,...)1004 static rc_t KSysDirVAccess_noargs ( const KSysDir_v1 * self, uint32_t *access, const char *path, ... )
1005 {
1006 va_list vl;
1007 va_start( vl, path );
1008 rc_t ret = KSysDirVAccess( self, access, path, vl );
1009 va_end(vl);
1010 return ret;
1011 }
1012
1013 static rc_t KSysDirSetAccess_v1 ( KSysDir_v1 * self, bool recur, uint32_t access, uint32_t mask,
1014 const char *path, va_list args );
KSysDirSetAccess_v1_noargs(KSysDir_v1 * self,bool recur,uint32_t access,uint32_t mask,const char * path,...)1015 static rc_t KSysDirSetAccess_v1_noargs ( KSysDir_v1 * self, bool recur, uint32_t access, uint32_t mask,
1016 const char *path, ... )
1017 {
1018 va_list vl;
1019 va_start( vl, path );
1020 rc_t ret = KSysDirSetAccess_v1( self, recur, access, mask, path, vl );
1021 va_end(vl);
1022 return ret;
1023 }
1024
1025 static
KSysDirRename_v1(KSysDir_v1 * self,bool force,const char * from,const char * to)1026 rc_t KSysDirRename_v1 ( KSysDir_v1 * self, bool force, const char *from, const char *to )
1027 {
1028 char ffrom [ PATH_MAX ];
1029 rc_t rc = KSysDirMakePath_v1_noargs ( self, rcRenaming, false, ffrom, sizeof ffrom, from );
1030 if ( rc == 0 )
1031 {
1032 char fto [ PATH_MAX ];
1033 rc = KSysDirMakePath_v1_noargs ( self, rcRenaming, false, fto, sizeof fto, to );
1034 if ( rc == 0 )
1035 {
1036 if ( rename ( ffrom, fto ) != 0 ) switch ( errno )
1037 {
1038 case EISDIR:
1039 case EXDEV:
1040 rc = RC ( rcFS, rcDirectory, rcRenaming, rcPath, rcIncorrect );
1041 break;
1042 case ENOTEMPTY:
1043 case EEXIST:
1044 case EBUSY:
1045 rc = RC ( rcFS, rcDirectory, rcRenaming, rcPath, rcBusy );
1046 break;
1047 case EINVAL:
1048 case ENOTDIR:
1049 case ENAMETOOLONG:
1050 case ELOOP:
1051 rc = RC ( rcFS, rcDirectory, rcRenaming, rcPath, rcInvalid );
1052 break;
1053 case EACCES:
1054 case EPERM:
1055 case EROFS:
1056 rc = RC ( rcFS, rcDirectory, rcRenaming, rcDirectory, rcUnauthorized );
1057 break;
1058 case ENOSPC:
1059 rc= RC ( rcFS, rcDirectory, rcRenaming, rcStorage, rcExhausted );
1060 break;
1061 case ENOMEM:
1062 rc = RC ( rcFS, rcDirectory, rcRenaming, rcMemory, rcExhausted );
1063 break;
1064 case ENOENT:
1065 rc = RC ( rcFS, rcDirectory, rcRenaming, rcPath, rcNotFound );
1066 break;
1067 default:
1068 rc = RC ( rcFS, rcDirectory, rcRenaming, rcNoObj, rcUnknown );
1069 break;
1070 }
1071 }
1072 if (force)
1073 {
1074 if (GetRCState(rc) == rcUnauthorized)
1075 {
1076 uint32_t faccess = 0;
1077 uint32_t taccess = 0;
1078 bool fchanged = false;
1079 bool tchanged = false;
1080
1081 rc = KSysDirVAccess_noargs (self, &taccess, to);
1082 if (rc == 0)
1083 {
1084 rc = KSysDirSetAccess_v1_noargs (self, false, 0222, 0222, to);
1085 tchanged = true;
1086 }
1087 else if(GetRCState(rc) == rcNotFound)
1088 {
1089 rc = 0;
1090 }
1091
1092 if (rc == 0)
1093 {
1094 rc = KSysDirVAccess_noargs (self, &faccess, from);
1095 if (rc == 0)
1096 {
1097 rc = KSysDirSetAccess_v1_noargs (self, false, 0222, 0222, from);
1098 if (rc == 0)
1099 {
1100 fchanged = true;
1101 rc = KSysDirRename_v1 (self, false, from, to);
1102 }
1103 }
1104 if (rc == 0)
1105 {
1106 /* set access on the new name to the access from the old name */
1107 KSysDirSetAccess_v1_noargs (self, false, faccess, 0222, to);
1108 }
1109 else
1110 {
1111 /* since something falied, try to restore changed access bits */
1112 if (fchanged)
1113 KSysDirSetAccess_v1_noargs (self, false, faccess, 0222, from);
1114 if (tchanged)
1115 KSysDirSetAccess_v1_noargs (self, false, taccess, 0222, to);
1116 }
1117
1118 }
1119
1120 }
1121 }
1122 }
1123 return rc;
1124 }
1125
1126
1127 /* KSysDirClearDir
1128 * remove all directory contents
1129 *
1130 * "path" [ IN ] - NUL terminated string in directory-native
1131 * character set denoting target directory
1132 *
1133 * "force" [ IN ] - if non-zero and directory entry is a
1134 * sub-directory, remove recursively
1135 */
1136 static
1137 rc_t KSysDirRemoveEntry_v1 ( char *path, size_t path_max, bool force );
1138
1139 static
KSysDirEmptyDir_v1(char * path,size_t path_max,bool force)1140 rc_t KSysDirEmptyDir_v1 ( char *path, size_t path_max, bool force )
1141 {
1142 KSysDirEnum list;
1143 rc_t rc = KSysDirEnumInit ( & list, path );
1144 if ( rc != 0 )
1145 rc = ResetRCContext ( rc, rcFS, rcDirectory, rcClearing );
1146 else
1147 {
1148 size_t path_size = strlen ( path );
1149 path [ path_size ] = '/';
1150 if ( ++ path_size == path_max )
1151 rc = RC ( rcFS, rcDirectory, rcClearing, rcPath, rcExcessive );
1152 else
1153 {
1154 const char *leaf;
1155 while ( ( leaf = KSysDirEnumNext ( & list ) ) != NULL )
1156 {
1157 size_t leaf_size = strlen ( leaf );
1158 if ( path_size + leaf_size >= path_max )
1159 {
1160 rc = RC ( rcFS, rcDirectory, rcClearing, rcPath, rcExcessive );
1161 break;
1162 }
1163
1164 strcpy ( & path [ path_size ], leaf );
1165 rc = KSysDirRemoveEntry_v1 ( path, path_max, force );
1166 if ( rc != 0 )
1167 {
1168 rc = ResetRCContext ( rc, rcFS, rcDirectory, rcClearing );
1169 break;
1170 }
1171 }
1172
1173 path [ path_size - 1 ] = 0;
1174 }
1175
1176 KSysDirEnumWhack ( & list );
1177 }
1178 return rc;
1179 }
1180
1181 static
KSysDirClearDir_v1(KSysDir_v1 * self,bool force,const char * path,va_list args)1182 rc_t KSysDirClearDir_v1 ( KSysDir_v1 * self, bool force, const char *path, va_list args )
1183 {
1184 char full [ PATH_MAX ];
1185 rc_t rc = KSysDirMakePath_v1 ( self, rcClearing, false, full, sizeof full, path, args );
1186 if ( rc == 0 )
1187 rc = KSysDirEmptyDir_v1 ( full, sizeof full, force );
1188 return rc;
1189 }
1190
1191 /* KSysDirRemove
1192 * remove an accessible object from its directory
1193 *
1194 * "path" [ IN ] - NUL terminated string in directory-native
1195 * character set denoting target object
1196 *
1197 * "force" [ IN ] - if non-zero and target is a directory,
1198 * remove recursively
1199 */
1200 static
KSysDirRemoveEntry_v1(char * path,size_t path_max,bool force)1201 rc_t KSysDirRemoveEntry_v1 ( char *path, size_t path_max, bool force )
1202 {
1203 if ( unlink ( path ) != 0 )
1204 {
1205 switch ( errno )
1206 {
1207 case ENOENT:
1208 return 0;
1209 case EPERM:
1210 case EISDIR:
1211 break;
1212 case EACCES:
1213 case EROFS:
1214 return RC ( rcFS, rcDirectory, rcRemoving, rcDirectory, rcUnauthorized );
1215 case EBUSY:
1216 return RC ( rcFS, rcDirectory, rcRemoving, rcPath, rcBusy );
1217 case ENAMETOOLONG:
1218 case ENOTDIR:
1219 case ELOOP:
1220 return RC ( rcFS, rcDirectory, rcRemoving, rcPath, rcInvalid );
1221 case ENOMEM:
1222 return RC ( rcFS, rcDirectory, rcRemoving, rcMemory, rcExhausted );
1223 case EIO:
1224 return RC ( rcFS, rcDirectory, rcRemoving, rcTransfer, rcUnknown );
1225 default:
1226 return RC ( rcFS, rcDirectory, rcRemoving, rcNoObj, rcUnknown );
1227 }
1228
1229 while ( rmdir ( path ) != 0 ) switch ( errno )
1230 {
1231 case EEXIST:
1232 case ENOTEMPTY:
1233 if ( force )
1234 {
1235 rc_t rc = KSysDirEmptyDir_v1 ( path, path_max, force );
1236 if ( rc != 0 )
1237 return rc;
1238 force = false;
1239 break;
1240 }
1241 case EBUSY:
1242 return RC ( rcFS, rcDirectory, rcRemoving, rcPath, rcBusy );
1243 case EPERM:
1244 case EACCES:
1245 case EROFS:
1246 return RC ( rcFS, rcDirectory, rcRemoving, rcDirectory, rcUnauthorized );
1247 case ENOMEM:
1248 return RC ( rcFS, rcDirectory, rcRemoving, rcMemory, rcExhausted );
1249 default:
1250 return RC ( rcFS, rcDirectory, rcRemoving, rcNoObj, rcUnknown );
1251 }
1252 }
1253
1254 return 0;
1255 }
1256
1257 static
KSysDirRemove_v1(KSysDir_v1 * self,bool force,const char * path,va_list args)1258 rc_t KSysDirRemove_v1 ( KSysDir_v1 * self, bool force, const char *path, va_list args )
1259 {
1260 char full [ PATH_MAX ];
1261 rc_t rc = KSysDirMakePath_v1 ( self, rcRemoving, false, full, sizeof full, path, args );
1262 if ( rc == 0 )
1263 rc = KSysDirRemoveEntry_v1 ( full, sizeof full, force );
1264 return rc;
1265 }
1266
1267 /* KSysDirAccess
1268 * get access to object
1269 *
1270 * "access" [ OUT ] - return parameter for Unix access mode
1271 *
1272 * "path" [ IN ] - NUL terminated string in directory-native
1273 * character set denoting target object
1274 */
1275 static
KSysDirVAccess(const KSysDir_v1 * self,uint32_t * access,const char * path,va_list args)1276 rc_t KSysDirVAccess ( const KSysDir_v1 * self,
1277 uint32_t *access, const char *path, va_list args )
1278 {
1279 char full [ PATH_MAX ];
1280 rc_t rc = KSysDirMakePath_v1 ( self, rcAccessing, false, full, sizeof full, path, args );
1281 if ( rc == 0 )
1282 {
1283 struct stat st;
1284 if ( lstat ( full, & st ) != 0 ) switch ( errno )
1285 {
1286 case ENOENT:
1287 return RC ( rcFS, rcDirectory, rcAccessing, rcPath, rcNotFound );
1288 case ENOTDIR:
1289 case ELOOP:
1290 case ENAMETOOLONG:
1291 return RC ( rcFS, rcDirectory, rcAccessing, rcPath, rcInvalid );
1292 case EACCES:
1293 return RC ( rcFS, rcDirectory, rcAccessing, rcDirectory, rcUnauthorized );
1294 case ENOMEM:
1295 return RC ( rcFS, rcDirectory, rcAccessing, rcMemory, rcExhausted );
1296 default:
1297 return RC ( rcFS, rcDirectory, rcAccessing, rcNoObj, rcUnknown );
1298 }
1299
1300 * access = st . st_mode & 07777;
1301 }
1302 return rc;
1303 }
1304
1305 /* KSysDirSetAccess
1306 * set access to object a la Unix "chmod"
1307 *
1308 * "path" [ IN ] - NUL terminated string in directory-native
1309 * character set denoting target object
1310 *
1311 * "access" [ IN ] and "mask" [ IN ] - definition of change
1312 * where "access" contains new bit values and "mask defines
1313 * which bits should be changed.
1314 *
1315 * "recur" [ IN ] - if non zero and "path" is a directory,
1316 * apply changes recursively.
1317 */
1318 static
1319 rc_t KSysDirChangeAccess_v1 ( char *path, size_t path_max,
1320 uint32_t access, uint32_t mask, bool recur );
1321
1322 static
KSysDirChangeDirAccess_v1(char * path,size_t path_max,uint32_t access,uint32_t mask)1323 rc_t KSysDirChangeDirAccess_v1 ( char *path, size_t path_max,
1324 uint32_t access, uint32_t mask )
1325 {
1326 KSysDirEnum list;
1327 rc_t rc = KSysDirEnumInit ( & list, path );
1328 if ( rc == 0 )
1329 {
1330 bool eperm = false;
1331 size_t path_size = strlen ( path );
1332 path [ path_size ] = '/';
1333 if ( ++ path_size == path_max )
1334 rc = RC ( rcFS, rcDirectory, rcUpdating, rcBuffer, rcInsufficient );
1335 else
1336 {
1337 const char *leaf;
1338 while ( ( leaf = KSysDirEnumNext ( & list ) ) != NULL )
1339 {
1340 size_t leaf_size = strlen ( leaf );
1341 if ( path_size + leaf_size >= path_max )
1342 {
1343 rc = RC ( rcFS, rcDirectory, rcUpdating, rcBuffer, rcInsufficient );
1344 break;
1345 }
1346
1347 strcpy ( & path [ path_size ], leaf );
1348 rc = KSysDirChangeAccess_v1 ( path, path_max, access, mask, 1 );
1349 if ( rc != 0 )
1350 {
1351 if ( GetRCState ( rc ) != rcUnauthorized )
1352 break;
1353 eperm = true;
1354 rc = 0;
1355 }
1356 }
1357
1358 path [ path_size - 1 ] = 0;
1359 }
1360
1361 KSysDirEnumWhack ( & list );
1362
1363 if ( rc == 0 && eperm )
1364 rc = RC ( rcFS, rcDirectory, rcUpdating, rcDirectory, rcUnauthorized );
1365 }
1366 return rc;
1367 }
1368
1369 static
KSysDirChangeEntryAccess_v1(char * path,size_t path_max,uint32_t access,uint32_t mask,uint32_t st_mode)1370 rc_t KSysDirChangeEntryAccess_v1 ( char *path, size_t path_max,
1371 uint32_t access, uint32_t mask, uint32_t st_mode )
1372 {
1373 /* keep old bits */
1374 access &= mask;
1375 access |= st_mode & ~ mask;
1376
1377 if ( chmod ( path, access & 07777 ) != 0 ) switch ( errno )
1378 {
1379 case EPERM:
1380 case EACCES:
1381 case EROFS:
1382 return RC ( rcFS, rcDirectory, rcUpdating, rcDirectory, rcUnauthorized );
1383 case ENOTDIR:
1384 case ELOOP:
1385 return RC ( rcFS, rcDirectory, rcUpdating, rcPath, rcInvalid );
1386 case ENAMETOOLONG:
1387 return RC ( rcFS, rcDirectory, rcUpdating, rcPath, rcExcessive );
1388 case ENOENT:
1389 return RC ( rcFS, rcDirectory, rcUpdating, rcPath, rcNotFound );
1390 case ENOMEM:
1391 return RC ( rcFS, rcDirectory, rcUpdating, rcMemory, rcExhausted );
1392 default:
1393 return RC ( rcFS, rcDirectory, rcUpdating, rcNoObj, rcUnknown );
1394 }
1395
1396 return 0;
1397 }
1398
1399 static
KSysDirChangeAccess_v1(char * path,size_t path_max,uint32_t access,uint32_t mask,bool recur)1400 rc_t KSysDirChangeAccess_v1 ( char *path, size_t path_max,
1401 uint32_t access, uint32_t mask, bool recur )
1402 {
1403 struct stat st;
1404 if ( lstat ( path, & st ) != 0 ) switch ( errno )
1405 {
1406 case ENOENT:
1407 return RC ( rcFS, rcDirectory, rcUpdating, rcPath, rcNotFound );
1408 case ENOTDIR:
1409 case ELOOP:
1410 return RC ( rcFS, rcDirectory, rcUpdating, rcPath, rcInvalid );
1411 case ENAMETOOLONG:
1412 return RC ( rcFS, rcDirectory, rcUpdating, rcPath, rcExcessive );
1413 case EACCES:
1414 return RC ( rcFS, rcDirectory, rcUpdating, rcDirectory, rcUnauthorized );
1415 case ENOMEM:
1416 return RC ( rcFS, rcDirectory, rcUpdating, rcMemory, rcExhausted );
1417 default:
1418 return RC ( rcFS, rcDirectory, rcUpdating, rcNoObj, rcUnknown );
1419 }
1420
1421 if ( recur && S_ISDIR ( st . st_mode ) )
1422 {
1423 rc_t rc;
1424 uint32_t enable = access & mask;
1425 if ( enable != 0 )
1426 {
1427 rc = KSysDirChangeEntryAccess_v1 ( path, path_max,
1428 access, enable, st . st_mode );
1429 if ( rc != 0 )
1430 return rc;
1431 }
1432
1433 rc = KSysDirChangeDirAccess_v1 ( path, path_max, access, mask );
1434 if ( rc == 0 )
1435 {
1436 uint32_t disable = ~ access & mask;
1437 if ( disable != 0 )
1438 {
1439 rc = KSysDirChangeEntryAccess_v1 ( path, path_max,
1440 access, disable, st . st_mode | enable );
1441 }
1442 }
1443 return rc;
1444 }
1445
1446 return KSysDirChangeEntryAccess_v1 ( path, path_max,
1447 access, mask, st . st_mode );
1448 }
1449
1450 static
KSysDirSetAccess_v1(KSysDir_v1 * self,bool recur,uint32_t access,uint32_t mask,const char * path,va_list args)1451 rc_t KSysDirSetAccess_v1 ( KSysDir_v1 * self, bool recur,
1452 uint32_t access, uint32_t mask, const char *path, va_list args )
1453 {
1454 char full [ PATH_MAX ];
1455 rc_t rc = KSysDirMakePath_v1 ( self, rcUpdating, false, full, sizeof full, path, args );
1456 if ( rc == 0 )
1457 {
1458 if ( mask == 0 )
1459 mask = 07777;
1460
1461 rc = KSysDirChangeAccess_v1 ( full, sizeof full,
1462 access, mask & 07777, recur );
1463 }
1464 return rc;
1465 }
1466
1467 /* KSysDirDate
1468 * get access to object
1469 *
1470 * "date" [ OUT ] - return parameter for Unix access mode
1471 *
1472 * "path" [ IN ] - NUL terminated string in directory-native
1473 * character set denoting target object
1474 */
1475 static
KSysDirVDate(const KSysDir_v1 * self,KTime_t * date,const char * path,va_list args)1476 rc_t KSysDirVDate ( const KSysDir_v1 * self,
1477 KTime_t * date, const char *path, va_list args )
1478 {
1479 char full [ PATH_MAX ];
1480 rc_t rc = KSysDirMakePath_v1 ( self, rcAccessing, false, full, sizeof full, path, args );
1481 if ( rc == 0 )
1482 {
1483 struct stat st;
1484 if ( lstat ( full, & st ) != 0 ) switch ( errno )
1485 {
1486 case ENOENT:
1487 return RC ( rcFS, rcDirectory, rcAccessing, rcPath, rcNotFound );
1488 case ENOTDIR:
1489 case ELOOP:
1490 case ENAMETOOLONG:
1491 return RC ( rcFS, rcDirectory, rcAccessing, rcPath, rcInvalid );
1492 case EACCES:
1493 return RC ( rcFS, rcDirectory, rcAccessing, rcDirectory, rcUnauthorized );
1494 case ENOMEM:
1495 return RC ( rcFS, rcDirectory, rcAccessing, rcMemory, rcExhausted );
1496 default:
1497 return RC ( rcFS, rcDirectory, rcAccessing, rcNoObj, rcUnknown );
1498 }
1499
1500 * date = ( KTime_t ) st . st_mtime;
1501 }
1502 return rc;
1503 }
1504
1505 /* KSysDirSetDate
1506 * set date to object a la Unix "touch"
1507 *
1508 * "path" [ IN ] - NUL terminated string in directory-native
1509 * character set denoting target object
1510 *
1511 * "date" [ IN ] - new mtime
1512 *
1513 * "recur" [ IN ] - if non zero and "path" is a directory,
1514 * apply changes recursively.
1515 */
1516 static
1517 rc_t KSysDirChangeDate_v1 ( char *path, size_t path_max,
1518 KTime_t date, bool recur );
1519
1520 static
KSysDirChangeDirDate_v1(char * path,size_t path_max,KTime_t date)1521 rc_t KSysDirChangeDirDate_v1 ( char *path, size_t path_max,
1522 KTime_t date )
1523 {
1524 KSysDirEnum list;
1525 rc_t rc = KSysDirEnumInit ( & list, path );
1526 if ( rc == 0 )
1527 {
1528 bool eperm = false;
1529 size_t path_size = strlen ( path );
1530 path [ path_size ] = '/';
1531 if ( ++ path_size == path_max )
1532 rc = RC ( rcFS, rcDirectory, rcUpdating, rcBuffer, rcInsufficient );
1533 else
1534 {
1535 const char *leaf;
1536 while ( ( leaf = KSysDirEnumNext ( & list ) ) != NULL )
1537 {
1538 size_t leaf_size = strlen ( leaf );
1539 if ( path_size + leaf_size >= path_max )
1540 {
1541 rc = RC ( rcFS, rcDirectory, rcUpdating, rcBuffer, rcInsufficient );
1542 break;
1543 }
1544
1545 strcpy ( & path [ path_size ], leaf );
1546 rc = KSysDirChangeDate_v1 ( path, path_max, date, 1 );
1547 if ( rc != 0 )
1548 {
1549 if ( GetRCState ( rc ) != rcUnauthorized )
1550 break;
1551 eperm = true;
1552 rc = 0;
1553 }
1554 }
1555
1556 path [ path_size - 1 ] = 0;
1557 }
1558
1559 KSysDirEnumWhack ( & list );
1560
1561 if ( rc == 0 && eperm )
1562 rc = RC ( rcFS, rcDirectory, rcUpdating, rcDirectory, rcUnauthorized );
1563 }
1564 return rc;
1565 }
1566
1567 static
KSysDirChangeEntryDate_v1(char * path,size_t path_max,struct utimbuf * tb)1568 rc_t KSysDirChangeEntryDate_v1 ( char *path, size_t path_max,
1569 struct utimbuf * tb)
1570 {
1571 if ( utime ( path, tb ) != 0 ) switch ( errno )
1572 {
1573 case EPERM:
1574 case EACCES:
1575 case EROFS:
1576 return RC ( rcFS, rcDirectory, rcUpdating, rcDirectory, rcUnauthorized );
1577 case ENOTDIR:
1578 case ELOOP:
1579 return RC ( rcFS, rcDirectory, rcUpdating, rcPath, rcInvalid );
1580 case ENAMETOOLONG:
1581 return RC ( rcFS, rcDirectory, rcUpdating, rcPath, rcExcessive );
1582 case ENOENT:
1583 return RC ( rcFS, rcDirectory, rcUpdating, rcPath, rcNotFound );
1584 case ENOMEM:
1585 return RC ( rcFS, rcDirectory, rcUpdating, rcMemory, rcExhausted );
1586 default:
1587 return RC ( rcFS, rcDirectory, rcUpdating, rcNoObj, rcUnknown );
1588 }
1589
1590 return 0;
1591 }
1592
1593 static
KSysDirChangeDate_v1(char * path,size_t path_max,KTime_t date,bool recur)1594 rc_t KSysDirChangeDate_v1 ( char *path, size_t path_max,
1595 KTime_t date, bool recur )
1596 {
1597 struct stat st;
1598 struct utimbuf u;
1599
1600 if ( lstat ( path, & st ) != 0 ) switch ( errno )
1601 {
1602 case ENOENT:
1603 return RC ( rcFS, rcDirectory, rcUpdating, rcPath, rcNotFound );
1604 case ENOTDIR:
1605 case ELOOP:
1606 return RC ( rcFS, rcDirectory, rcUpdating, rcPath, rcInvalid );
1607 case ENAMETOOLONG:
1608 return RC ( rcFS, rcDirectory, rcUpdating, rcPath, rcExcessive );
1609 case EACCES:
1610 return RC ( rcFS, rcDirectory, rcUpdating, rcDirectory, rcUnauthorized );
1611 case ENOMEM:
1612 return RC ( rcFS, rcDirectory, rcUpdating, rcMemory, rcExhausted );
1613 default:
1614 return RC ( rcFS, rcDirectory, rcUpdating, rcNoObj, rcUnknown );
1615 }
1616
1617 u . actime = u . modtime = date;
1618
1619 if ( recur && S_ISDIR ( st . st_mode ) )
1620 {
1621 rc_t rc = KSysDirChangeEntryDate_v1 ( path, path_max, & u );
1622 if ( rc != 0 )
1623 return rc;
1624
1625 rc = KSysDirChangeDirDate_v1 ( path, path_max, date );
1626 if ( rc == 0 )
1627 rc = KSysDirChangeEntryDate_v1 ( path, path_max, & u );
1628
1629 return rc;
1630 }
1631
1632 return KSysDirChangeEntryDate_v1 ( path, path_max, & u );
1633 }
1634
1635 static
KSysDirVSetDate(KSysDir_v1 * self,bool recur,KTime_t date,const char * path,va_list args)1636 rc_t KSysDirVSetDate ( KSysDir_v1 *self, bool recur,
1637 KTime_t date, const char *path, va_list args )
1638 {
1639 char full [ PATH_MAX ];
1640 rc_t rc = KSysDirMakePath_v1 ( self, rcUpdating, false, full, sizeof full, path, args );
1641 if ( rc == 0 )
1642 rc = KSysDirChangeDate_v1 ( full, sizeof full, date, recur );
1643
1644 return rc;
1645 }
1646
1647 static
KSysDirGetSysdir_v1(const KSysDir_v1 * cself)1648 KSysDir_v1 *KSysDirGetSysdir_v1 ( const KSysDir_v1 *cself )
1649 {
1650 return ( KSysDir_v1 * ) cself;
1651 }
1652
1653 /* KSysDirCreateParents
1654 * creates missing parent directories
1655 */
1656 static
make_dir_v1(const char * path,uint32_t access)1657 rc_t make_dir_v1 ( const char *path, uint32_t access )
1658 {
1659 if ( mkdir ( path, ( int ) access ) != 0 ) switch ( errno )
1660 {
1661 case ENOENT:
1662 return RC ( rcFS, rcDirectory, rcCreating, rcPath, rcNotFound );
1663 case EEXIST:
1664 return RC ( rcFS, rcDirectory, rcCreating, rcPath, rcExists );
1665 case EPERM:
1666 case EACCES:
1667 case EROFS:
1668 return RC ( rcFS, rcDirectory, rcCreating, rcDirectory, rcUnauthorized );
1669 case ENOTDIR:
1670 case ELOOP:
1671 return RC ( rcFS, rcDirectory, rcCreating, rcPath, rcInvalid );
1672 case ENOMEM:
1673 return RC ( rcFS, rcDirectory, rcCreating, rcMemory, rcExhausted );
1674 case ENOSPC:
1675 return RC ( rcFS, rcDirectory, rcCreating, rcStorage, rcExhausted );
1676 default:
1677 return RC ( rcFS, rcDirectory, rcCreating, rcNoObj, rcUnknown );
1678 }
1679 return 0;
1680 }
1681
1682 static
KSysDirCreateParents_v1(const KSysDir_v1 * self,char * path,uint32_t access,bool strip)1683 rc_t KSysDirCreateParents_v1 ( const KSysDir_v1 * self,
1684 char *path, uint32_t access, bool strip )
1685 {
1686 rc_t rc;
1687 char *p, *par = path + self -> root + 1;
1688 size_t size = strlen ( par );
1689
1690 if ( ! strip )
1691 p = par + size;
1692 else
1693 {
1694 p = strrchr ( par, '/' );
1695 if ( p == NULL )
1696 return 0;
1697 size = p - par;
1698 }
1699
1700 while ( 1 )
1701 {
1702 /* crop string */
1703 p [ 0 ] = 0;
1704
1705 /* try to create directory */
1706 rc = make_dir_v1 ( path, access );
1707 if ( GetRCState ( rc ) != rcNotFound )
1708 break;
1709
1710 /* back up some more */
1711 p = strrchr ( par, '/' );
1712 if ( p == NULL )
1713 {
1714 p = par + strlen ( par );
1715 break;
1716 }
1717 }
1718
1719 par += size;
1720 assert ( p != NULL );
1721
1722 /* create directories from here */
1723 if ( rc == 0 ) while ( p < par )
1724 {
1725 p [ 0 ] = '/';
1726 rc = make_dir_v1 ( path, access );
1727 if ( rc != 0 || ++ p >= par )
1728 break;
1729 p += strlen ( p );
1730 }
1731
1732 /* fix up remaining path */
1733 while ( p < par )
1734 {
1735 p [ 0 ] = '/';
1736 if ( ++ p >= par )
1737 break;
1738 p += strlen ( p );
1739 }
1740
1741 /* repair stripped path */
1742 if ( strip )
1743 par [ 0 ] = '/';
1744
1745 return rc;
1746 }
1747
1748 /* CreateAlias
1749 * creates a path alias according to create mode
1750 * such that "alias" => "targ"
1751 *
1752 * "access" [ IN ] - standard Unix directory access mode
1753 * used when "mode" has kcmParents set and alias path does
1754 * not exist.
1755 *
1756 * "mode" [ IN ] - a creation mode ( see explanation above ).
1757 *
1758 * "targ" [ IN ] - NUL terminated string in directory-native
1759 * character set denoting target object, i.e. the object which
1760 * is designated by symlink "alias".
1761 *
1762 * "alias" [ IN ] - NUL terminated string in directory-native
1763 * character set denoting target alias, i.e. the symlink that
1764 * designates a target "targ".
1765 */
1766 static
KSysDirCreateAlias_v1(KSysDir_v1 * self,uint32_t access,KCreateMode mode,const char * targ,const char * alias)1767 rc_t KSysDirCreateAlias_v1 ( KSysDir_v1 * self,
1768 uint32_t access, KCreateMode mode,
1769 const char *targ, const char *alias )
1770 {
1771 /* create full path to symlink */
1772 char falias [ PATH_MAX ];
1773 rc_t rc = KSysDirMakePath_v1_noargs ( self, rcCreating, true, falias, sizeof falias, alias );
1774 if ( rc == 0 )
1775 {
1776 /* the full path to target RELATIVE TO self */
1777 char ftarg [ PATH_MAX ];
1778 rc = KSysDirMakePath_v1_noargs ( self, rcCreating, true, ftarg, sizeof ftarg, targ );
1779 if ( rc == 0 )
1780 {
1781 /* if "targ" is relative or "self" is chroot'd,
1782 "ftarg" must be made relative */
1783 if ( targ [ 0 ] != '/' || self -> root != 0 )
1784 {
1785 /* take path to alias as root.
1786 generate a path RELATIVE TO alias */
1787 rc = KSysDirRelativePath_v1 ( self, rcCreating, falias,
1788 ftarg, sizeof ftarg /*strlen ( ftarg )*/ );
1789 if ( rc != 0 )
1790 return rc;
1791 }
1792
1793 if ( symlink ( ftarg, falias ) == 0 )
1794 return 0;
1795
1796 switch ( errno )
1797 {
1798 case EEXIST:
1799 /* alias already exists. unless mode is
1800 create-only, force creation by removing old */
1801 if ( ( mode & kcmValueMask ) != kcmCreate )
1802 {
1803 /* refuse to drop if not an alias */
1804 if ( ( KSysDirFullPathType_v1 ( falias ) & kptAlias ) == 0 )
1805 return RC ( rcFS, rcDirectory, rcCreating, rcPath, rcIncorrect );
1806
1807 /* drop existing alias */
1808 rc = KSysDirRemoveEntry_v1 ( falias, sizeof falias, false );
1809 if ( rc == 0 )
1810 break;
1811 }
1812 return RC ( rcFS, rcDirectory, rcCreating, rcPath, rcExists );
1813
1814 case ENOENT:
1815 /* a part of the alias path doesn't exist */
1816 if ( ( mode & kcmParents ) != 0 )
1817 {
1818 KSysDirCreateParents_v1 ( self, falias, access, true );
1819 break;
1820 }
1821 return RC ( rcFS, rcDirectory, rcCreating, rcPath, rcNotFound );
1822
1823 case EPERM:
1824 case EACCES:
1825 case EROFS:
1826 return RC ( rcFS, rcDirectory, rcCreating, rcDirectory, rcUnauthorized );
1827 case ENAMETOOLONG:
1828 return RC ( rcFS, rcDirectory, rcCreating, rcPath, rcExcessive );
1829 case ENOTDIR:
1830 case ELOOP:
1831 return RC ( rcFS, rcDirectory, rcCreating, rcPath, rcInvalid );
1832 case ENOMEM:
1833 return RC ( rcFS, rcDirectory, rcCreating, rcMemory, rcExhausted );
1834 case ENOSPC:
1835 return RC ( rcFS, rcDirectory, rcCreating, rcStorage, rcExhausted );
1836 case EIO:
1837 return RC ( rcFS, rcDirectory, rcCreating, rcTransfer, rcUnknown );
1838 default:
1839 return RC ( rcFS, rcDirectory, rcCreating, rcNoObj, rcUnknown );
1840 }
1841
1842 /* try again either with existing guy removed
1843 or missing directories created */
1844 if ( symlink ( ftarg, falias ) != 0 ) switch ( errno )
1845 {
1846 case EEXIST:
1847 return RC ( rcFS, rcDirectory, rcCreating, rcPath, rcExists );
1848 case ENOENT:
1849 return RC ( rcFS, rcDirectory, rcCreating, rcPath, rcNotFound );
1850 default:
1851 return RC ( rcFS, rcDirectory, rcCreating, rcNoObj, rcUnknown );
1852 }
1853
1854 assert ( rc == 0 );
1855 }
1856 }
1857 return rc;
1858 }
1859
1860
1861 /* CreateLink ( v1.5 )
1862 * creates a new link (also known as a hard link).
1863 *
1864 * "access" [ IN ] - standard Unix directory access mode
1865 * used when "mode" has kcmParents set and alias path does
1866 * not exist.
1867 *
1868 * "mode" [ IN ] - a creation mode ( see explanation above ).
1869 *
1870 * "oldpath" [ IN ] - NUL terminated string in directory-native
1871 * character set denoting existing object. THE PATH IS GIVEN RELATIVE
1872 * TO DIRECTORY ( "self" ), NOT LINK ( "newpath" )!
1873 *
1874 * "newpath" [ IN ] - NUL terminated string in directory-native
1875 * character set denoting a new link.
1876 */
1877 static
KSysDirCreateLink_v1(KSysDir_v1 * self,uint32_t access,KCreateMode mode,const char * oldpath,const char * newpath)1878 rc_t KSysDirCreateLink_v1 ( KSysDir_v1 * self,
1879 uint32_t access, KCreateMode mode,
1880 const char *oldpath, const char *newpath )
1881 {
1882 /* create full path to link */
1883 char flink [ PATH_MAX ] = "";
1884 rc_t rc = KSysDirMakePath_v1_noargs ( self, rcCreating, true,
1885 flink, sizeof flink, newpath );
1886 if ( rc == 0 )
1887 {
1888 /* the full path to oldpath RELATIVE TO self */
1889 char fold [ PATH_MAX ] = "";
1890 rc = KSysDirMakePath_v1_noargs ( self, rcCreating, true,
1891 fold, sizeof fold, oldpath );
1892 if ( rc == 0 )
1893 {
1894 /* if "self" is chroot'd, "fold" must be made relative */
1895 if ( self -> root != 0 )
1896 {
1897 /* take path to newpath as root.
1898 generate a path RELATIVE TO newpath */
1899 rc = KSysDirRelativePath_v1 ( self, rcCreating, flink,
1900 fold, sizeof fold /*strlen ( fold )*/ );
1901 if ( rc != 0 )
1902 return rc;
1903 }
1904
1905 if ( link ( fold, flink ) == 0 )
1906 return 0;
1907
1908 switch ( errno )
1909 {
1910 case EMLINK:
1911 /* The number of links to the file named by oldpath
1912 would exceed {LINK_MAX} */
1913 return RC ( rcFS, rcDirectory, rcCreating,
1914 rcFile, rcExcessive );
1915
1916 case EXDEV:
1917 /* The link named by newpath and the file named by oldpath are
1918 on different file systems and the implementation does not
1919 support links between file systems. */
1920 return RC ( rcFS, rcDirectory, rcCreating,
1921 rcPath, rcIncorrect );
1922
1923 case EEXIST:
1924 /* newpath already exists */
1925 return RC ( rcFS, rcDirectory, rcCreating, rcPath, rcExists );
1926
1927 case ENOENT:
1928 /* a part of the newpath path doesn't exist */
1929 if ( ( mode & kcmParents ) != 0 )
1930 {
1931 KSysDirCreateParents_v1 ( self, flink, access, true );
1932 break;
1933 }
1934 return RC ( rcFS, rcDirectory, rcCreating, rcPath, rcNotFound );
1935
1936 case EPERM:
1937 case EACCES:
1938 case EROFS:
1939 return RC ( rcFS, rcDirectory, rcCreating,
1940 rcDirectory, rcUnauthorized );
1941 case ENAMETOOLONG:
1942 return RC ( rcFS, rcDirectory, rcCreating,
1943 rcPath, rcExcessive );
1944 case ENOTDIR:
1945 case ELOOP:
1946 return RC ( rcFS, rcDirectory, rcCreating, rcPath, rcInvalid );
1947 case ENOSPC:
1948 return RC ( rcFS, rcDirectory, rcCreating,
1949 rcStorage, rcExhausted );
1950 default:
1951 return RC ( rcFS, rcDirectory, rcCreating, rcNoObj, rcUnknown );
1952 }
1953
1954 /* try with missing directories created */
1955 if ( link ( fold, flink ) != 0 ) switch ( errno )
1956 {
1957 case ENOENT:
1958 return RC ( rcFS, rcDirectory, rcCreating, rcPath, rcNotFound );
1959 default:
1960 return RC ( rcFS, rcDirectory, rcCreating, rcNoObj, rcUnknown );
1961 }
1962
1963 assert ( rc == 0 );
1964 }
1965 }
1966 return rc;
1967 }
1968
1969
1970 /* KSysDirOpenFileRead
1971 * opens an existing file with read-only access
1972 *
1973 * "f" [ OUT ] - return parameter for newly opened file
1974 *
1975 * "path" [ IN ] - NUL terminated string in directory-native
1976 * character set denoting target file
1977 */
1978 static
KSysDirOpenFileRead_v1(const KSysDir_v1 * self,const KFile_v1 ** f,const char * path,va_list args)1979 rc_t KSysDirOpenFileRead_v1 ( const KSysDir_v1 * self,
1980 const KFile_v1 **f, const char *path, va_list args )
1981 {
1982 char full [ PATH_MAX ];
1983 rc_t rc = KSysDirMakePath_v1 ( self, rcOpening, false, full, sizeof full, path, args );
1984 if ( rc == 0 )
1985 {
1986 int fd = open ( full, O_RDONLY );
1987 if ( fd < 0 ) switch ( errno )
1988 {
1989 case ENOENT:
1990 return SILENT_RC ( rcFS, rcDirectory, rcOpening, rcPath, rcNotFound );
1991 case EACCES:
1992 return RC ( rcFS, rcDirectory, rcOpening, rcDirectory, rcUnauthorized );
1993 case EISDIR:
1994 return RC ( rcFS, rcDirectory, rcOpening, rcPath, rcIncorrect );
1995 case ENOTDIR:
1996 case ELOOP:
1997 return RC ( rcFS, rcDirectory, rcOpening, rcPath, rcInvalid );
1998 case ENAMETOOLONG:
1999 return RC ( rcFS, rcDirectory, rcOpening, rcPath, rcExcessive );
2000 case ENOMEM:
2001 return RC ( rcFS, rcDirectory, rcOpening, rcMemory, rcExhausted );
2002 case EMFILE:
2003 case ENFILE:
2004 return RC ( rcFS, rcDirectory, rcOpening, rcFileDesc, rcExhausted );
2005 default:
2006 return RC ( rcFS, rcDirectory, rcOpening, rcNoObj, rcUnknown );
2007 }
2008
2009 rc = KSysFileMake_v1 ( ( KSysFile_v1 ** ) f, fd, full, true, false );
2010 if ( rc != 0 )
2011 close ( fd );
2012 }
2013 return rc;
2014 }
2015
2016 /* KSysDirOpenFileWrite
2017 * opens an existing file with write access
2018 *
2019 * "f" [ OUT ] - return parameter for newly opened file
2020 *
2021 * "path" [ IN ] - NUL terminated string in directory-native
2022 * character set denoting target file
2023 *
2024 * "update" [ IN ] - if non-zero, open in read/write mode
2025 * otherwise, open in write-only mode
2026 */
2027 static
KSysDirOpenFileWrite_v1(KSysDir_v1 * self,KFile_v1 ** f,bool update,const char * path,va_list args)2028 rc_t KSysDirOpenFileWrite_v1 ( KSysDir_v1 * self,
2029 KFile_v1 **f, bool update, const char *path, va_list args )
2030 {
2031 char full [ PATH_MAX ];
2032 rc_t rc = KSysDirMakePath_v1 ( self, rcOpening, false, full, sizeof full, path, args );
2033 if ( rc == 0 )
2034 {
2035 int fd = open ( full, update ? O_RDWR : O_WRONLY );
2036 if ( fd < 0 ) switch ( errno )
2037 {
2038 case ENOENT:
2039 return RC ( rcFS, rcDirectory, rcOpening, rcPath, rcNotFound );
2040 case EACCES:
2041 case EROFS:
2042 return RC ( rcFS, rcDirectory, rcAccessing, rcDirectory, rcUnauthorized );
2043 case EISDIR:
2044 return RC ( rcFS, rcDirectory, rcAccessing, rcPath, rcIncorrect );
2045 case ENOTDIR:
2046 case ELOOP:
2047 return RC ( rcFS, rcDirectory, rcAccessing, rcPath, rcInvalid );
2048 case ENAMETOOLONG:
2049 return RC ( rcFS, rcDirectory, rcAccessing, rcPath, rcExcessive );
2050 case ENOMEM:
2051 return RC ( rcFS, rcDirectory, rcOpening, rcMemory, rcExhausted );
2052 case EMFILE:
2053 case ENFILE:
2054 return RC ( rcFS, rcDirectory, rcOpening, rcFileDesc, rcExhausted );
2055 default:
2056 return RC ( rcFS, rcDirectory, rcOpening, rcNoObj, rcUnknown );
2057 }
2058
2059 rc = KSysFileMake_v1 ( ( KSysFile_v1 ** ) f, fd, full, update, 1 );
2060 if ( rc != 0 )
2061 close ( fd );
2062 }
2063 return rc;
2064 }
2065
2066 /* KSysDirCreateFile
2067 * opens a file with write access
2068 *
2069 * "f" [ OUT ] - return parameter for newly opened file
2070 *
2071 * "path" [ IN ] - NUL terminated string in directory-native
2072 * character set denoting target file
2073 *
2074 * "access" [ IN ] - standard Unix access mode, e.g. 0664
2075 *
2076 * "update" [ IN ] - if non-zero, open in read/write mode
2077 * otherwise, open in write-only mode
2078 *
2079 * "mode" [ IN ] - a creation mode ( see explanation above ).
2080 */
2081 static
KSysDirCreateFile_v1(KSysDir_v1 * self,KFile_v1 ** f,bool update,uint32_t access,KCreateMode cmode,const char * path,va_list args)2082 rc_t KSysDirCreateFile_v1 ( KSysDir_v1 * self, KFile_v1 **f, bool update,
2083 uint32_t access, KCreateMode cmode, const char *path, va_list args )
2084 {
2085 char full [ PATH_MAX ];
2086 rc_t rc = KSysDirMakePath_v1 ( self, rcCreating, true, full, sizeof full, path, args );
2087 if ( rc == 0 )
2088 {
2089 int fd, mode = update ? O_RDWR | O_CREAT : O_WRONLY | O_CREAT;
2090 switch ( cmode & kcmValueMask )
2091 {
2092 case kcmOpen:
2093 break;
2094 case kcmInit:
2095 mode |= O_TRUNC;
2096 break;
2097 case kcmCreate:
2098 mode |= O_EXCL;
2099 break;
2100 case kcmSharedAppend:
2101 mode = O_WRONLY | O_APPEND | O_CREAT;
2102 break;
2103 }
2104
2105 fd = open ( full, mode, ( int ) access );
2106 while ( fd < 0 )
2107 {
2108 /* a common creation error is missing parents */
2109 if ( ( cmode & kcmParents ) != 0 && errno == ENOENT )
2110 {
2111 /* force directory mode to have execute
2112 wherever there is read or write on file */
2113 uint32_t dir_access = access |
2114 ( ( access & 0444 ) >> 2 ) | ( ( access & 0222 ) >> 1 );
2115 /* NEW 2/15/2013 - also force read */
2116 dir_access |= ( dir_access & 0111 ) << 2;
2117 KSysDirCreateParents_v1 ( self, full, dir_access, true );
2118
2119 /* try again */
2120 fd = open ( full, mode, ( int ) access );
2121 if ( fd >= 0 )
2122 break;
2123 }
2124
2125 /* when simply "touching" a file, the request for
2126 write access may fail if created without write access */
2127 if ( ( access & 0200 ) == 0 && errno == EACCES )
2128 {
2129 mode = O_CREAT;
2130 if ( ( access & 0400 ) != 0 )
2131 mode |= O_RDONLY;
2132 fd = open ( full, mode, ( int ) access );
2133 if ( fd >= 0 )
2134 break;
2135 }
2136
2137 switch ( errno )
2138 {
2139 case ENOENT:
2140 rc = RC ( rcFS, rcDirectory, rcCreating, rcPath, rcNotFound );
2141 break;
2142 case EEXIST:
2143 rc = RC ( rcFS, rcDirectory, rcCreating, rcPath, rcExists );
2144 break;
2145 case EACCES:
2146 case EROFS:
2147 rc = RC ( rcFS, rcDirectory, rcCreating, rcDirectory, rcUnauthorized );
2148 break;
2149 case EISDIR:
2150 rc = RC ( rcFS, rcDirectory, rcCreating, rcPath, rcIncorrect );
2151 break;
2152 case ENOTDIR:
2153 case ELOOP:
2154 rc = RC ( rcFS, rcDirectory, rcCreating, rcPath, rcInvalid );
2155 break;
2156 case ENAMETOOLONG:
2157 rc = RC ( rcFS, rcDirectory, rcCreating, rcPath, rcExcessive );
2158 break;
2159 case ENOSPC:
2160 rc = RC ( rcFS, rcDirectory, rcCreating, rcStorage, rcExhausted );
2161 break;
2162 case ENOMEM:
2163 rc = RC ( rcFS, rcDirectory, rcCreating, rcMemory, rcExhausted );
2164 break;
2165 case EMFILE:
2166 case ENFILE:
2167 rc = RC ( rcFS, rcDirectory, rcCreating, rcFileDesc, rcExhausted );
2168 break;
2169 default:
2170 rc = RC ( rcFS, rcDirectory, rcCreating, rcNoObj, rcUnknown );
2171 break;
2172 }
2173
2174 /* disabled 12/12/2012 : it prints an error message, if vdb tries to open
2175 the same reference-object twice via http. The lock-file for the 2nd try
2176 does already exist. This is not an error, just a condition. */
2177
2178 /* PLOGERR (klogErr, (klogErr, rc, "failed to create '$(F)'", "F=%s", full)); */
2179 return rc;
2180 }
2181
2182 rc = KSysFileMake_v1 ( ( KSysFile** ) f, fd, full, update, true );
2183 if ( rc != 0 )
2184 close ( fd );
2185 }
2186 return rc;
2187 }
2188
2189 /* KSysDirFileSize
2190 * returns size in bytes of target file
2191 *
2192 * "path" [ IN ] - NUL terminated string in directory-native
2193 * character set denoting target file
2194 *
2195 * "size" [ OUT ] - return parameter for file size
2196 */
2197 static
KSysDirFileSize_v1(const KSysDir_v1 * self,uint64_t * size,const char * path,va_list args)2198 rc_t KSysDirFileSize_v1 ( const KSysDir_v1 * self,
2199 uint64_t *size, const char *path, va_list args )
2200 {
2201 char full [ PATH_MAX ];
2202 rc_t rc = KSysDirMakePath_v1 ( self, rcAccessing, false, full, sizeof full, path, args );
2203 if ( rc == 0 )
2204 {
2205 struct stat st;
2206 if ( stat ( full, & st ) != 0 ) switch ( errno )
2207 {
2208 case ENOENT:
2209 return RC ( rcFS, rcDirectory, rcAccessing, rcPath, rcNotFound );
2210 case ENOTDIR:
2211 case ELOOP:
2212 return RC ( rcFS, rcDirectory, rcAccessing, rcPath, rcInvalid );
2213 case ENAMETOOLONG:
2214 return RC ( rcFS, rcDirectory, rcAccessing, rcPath, rcExcessive );
2215 case EACCES:
2216 return RC ( rcFS, rcDirectory, rcAccessing, rcDirectory, rcUnauthorized );
2217 case ENOMEM:
2218 return RC ( rcFS, rcDirectory, rcAccessing, rcMemory, rcExhausted );
2219 default:
2220 return RC ( rcFS, rcDirectory, rcAccessing, rcNoObj, rcUnknown );
2221 }
2222
2223 if ( S_ISDIR ( st . st_mode ) )
2224 return RC ( rcFS, rcDirectory, rcAccessing, rcPath, rcIncorrect );
2225
2226 * size = st . st_size;
2227 }
2228 return rc;
2229 }
2230
2231 /* KSysDirSetFileSize
2232 * sets size in bytes of target file
2233 *
2234 * "path" [ IN ] - NUL terminated string in directory-native
2235 * character set denoting target file
2236 *
2237 * "size" [ IN ] - new file size
2238 */
2239 static
KSysDirSetFileSize_v1(KSysDir_v1 * self,uint64_t size,const char * path,va_list args)2240 rc_t KSysDirSetFileSize_v1 ( KSysDir_v1 * self,
2241 uint64_t size, const char *path, va_list args )
2242 {
2243 char full [ PATH_MAX ];
2244 rc_t rc = KSysDirMakePath_v1 ( self, rcUpdating, false, full, sizeof full, path, args );
2245 if ( rc == 0 )
2246 {
2247 if ( truncate ( full, size ) != 0 ) switch ( errno )
2248 {
2249 case ENOENT:
2250 return RC ( rcFS, rcDirectory, rcUpdating, rcPath, rcNotFound );
2251 case EACCES:
2252 case EROFS:
2253 return RC ( rcFS, rcDirectory, rcUpdating, rcDirectory, rcUnauthorized );
2254 case EFBIG:
2255 return RC ( rcFS, rcDirectory, rcUpdating, rcParam, rcExcessive );
2256 case EINTR:
2257 return RC ( rcFS, rcDirectory, rcUpdating, rcFunction, rcIncomplete );
2258 case EINVAL:
2259 return RC ( rcFS, rcDirectory, rcUpdating, rcParam, rcInvalid );
2260 case EIO:
2261 return RC ( rcFS, rcDirectory, rcUpdating, rcTransfer, rcUnknown );
2262 case EISDIR:
2263 return RC ( rcFS, rcDirectory, rcUpdating, rcPath, rcIncorrect );
2264 case ELOOP:
2265 return RC ( rcFS, rcDirectory, rcUpdating, rcPath, rcInvalid );
2266 case ENAMETOOLONG:
2267 return RC ( rcFS, rcDirectory, rcUpdating, rcPath, rcExcessive );
2268 default:
2269 return RC ( rcFS, rcDirectory, rcUpdating, rcNoObj, rcUnknown );
2270 }
2271 }
2272 return rc;
2273 }
2274
2275 /* KSysDirOpenDirRead
2276 * KSysDirOpenDirUpdate
2277 * opens a sub-directory
2278 *
2279 * "path" [ IN ] - NUL terminated string in directory-native
2280 * character set denoting target directory
2281 *
2282 * "chroot" [ IN ] - if non-zero, the new directory becomes
2283 * chroot'd and will interpret paths beginning with '/'
2284 * relative to itself.
2285 */
2286 static
KSysDirOpenDirRead_v1(const KSysDir_v1 * self,const KDirectory_v1 ** subp,bool chroot,const char * path,va_list args)2287 rc_t KSysDirOpenDirRead_v1 ( const KSysDir_v1 * self,
2288 const KDirectory_v1 **subp, bool chroot, const char *path, va_list args )
2289 {
2290 char full [ PATH_MAX ];
2291 rc_t rc;
2292
2293 rc = KSysDirMakePath_v1 ( self, rcOpening, true, full, sizeof full, path, args );
2294 if ( rc == 0 )
2295 {
2296 int t;
2297 KSysDir_v1 *sub;
2298
2299 size_t path_size = strlen ( full );
2300 while ( path_size > 1 && full [ path_size - 1 ] == '/' )
2301 full [ -- path_size ] = 0;
2302
2303 t = KSysDirFullPathType_v1 ( full ) & ( kptAlias - 1 );
2304 if ( t == kptNotFound )
2305 return RC ( rcFS, rcDirectory, rcOpening, rcPath, rcNotFound );
2306 if ( t != kptDir )
2307 return RC ( rcFS, rcDirectory, rcOpening, rcPath, rcIncorrect );
2308
2309 sub = KSysDirMake_v1 ( path_size );
2310 if ( sub == NULL )
2311 rc = RC ( rcFS, rcDirectory, rcOpening, rcMemory, rcExhausted );
2312 else
2313 {
2314 rc = KSysDirInit_v1 ( sub, rcOpening, self -> root, full, path_size, false, chroot );
2315 if ( rc == 0 )
2316 {
2317 * subp = & sub -> dad;
2318 return 0;
2319 }
2320
2321 free ( sub );
2322 }
2323 }
2324 return rc;
2325 }
2326
2327 static
KSysDirOpenDirUpdate_v1(KSysDir_v1 * self,KDirectory_v1 ** subp,bool chroot,const char * path,va_list args)2328 rc_t KSysDirOpenDirUpdate_v1 ( KSysDir_v1 * self,
2329 KDirectory_v1 **subp, bool chroot, const char *path, va_list args )
2330 {
2331 char full [ PATH_MAX ];
2332 rc_t rc;
2333
2334 rc = KSysDirMakePath_v1 ( self, rcOpening, true, full, sizeof full, path, args );
2335 if ( rc == 0 )
2336 {
2337 KSysDir_v1 *sub;
2338
2339 size_t path_size = strlen ( full );
2340 while ( path_size > 1 && full [ path_size - 1 ] == '/' )
2341 full [ -- path_size ] = 0;
2342
2343 switch ( KSysDirFullPathType_v1 ( full ) )
2344 {
2345 case kptNotFound:
2346 return RC ( rcFS, rcDirectory, rcOpening, rcPath, rcNotFound );
2347 case kptBadPath:
2348 return RC ( rcFS, rcDirectory, rcOpening, rcPath, rcInvalid );
2349 case kptDir:
2350 case kptDir | kptAlias:
2351 break;
2352 default:
2353 return RC ( rcFS, rcDirectory, rcOpening, rcPath, rcIncorrect );
2354 }
2355
2356 sub = KSysDirMake_v1 ( path_size );
2357 if ( sub == NULL )
2358 rc = RC ( rcFS, rcDirectory, rcOpening, rcMemory, rcExhausted );
2359 else
2360 {
2361 rc = KSysDirInit_v1 ( sub, rcOpening, self -> root, full, path_size, true, chroot );
2362 if ( rc == 0 )
2363 {
2364 * subp = & sub -> dad;
2365 return 0;
2366 }
2367
2368 free ( sub );
2369 }
2370 }
2371 return rc;
2372 }
2373
2374 /* KSysDirCreateDir
2375 * create a sub-directory
2376 *
2377 * "path" [ IN ] - NUL terminated string in directory-native
2378 * character set denoting target directory
2379 *
2380 * "access" [ IN ] - standard Unix directory permissions
2381 *
2382 * "mode" [ IN ] - a creation mode ( see explanation above ).
2383 */
2384 static
KSysDirCreateDir_v1(KSysDir_v1 * self,uint32_t access,KCreateMode mode,const char * path,va_list args)2385 rc_t KSysDirCreateDir_v1 ( KSysDir_v1 * self,
2386 uint32_t access, KCreateMode mode, const char *path, va_list args )
2387 {
2388 char full [ PATH_MAX ];
2389 rc_t rc = KSysDirMakePath_v1 ( self, rcCreating, true, full, sizeof full, path, args );
2390 if ( rc == 0 )
2391 {
2392 if ( ( mode & kcmValueMask ) == kcmCreate )
2393 {
2394 switch ( KSysDirFullPathType_v1 ( full ) )
2395 {
2396 case kptNotFound:
2397 break;
2398 case kptBadPath:
2399 return RC ( rcFS, rcDirectory, rcCreating, rcPath, rcInvalid );
2400 case kptDir:
2401 return RC ( rcFS, rcDirectory, rcCreating, rcDirectory, rcExists );
2402 default:
2403 return RC ( rcFS, rcDirectory, rcCreating, rcPath, rcIncorrect );
2404 }
2405 }
2406
2407 rc = make_dir_v1 ( full, access );
2408 if ( rc != 0 ) switch ( GetRCState ( rc ) )
2409 {
2410 case rcExists:
2411 rc = 0;
2412 if ( ( mode & kcmValueMask ) == kcmInit )
2413 rc = KSysDirEmptyDir_v1 ( full, sizeof full, 1 );
2414 break;
2415 case rcNotFound:
2416 if ( ( mode & kcmParents ) != 0 )
2417 rc = KSysDirCreateParents_v1 ( self, full, access, false );
2418 break;
2419 default:
2420 break;
2421 }
2422 }
2423 return rc;
2424 }
2425
2426
2427 /* FileLocator
2428 * returns a 64-bit key pertinent only to the particular file
2429 * system device holding that file.
2430 *
2431 * It can be used as a form of sort key except that it is not
2432 * guaranteed to be unique.
2433 *
2434 * "locator" [ OUT ] - return parameter for file locator
2435 *
2436 * "path" [ IN ] - NUL terminated string in directory-native
2437 * character set denoting target file
2438 */
2439 static
KSysDirFileLocator_v1(const KSysDir_v1 * self,uint64_t * locator,const char * path,va_list args)2440 rc_t CC KSysDirFileLocator_v1 ( const KSysDir_v1 *self,
2441 uint64_t *locator, const char *path, va_list args )
2442 {
2443 /* TBD - could return an inode */
2444 assert ( locator != NULL );
2445 * locator = 0;
2446 return RC ( rcFS, rcDirectory, rcAccessing, rcFunction, rcUnsupported );
2447 }
2448
2449 /* FilePhysicalSize
2450 * returns physical allocated size in bytes of target file. It might
2451 * or might not differ from FileSize
2452 *
2453 * "size" [ OUT ] - return parameter for file size
2454 *
2455 * "path" [ IN ] - NUL terminated string in directory-native
2456 * character set denoting target file
2457 */
2458 static
KSysDirFilePhysicalSize_v1(const KSysDir_v1 * self,uint64_t * size,const char * path,va_list args)2459 rc_t CC KSysDirFilePhysicalSize_v1 ( const KSysDir_v1 *self,
2460 uint64_t *size, const char *path, va_list args )
2461 {
2462 /* TBD - can be completed */
2463 assert ( size != NULL );
2464 * size = 0;
2465 return RC ( rcFS, rcDirectory, rcAccessing, rcFunction, rcUnsupported );
2466 }
2467
2468 /* FileContiguous
2469 * returns true if the file is "contiguous". Chunked or sparse files are not
2470 * contiguous while most data files are. Virtual generated files would likely
2471 * not be contiguous.
2472 *
2473 * "contiguous" [ OUT ] - return parameter for file contiguous
2474 *
2475 * "path" [ IN ] - NUL terminated string in directory-native
2476 * character set denoting target file
2477 */
2478 static
KSysDirFileContiguous_v1(const KSysDir_v1 * self,bool * contiguous,const char * path,va_list args)2479 rc_t CC KSysDirFileContiguous_v1 ( const KSysDir_v1 *self,
2480 bool *contiguous, const char *path, va_list args )
2481 {
2482 assert ( contiguous != NULL );
2483 * contiguous = true;
2484 return 0;
2485 }
2486
2487
2488 /* KDirectoryNativeDir
2489 * returns a native file-system directory node reference
2490 * the directory root will be "/" and set to the native
2491 * idea of current working directory
2492 *
2493 * NB - the returned reference will be non-const, allowing
2494 * modification operations to be attempted. these operations
2495 * may still fail if the underlying FS disallows them.
2496 *
2497 * "dir" [ OUT ] - return parameter for native directory
2498 */
2499
2500 static KDirectory_vt_v1 vtKSysDir =
2501 {
2502 /* version 1.5 */
2503 1, 5,
2504
2505 /* start minor version 0*/
2506 KSysDirDestroy_v1,
2507 KSysDirList_v1,
2508
2509 /* the following two messages map to the same method, requiring type casting */
2510 ( rc_t ( * ) ( const KSysDir_v1*, bool,
2511 rc_t ( * ) ( const KDirectory_v1*, uint32_t, const char*, void* ), void*,
2512 const char*, va_list ) ) KSysDirVisit_v1,
2513 ( rc_t ( * ) ( KSysDir_v1*, bool,
2514 rc_t ( * ) ( KDirectory_v1*, uint32_t, const char*, void* ), void*,
2515 const char*, va_list ) ) KSysDirVisit_v1,
2516
2517 KSysDirPathType_v1,
2518 KSysDirResolvePath_v1,
2519 KSysDirResolveAlias_v1,
2520 KSysDirRename_v1,
2521 KSysDirRemove_v1,
2522 KSysDirClearDir_v1,
2523 KSysDirVAccess,
2524 KSysDirSetAccess_v1,
2525 KSysDirCreateAlias_v1,
2526 KSysDirOpenFileRead_v1,
2527 KSysDirOpenFileWrite_v1,
2528 KSysDirCreateFile_v1,
2529 KSysDirFileSize_v1,
2530 KSysDirSetFileSize_v1,
2531 KSysDirOpenDirRead_v1,
2532 KSysDirOpenDirUpdate_v1,
2533 KSysDirCreateDir_v1,
2534 NULL, /* we don't track files*/
2535 /* end minor version 0*/
2536
2537 /* start minor version 1*/
2538 KSysDirVDate,
2539 KSysDirVSetDate,
2540 KSysDirGetSysdir_v1,
2541 /* end minor version 1*/
2542
2543 /* start minor version 2 */
2544 KSysDirFileLocator_v1,
2545 /* end minor version 2 */
2546
2547 /* start minor version 3 */
2548 KSysDirFilePhysicalSize_v1,
2549 KSysDirFileContiguous_v1,
2550 /* end minor version 3 */
2551
2552 /* start minor version 4 */
2553 KSysDirOpenFileWrite_v1,
2554 /* end minor version 4 */
2555
2556 /* start minor version 5 */
2557 KSysDirCreateLink_v1,
2558 /* end minor version 5 */
2559 };
2560
2561 /* KSysDirInit
2562 */
2563 static
KSysDirInit_v1(KSysDir_v1 * self,enum RCContext ctx,uint32_t dad_root,const char * path,uint32_t path_size,bool update,bool chroot)2564 rc_t KSysDirInit_v1 ( KSysDir_v1 * self, enum RCContext ctx, uint32_t dad_root,
2565 const char *path, uint32_t path_size, bool update, bool chroot )
2566 {
2567 rc_t rc;
2568
2569 rc = KDirectoryInit ( & self -> dad, ( const KDirectory_vt * ) & vtKSysDir,
2570 "KSysDir", path?path:"(null)", update );
2571 if ( rc != 0 )
2572 return ResetRCContext ( rc, rcFS, rcDirectory, ctx );
2573
2574 if ( path != NULL )
2575 memmove ( self -> path, path, path_size );
2576 self -> root = chroot ? path_size : dad_root;
2577 self -> size = path_size + 1;
2578 self -> path [ path_size ] = '/';
2579 self -> path [ path_size + 1 ] = 0;
2580
2581 return 0;
2582 }
2583
2584 extern rc_t CC ReportCWD ( const ReportFuncs *f, uint32_t indent );
2585 extern rc_t CC ReportRedirect ( KWrtHandler* handler,
2586 const char* filename, bool* to_file, bool finalize );
2587
KDirectoryNativeDir_v1(KDirectory_v1 ** dirp)2588 LIB_EXPORT rc_t CC KDirectoryNativeDir_v1 ( KDirectory_v1 **dirp )
2589 {
2590 rc_t rc;
2591 KSysDir_v1 *dir;
2592 uint32_t size;
2593 char wd [ PATH_MAX ];
2594
2595 static bool latch;
2596 if ( ! latch )
2597 {
2598 ReportInitKFS ( ReportCWD, ReportRedirect );
2599 latch = true;
2600 }
2601
2602 if ( dirp == NULL )
2603 return RC ( rcFS, rcDirectory, rcAccessing, rcParam, rcNull );
2604
2605 * dirp = NULL;
2606
2607 if ( realpath ( ".", wd ) == NULL ) switch ( errno )
2608 {
2609 case EACCES:
2610 return RC ( rcFS, rcDirectory, rcAccessing, rcDirectory, rcUnauthorized );
2611 case EIO:
2612 return RC ( rcFS, rcDirectory, rcAccessing, rcTransfer, rcUnknown );
2613 default:
2614 return RC ( rcFS, rcDirectory, rcAccessing, rcNoObj, rcUnknown );
2615 }
2616
2617 size = strlen ( wd );
2618 if ( size + 2 > sizeof wd )
2619 return RC ( rcFS, rcDirectory, rcAccessing, rcBuffer, rcInsufficient );
2620
2621 /* trim trailing slash */
2622 if ( size > 0 && wd [ size - 1 ] == '/' )
2623 wd [ -- size ] = 0;
2624
2625 dir = KSysDirMake_v1 ( size );
2626 if ( dir == NULL )
2627 rc = RC ( rcFS, rcDirectory, rcAccessing, rcMemory, rcExhausted );
2628 else
2629 {
2630 rc = KSysDirInit_v1 ( dir, rcAccessing, 0, wd, size, true, false );
2631 if ( rc == 0 )
2632 {
2633 * dirp = & dir -> dad;
2634 return 0;
2635 }
2636
2637 free ( dir );
2638 }
2639
2640 return rc;
2641 }
2642
KDirectoryGetDiskFreeSpace_v1(const KDirectory * self,uint64_t * free_bytes_available,uint64_t * total_number_of_bytes)2643 LIB_EXPORT rc_t CC KDirectoryGetDiskFreeSpace_v1 ( const KDirectory * self,
2644 uint64_t * free_bytes_available, uint64_t * total_number_of_bytes )
2645 {
2646 if ( self == NULL )
2647 return RC ( rcFS, rcDirectory, rcAccessing, rcSelf, rcNull );
2648 else {
2649 KSysDir_v1 * dir = ( KSysDir_v1 * ) self;
2650 struct statvfs buf;
2651 memset ( & buf, 0, sizeof buf );
2652 if ( statvfs ( dir -> path, & buf) == 0 ) {
2653 if ( free_bytes_available != NULL ) {
2654 * free_bytes_available = buf . f_bavail * buf . f_frsize;
2655 }
2656 if ( total_number_of_bytes != NULL ) {
2657 * total_number_of_bytes = buf . f_blocks * buf . f_frsize;
2658 }
2659 return 0;
2660 }
2661
2662 return RC ( rcFS, rcDirectory, rcAccessing, rcError, rcUnknown );
2663 }
2664 }
2665