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