1 /*
2 Copyright (c) 2012, Broadcom Europe Ltd
3 All rights reserved.
4 
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7     * Redistributions of source code must retain the above copyright
8       notice, this list of conditions and the following disclaimer.
9     * Redistributions in binary form must reproduce the above copyright
10       notice, this list of conditions and the following disclaimer in the
11       documentation and/or other materials provided with the distribution.
12     * Neither the name of the copyright holder nor the
13       names of its contributors may be used to endorse or promote products
14       derived from this software without specific prior written permission.
15 
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27 
28 #define VCOS_LOG_CATEGORY (&hostfs_log_cat)
29 
30 #ifndef  _LARGEFILE_SOURCE
31 #define  _LARGEFILE_SOURCE
32 #endif
33 #ifndef  _LARGEFILE64_SOURCE
34 #define  _LARGEFILE64_SOURCE
35 #endif
36 #define  _FILE_OFFSET_BITS 64    /* So we get lseek and lseek64 */
37 
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <stdint.h>
41 #include <string.h>
42 #include <ctype.h>
43 #include <unistd.h>
44 #include <sys/stat.h>
45 #include <sys/statfs.h>
46 #include <fcntl.h>
47 #include <errno.h>
48 #include <assert.h>
49 #include <ctype.h>
50 #include <limits.h>
51 
52 #if defined(__GLIBC__) && !defined( __USE_FILE_OFFSET64 )
53 #error   "__USE_FILE_OFFSET64 isn't defined"
54 #endif
55 
56 #include "interface/vcos/vcos.h"
57 
58 /* Some hackery to prevent a clash with the Linux type of the same name */
59 #define dirent fs_dirent
60 #include "vcfilesys_defs.h"
61 #include "vchost.h"
62 #undef dirent
63 
64 #include <dirent.h>
65 
66 #include "vc_fileservice_defs.h"
67 
68 VCOS_LOG_CAT_T hostfs_log_cat;
69 
70 /******************************************************************************
71 Global data.
72 ******************************************************************************/
73 
74 /******************************************************************************
75 Local types and defines.
76 ******************************************************************************/
77 
78 //#define DEBUG_LEVEL 1
79 #define DEBUG_MINOR(...) vcos_log_info(__VA_ARGS__)
80 #define DEBUG_MAJOR(...) vcos_log_warn(__VA_ARGS__)
81 
82 /* Define a wrapper for the native directory handle which includes the path
83  * to that directory (needed to retrieve size and attributes via stat()).
84  */
85 
86 struct fs_dir
87 {
88    DIR *dhandle;
89    int pathlen;
90    char pathbuf[PATH_MAX];
91 };
92 
93 /*
94  *  The media player on the Videocore may be asked to open a file on the Host that
95  *  is in fact a FIFO.  We need to note when a FIFO has been opened so that we
96  *  can fake out some FIFO seeks that the Videocore may perform, hence the following
97  *  types and variables.
98  */
99 
100 typedef struct
101 {
102    int is_fifo;            // non-zero if file is a FIFO
103    uint64_t read_offset;   // read offset into file
104 } file_info_t;
105 
106 #define FILE_INFO_TABLE_CHUNK_LEN   20
107 
108 /******************************************************************************
109 Static data.
110 ******************************************************************************/
111 
112 static file_info_t *p_file_info_table = NULL;
113 static int file_info_table_len = 0;
114 
115 /******************************************************************************
116 Static functions.
117 ******************************************************************************/
118 
119 static void backslash_to_slash( char *s );
120 
121 /******************************************************************************
122 Global functions.
123 ******************************************************************************/
124 
125 /******************************************************************************
126 NAME
127    vc_hostfs_init
128 
129 SYNOPSIS
130    void vc_hostfs_init(void)
131 
132 FUNCTION
133    Initialises the host to accept requests from Videocore
134 
135 RETURNS
136    void
137 ******************************************************************************/
138 
vc_hostfs_init(void)139 void vc_hostfs_init(void)
140 {
141    // This hostfs module is not thread safe - it allocaes a block
142    // of memory and uses it without any kind of locking.
143    //
144    // It offers no advantage of stdio, and so most clients should
145    // not use it. Arguably FILESYS should use it in order to get
146    // the FIFO support.
147 
148    const char *thread_name = vcos_thread_get_name(vcos_thread_current());
149    if (strcmp(thread_name, "FILESYS") != 0 && strcmp(thread_name, "HFilesys") != 0)
150    {
151       fprintf(stderr,"%s: vc_hostfs is deprecated. Please use stdio\n",
152               vcos_thread_get_name(vcos_thread_current()));
153    }
154 
155    vcos_log_register("hostfs", &hostfs_log_cat);
156    DEBUG_MINOR("init");
157    // Allocate memory for the file info table
158    p_file_info_table = (file_info_t *)calloc( FILE_INFO_TABLE_CHUNK_LEN, sizeof( file_info_t ) );
159    assert( p_file_info_table != NULL );
160    if (p_file_info_table)
161    {
162       file_info_table_len = FILE_INFO_TABLE_CHUNK_LEN;
163    }
164 }
165 
166 /** Terminate this library. Clean up resources.
167   */
168 
vc_hostfs_exit(void)169 void vc_hostfs_exit(void)
170 {
171    vcos_log_unregister(&hostfs_log_cat);
172    if (p_file_info_table)
173    {
174       free(p_file_info_table);
175       p_file_info_table = NULL;
176    }
177 }
178 
179 /******************************************************************************
180 NAME
181    vc_hostfs_close
182 
183 SYNOPSIS
184    int vc_hostfs_close(int fildes)
185 
186 FUNCTION
187    Deallocates the file descriptor to a file.
188 
189 RETURNS
190    Successful completion: 0
191    Otherwise: -1
192 ******************************************************************************/
193 
vc_hostfs_close(int fildes)194 int vc_hostfs_close(int fildes)
195 {
196    DEBUG_MINOR("vc_hostfs_close(%d)", fildes);
197    return close(fildes);
198 }
199 
200 /******************************************************************************
201 NAME
202    vc_hostfs_lseek
203 
204 SYNOPSIS
205    long vc_hostfs_lseek(int fildes, long offset, int whence)
206 
207 FUNCTION
208    Sets the file pointer associated with the open file specified by fildes.  If
209    the file is a FIFO (Linux does not support seeking on a FIFO) then, for the
210    benefit of the Videocore streaming file handlers which do a number of null seeks,
211    that is, seeks to the current position, the return value is faked without an
212    actual seek being done.
213 
214 RETURNS
215    Successful completion: offset
216    Otherwise: -1
217 ******************************************************************************/
218 
vc_hostfs_lseek(int fildes,long offset,int whence)219 long vc_hostfs_lseek(int fildes, long offset, int whence)
220 {
221    return (long) vc_hostfs_lseek64( fildes, (int64_t) offset, whence);
222 }
223 
224 /******************************************************************************
225 NAME
226    vc_hostfs_lseek64
227 
228 SYNOPSIS
229    int64_t vc_hostfs_lseek64(int fildes, int64_t offset, int whence)
230 
231 FUNCTION
232    Sets the file pointer associated with the open file specified by fildes.  If
233    the file is a FIFO (Linux does not support seeking on a FIFO) then, for the
234    benefit of the Videocore streaming file handlers which do a number of null seeks,
235    that is, seeks to the current position, the return value is faked without an
236    actual seek being done.
237 
238 RETURNS
239    Successful completion: offset
240    Otherwise: -1
241 ******************************************************************************/
242 
vc_hostfs_lseek64(int fildes,int64_t offset,int whence)243 int64_t vc_hostfs_lseek64(int fildes, int64_t offset, int whence)
244 {
245    DEBUG_MINOR("vc_hostfs_lseek(%d,%" PRId64 ",%d)", fildes, offset, whence);
246    if (fildes >= file_info_table_len)
247    {
248       // File descriptor not in table, so this is an error
249       DEBUG_MAJOR("vc_hostfs_lseek: invalid fildes %d", fildes);
250       return -1;
251    }
252    else
253    {
254       // There is entry in the file info table for this file descriptor, so go
255       // ahead and handle the seek
256       int64_t read_offset = p_file_info_table[fildes].read_offset;
257 
258       if (p_file_info_table[fildes].is_fifo)
259       {
260          // The Videocore is attempting to seek on a FIFO.  FIFOs don't support seeking
261          // but, for the benefit of certain Videocore "streaming" file handlers, we
262          // will fake limited FIFO seek functionality by computing where a seek
263          // would take us to
264          if (whence == SEEK_SET)
265          {
266             read_offset = offset;
267          }
268          else if (whence == SEEK_CUR)
269          {
270             read_offset += offset;
271          }
272          else
273          {
274             // seeking to the end of FIFO makes no sense, so this is an error
275             DEBUG_MAJOR("vc_hostfs_lseek(%d,%lld,%d): SEEK_END not supported on FIFO", fildes, (long long)offset, whence);
276             return -1;
277          }
278       }
279       else
280       {
281          // File is not a FIFO, so do the seek
282          read_offset = lseek64(fildes, offset, whence);
283       }
284       p_file_info_table[fildes].read_offset = read_offset;
285       DEBUG_MINOR("vc_hostfs_lseek returning %" PRId64 ")", read_offset);
286       return read_offset;
287    }
288 }
289 
290 /******************************************************************************
291 NAME
292    vc_hostfs_open
293 
294 SYNOPSIS
295    int vc_hostfs_open(const char *path, int vc_oflag)
296 
297 FUNCTION
298    Establishes a connection between a file and a file descriptor.  For the benefit
299    of faking out seeks on a FIFO, we will need to keep track of the read offset for
300    all reads, and to facilitate this each opened file is given an entry in a local
301    file info table.
302 
303 RETURNS
304    Successful completion: file descriptor
305    Otherwise: -1
306 ******************************************************************************/
307 
vc_hostfs_open(const char * inPath,int vc_oflag)308 int vc_hostfs_open(const char *inPath, int vc_oflag)
309 {
310    char *path = strdup( inPath );
311    //char *s;
312    int flags = 0, ret=errno;
313    struct stat fileStat;
314 
315    // Replace all '\' with '/'
316    backslash_to_slash( path );
317 
318 #if 0
319    s = path + strlen( path );
320    if (( s - path ) >= 4 )
321    {
322       if ( strcasecmp( &s[ -4 ], ".vll" ) == 0 )
323       {
324          // The Videocore is asking for a .vll file. Since it isn't consistent with
325          // the case, we convert .vll files to all lowercase.
326           "vc_hostfs_open: '%s'", path ;
327 
328          s--;	 // backup to the last character (*s is on the '\0')
329          while (( s >= path ) && ( *s != '/' ))
330          {
331             *s = tolower( *s );
332             s--;
333          }
334       }
335    }
336 #endif
337    DEBUG_MINOR("vc_hostfs_open: '%s'", path);
338 
339    flags = O_RDONLY;
340    if (vc_oflag & VC_O_WRONLY)  flags =  O_WRONLY;
341    if (vc_oflag & VC_O_RDWR)    flags =  O_RDWR;
342    if (vc_oflag & VC_O_APPEND)  flags |= O_APPEND;
343    if (vc_oflag & VC_O_CREAT)   flags |= O_CREAT;
344    if (vc_oflag & VC_O_TRUNC)   flags |= O_TRUNC;
345    if (vc_oflag & VC_O_EXCL)    flags |= O_EXCL;
346 
347    //while (*path == '\\') path++; // do not want initial '\'
348    if (flags & O_CREAT)
349       ret = open(path, flags, S_IRUSR | S_IWUSR );
350    else
351       ret = open(path, flags );
352 
353    if (ret < 0 )
354    {
355       DEBUG_MINOR("vc_hostfs_open(%s,%d) = %d", path, vc_oflag, ret);
356    }
357    else
358    {
359       DEBUG_MINOR("vc_hostfs_open(%s,%d) = %d", path, vc_oflag, ret);
360    }
361 
362    // If the file was successfully open then initialize its entry in
363    // the file info table.  If necessary, we expand the size of the table
364    if (ret >= 0)
365    {
366       // File was successfully opened
367       if (ret >= file_info_table_len)
368       {
369          file_info_t *p_new_file_info_table = p_file_info_table;
370          int new_file_info_table_len = file_info_table_len;
371 
372          // try and allocate a bigger buffer for the file info table
373          new_file_info_table_len += FILE_INFO_TABLE_CHUNK_LEN;
374          p_new_file_info_table = calloc( (size_t)new_file_info_table_len, sizeof( file_info_t ) );
375          if (p_new_file_info_table == NULL)
376          {
377             // calloc failed
378             DEBUG_MAJOR("vc_hostfs_open: file_info_table calloc failed");
379             assert( 0 );
380          }
381          else
382          {
383             // calloc successful, so copy data from previous buffer to new buffer,
384             // free previous buffer and update ptr and len info
385             memcpy( p_new_file_info_table, p_file_info_table, sizeof( file_info_t ) * file_info_table_len );
386             free( p_file_info_table );
387             p_file_info_table = p_new_file_info_table;
388             file_info_table_len = new_file_info_table_len;
389          }
390       }
391       assert( ret < file_info_table_len );
392       {
393          // initialize this file's entry in the file info table
394          p_file_info_table[ret].is_fifo = 0;
395          p_file_info_table[ret].read_offset = 0;
396       }
397 
398       // Check whether the file is a FIFO.  A FIFO does not support seeking
399       // but we will fake, to the extent supported by the buffered file system
400       // on the Videocore, limited FIFO seek functionality.  This is for the benefit
401       // of certain Videocore "streaming" file handlers.
402       if (fstat( ret, &fileStat ) != 0)
403       {
404          DEBUG_MINOR("vc_hostfs_open: fstat failed: %s", strerror(errno));
405       }
406       else if (S_ISFIFO( fileStat.st_mode ))
407       {
408          // file is a FIFO, so note its fildes for future reference
409          p_file_info_table[ret].is_fifo = 1;
410          DEBUG_MINOR("vc_hostfs_open: file with fildes %d is a FIFO", ret);
411       }
412    }
413 
414    free( path );
415 
416    return ret;
417 }
418 
419 /******************************************************************************
420 NAME
421    vc_hostfs_read
422 
423 SYNOPSIS
424    int vc_hostfs_read(int fildes, void *buf, unsigned int nbyte)
425 
426 FUNCTION
427    Attempts to read nbyte bytes from the file associated with the file
428    descriptor, fildes, into the buffer pointed to by buf.  For the benefit
429    of faking out seeks on a FIFO, we keep track of the read offset for all
430    reads.
431 
432 RETURNS
433    Successful completion: number of bytes read
434    Otherwise: -1
435 ******************************************************************************/
436 
vc_hostfs_read(int fildes,void * buf,unsigned int nbyte)437 int vc_hostfs_read(int fildes, void *buf, unsigned int nbyte)
438 {
439    if (fildes >= file_info_table_len)
440    {
441       // File descriptor not in table, so this is an error
442       DEBUG_MAJOR("vc_hostfs_read(%d,%p,%u): invalid fildes", fildes, buf, nbyte);
443       return -1;
444    }
445    else
446    {
447       // There is entry in the file info table for this file descriptor, so go
448       // ahead and handle the read
449       int ret = (int) read(fildes, buf, nbyte);
450       DEBUG_MINOR("vc_hostfs_read(%d,%p,%u) = %d", fildes, buf, nbyte, ret);
451       if (ret > 0)
452       {
453          p_file_info_table[fildes].read_offset += (long) ret;
454       }
455       return ret;
456    }
457 }
458 
459 /******************************************************************************
460 NAME
461    vc_hostfs_write
462 
463 SYNOPSIS
464    int vc_hostfs_write(int fildes, const void *buf, unsigned int nbyte)
465 
466 FUNCTION
467    Attempts to write nbyte bytes from the buffer pointed to by buf to file
468    associated with the file descriptor, fildes.
469 
470 RETURNS
471    Successful completion: number of bytes written
472    Otherwise: -1
473 ******************************************************************************/
474 
vc_hostfs_write(int fildes,const void * buf,unsigned int nbyte)475 int vc_hostfs_write(int fildes, const void *buf, unsigned int nbyte)
476 {
477    int ret = (int) write(fildes, buf, nbyte);
478    DEBUG_MINOR("vc_hostfs_write(%d,%p,%u) = %d", fildes, buf, nbyte, ret);
479    return ret;
480 }
481 
482 /******************************************************************************
483 NAME
484    vc_hostfs_closedir
485 
486 SYNOPSIS
487    int vc_hostfs_closedir(void *dhandle)
488 
489 FUNCTION
490    Ends a directory list iteration.
491 
492 RETURNS
493    Successful completion: 0
494    Otherwise: -1
495 ******************************************************************************/
496 
vc_hostfs_closedir(void * dhandle)497 int vc_hostfs_closedir(void *dhandle)
498 {
499    struct fs_dir *fsdir = (struct fs_dir *)dhandle;
500    int ret = -1;
501 
502    DEBUG_MINOR( "vc_hostfs_closedir(%p)", dhandle );
503 
504    if (dhandle && fsdir->dhandle)
505    {
506       (void)closedir(fsdir->dhandle);
507       fsdir->dhandle = NULL;
508       free(fsdir);
509       ret = 0;
510    }
511 
512    return ret;
513 }
514 
515 /******************************************************************************
516 NAME
517    vc_hostfs_format
518 
519 SYNOPSIS
520    int vc_hostfs_format(const char *path)
521 
522 FUNCTION
523    Formats the physical file system that contains path.
524 
525 RETURNS
526    Successful completion: 0
527    Otherwise: -1
528 ******************************************************************************/
529 
vc_hostfs_format(const char * path)530 int vc_hostfs_format(const char *path)
531 {
532    DEBUG_MINOR("vc_hostfs_format: '%s' not implemented", path);
533    return -1;
534 }
535 
536 /******************************************************************************
537 NAME
538    vc_hostfs_freespace
539 
540 SYNOPSIS
541    int vc_hostfs_freespace(const char *path)
542 
543 FUNCTION
544    Returns the amount of free space on the physical file system that contains
545    path.
546 
547 RETURNS
548    Successful completion: free space
549    Otherwise: -1
550 ******************************************************************************/
551 
vc_hostfs_freespace(const char * inPath)552 int vc_hostfs_freespace(const char *inPath)
553 {
554    int ret;
555 
556    int64_t freeSpace =  vc_hostfs_freespace64( inPath );
557 
558    // Saturate return value (need this in case we have a large file system)
559    if (freeSpace > (int64_t) INT_MAX)
560    {
561       ret = INT_MAX;
562    }
563    else
564    {
565       ret = (int) freeSpace;
566    }
567 
568    return ret;
569 }
570 
571 /******************************************************************************
572 NAME
573    vc_hostfs_freespace
574 
575 SYNOPSIS
576    int vc_hostfs_freespace(const char *path)
577 
578 FUNCTION
579    Returns the amount of free space on the physical file system that contains
580    path.
581 
582 RETURNS
583    Successful completion: free space
584    Otherwise: -1
585 ******************************************************************************/
vc_hostfs_freespace64(const char * inPath)586 int64_t vc_hostfs_freespace64(const char *inPath)
587 {
588    char *path = strdup( inPath );
589    int64_t ret;
590    struct statfs fsStat;
591 
592    // Replace all '\' with '/'
593    backslash_to_slash( path );
594 
595    ret = (int64_t) statfs( path, &fsStat );
596 
597    if (ret == 0)
598    {
599       ret = fsStat.f_bsize * fsStat.f_bavail;
600    }
601    else
602    {
603       ret = -1;
604    }
605 
606    DEBUG_MINOR( "vc_hostfs_freespace64 for '%s' returning %" PRId64 "", path, ret );
607 
608    free( path );
609    return ret;
610 }
611 
612 /******************************************************************************
613 NAME
614    vc_hostfs_get_attr
615 
616 SYNOPSIS
617    int vc_hostfs_get_attr(const char *path, fattributes_t *attr)
618 
619 FUNCTION
620    Gets the file/directory attributes.
621 
622 RETURNS
623    Successful completion: 0
624    Otherwise: -1
625 ******************************************************************************/
626 
vc_hostfs_get_attr(const char * path,fattributes_t * attr)627 int vc_hostfs_get_attr(const char *path, fattributes_t *attr)
628 {
629     struct stat sb;
630 
631     DEBUG_MINOR("vc_hostfs_get_attr: '%s'", path );
632 
633     *attr = 0;
634 
635     if ( stat( path, &sb ) == 0 )
636     {
637         if ( S_ISDIR( sb.st_mode ))
638         {
639             *attr |= ATTR_DIRENT;
640         }
641 
642         if (( sb.st_mode & S_IWUSR  ) == 0 )
643         {
644             *attr |= ATTR_RDONLY;
645         }
646 
647         return 0;
648     }
649     return -1;
650 }
651 
652 /******************************************************************************
653 NAME
654    vc_hostfs_mkdir
655 
656 SYNOPSIS
657    int vc_hostfs_mkdir(const char *path)
658 
659 FUNCTION
660    Creates a new directory named by the pathname pointed to by path.
661 
662 RETURNS
663    Successful completion: 0
664    Otherwise: -1
665 ******************************************************************************/
666 
vc_hostfs_mkdir(const char * path)667 int vc_hostfs_mkdir(const char *path)
668 {
669     DEBUG_MINOR( "vc_hostfs_mkdir: '%s'",  path );
670     if ( mkdir( path, 0777 ) == 0 )
671     {
672         return 0;
673     }
674     return -1;
675 }
676 
677 /******************************************************************************
678 NAME
679    vc_hostfs_opendir
680 
681 SYNOPSIS
682    void *vc_hostfs_opendir(const char *dirname)
683 
684 FUNCTION
685    Starts a directory list iteration of sub-directories.
686 
687 RETURNS
688    Successful completion: dhandle (pointer)
689    Otherwise: NULL
690 ******************************************************************************/
691 
vc_hostfs_opendir(const char * dirname)692 void *vc_hostfs_opendir(const char *dirname)
693 {
694    struct fs_dir *fsdir = NULL;
695 
696    DEBUG_MINOR( "vc_hostfs_opendir: '%s'", dirname );
697 
698    if (dirname && dirname[0])
699    {
700       fsdir = (struct fs_dir *)malloc(sizeof(struct fs_dir));
701 
702       if (fsdir)
703       {
704          DIR *dhandle;
705          int len = strlen(dirname);
706 
707          memcpy(fsdir->pathbuf, dirname, len);
708 
709          backslash_to_slash(fsdir->pathbuf);
710 
711          /* Remove any trailing slashes */
712          while (fsdir->pathbuf[len - 1] == '/')
713             len--;
714 
715          fsdir->pathbuf[len] = '\0';
716 
717          dhandle = opendir(fsdir->pathbuf);
718          DEBUG_MINOR( "opendir: '%s' = %p", fsdir->pathbuf, dhandle );
719 
720          if (dhandle)
721          {
722             fsdir->pathlen = len;
723             fsdir->dhandle = dhandle;
724          }
725          else
726          {
727             free(fsdir);
728             fsdir = NULL;
729          }
730       }
731    }
732 
733    return fsdir;
734 }
735 
736 /******************************************************************************
737 NAME
738    vc_hostfs_readdir_r
739 
740 SYNOPSIS
741    struct dirent *vc_hostfs_readdir_r(void *dhandle, struct dirent *result)
742 
743 FUNCTION
744    Fills in the passed result structure with details of the directory entry
745    at the current psition in the directory stream specified by the argument
746    dhandle, and positions the directory stream at the next entry. If the last
747    sub-directory has been reached it ends the iteration and begins a new one
748    for files in the directory.
749 
750 RETURNS
751    Successful completion: result
752    End of directory stream: NULL
753 ******************************************************************************/
754 
vc_hostfs_readdir_r(void * dhandle,struct fs_dirent * result)755 struct fs_dirent *vc_hostfs_readdir_r(void *dhandle, struct fs_dirent *result)
756 {
757    struct fs_dir *fsdir = (struct fs_dir *)dhandle;
758 
759    DEBUG_MINOR( "vc_hostfs_readdir_r(%p)", fsdir );
760 
761    if (fsdir && result)
762    {
763       struct dirent *dent;
764 
765       while ((dent = readdir(fsdir->dhandle)) != NULL)
766       {
767          struct stat statbuf;
768          int ret;
769 
770          /* Append the filename, and stat the resulting path */
771          fsdir->pathbuf[fsdir->pathlen] = '/';
772          vcos_safe_strcpy(fsdir->pathbuf, dent->d_name, sizeof(fsdir->pathbuf), fsdir->pathlen + 1);
773          ret = stat(fsdir->pathbuf, &statbuf);
774          fsdir->pathbuf[fsdir->pathlen] = '\0';
775 
776          if (ret == 0)
777          {
778             vcos_safe_strcpy(result->d_name, dent->d_name, sizeof(result->d_name), 0);
779             result->d_size = (statbuf.st_size <= 0xffffffff) ? (unsigned int)statbuf.st_size : 0xffffffff;
780             result->d_attrib = ATTR_NORMAL;
781             if ((statbuf.st_mode & S_IWUSR) == 0)
782                result->d_attrib |= ATTR_RDONLY;
783             if (statbuf.st_mode & S_IFDIR)
784                result->d_attrib |= ATTR_DIRENT;
785             result->d_creatime = statbuf.st_ctime;
786             result->d_modtime = statbuf.st_mtime;
787             DEBUG_MINOR( "vc_hostfs_readdir_r() = '%s', %x, %x", result->d_name, result->d_size, result->d_attrib );
788             break;
789          }
790       }
791 
792       if (!dent)
793       {
794          DEBUG_MINOR( "vc_hostfs_readdir_r() = NULL" );
795          rewinddir(fsdir->dhandle);
796          result = NULL;
797       }
798    }
799    else
800    {
801       result = NULL;
802    }
803 
804    return result;
805 }
806 
807 /******************************************************************************
808 NAME
809    vc_hostfs_remove
810 
811 SYNOPSIS
812    int vc_hostfs_remove(const char *path)
813 
814 FUNCTION
815    Removes a file or a directory. A directory must be empty before it can be
816    deleted.
817 
818 RETURNS
819    Successful completion: 0
820    Otherwise: -1
821 ******************************************************************************/
822 
vc_hostfs_remove(const char * path)823 int vc_hostfs_remove(const char *path)
824 {
825     char *pathbuf = strdup(path);
826     int ret = -1;
827 
828     DEBUG_MINOR( "vc_hostfs_remove: '%s'", path );
829 
830     if (pathbuf)
831     {
832        backslash_to_slash(pathbuf);
833 
834        if ( unlink( pathbuf ) == 0 )
835           ret = 0;
836     }
837 
838     free(pathbuf);
839 
840     return ret;
841 }
842 
843 /******************************************************************************
844 NAME
845    vc_hostfs_rename
846 
847 SYNOPSIS
848    int vc_hostfs_rename(const char *old, const char *new)
849 
850 FUNCTION
851    Changes the name of a file. The old and new pathnames must be on the same
852    physical file system.
853 
854 RETURNS
855    Successful completion: 0
856    Otherwise: -1
857 ******************************************************************************/
858 
vc_hostfs_rename(const char * old,const char * new)859 int vc_hostfs_rename(const char *old, const char *new)
860 {
861     char *oldbuf = strdup(old);
862     char *newbuf = strdup(new);
863     int ret = -1;
864 
865     DEBUG_MINOR( "vc_hostfs_rename: '%s' to '%s'", old, new );
866 
867     if (oldbuf && newbuf)
868     {
869        backslash_to_slash(oldbuf);
870        backslash_to_slash(newbuf);
871 
872        if ( rename( oldbuf, newbuf ) == 0 )
873           ret = 0;
874     }
875 
876     if (oldbuf)
877        free(oldbuf);
878 
879     if (newbuf)
880        free(newbuf);
881 
882     return ret;
883 }
884 
885 /******************************************************************************
886 NAME
887    vc_hostfs_set_attr
888 
889 SYNOPSIS
890    int vc_hostfs_set_attr(const char *path, fattributes_t attr)
891 
892 FUNCTION
893    Sets file/directory attributes.
894 
895 RETURNS
896    Successful completion: 0
897    Otherwise: -1
898 ******************************************************************************/
899 
vc_hostfs_set_attr(const char * path,fattributes_t attr)900 int vc_hostfs_set_attr(const char *path, fattributes_t attr)
901 {
902    char *pathbuf = strdup(path);
903    int ret = -1;
904 
905    DEBUG_MINOR( "vc_hostfs_set_attr: '%s', %x", path, attr );
906 
907    if (pathbuf)
908    {
909       mode_t mode = 0;
910       struct stat sb;
911 
912       backslash_to_slash(pathbuf);
913 
914       if ( stat( path, &sb ) == 0 )
915       {
916          mode = sb.st_mode;
917 
918          if ( attr & ATTR_RDONLY )
919          {
920             mode &= ~S_IWUSR;
921          }
922          else
923          {
924             mode |= S_IWUSR;
925          }
926 
927          /* coverity[toctou] Not doing anything security-relevant here,
928           * so the race condition is harmless */
929          if ( chmod( path, mode ) == 0 )
930             ret = 0;
931       }
932    }
933 
934    if (pathbuf)
935       free(pathbuf);
936 
937    return ret;
938 }
939 
940 /******************************************************************************
941 NAME
942    vc_hostfs_setend
943 
944 SYNOPSIS
945    int vc_hostfs_setend(int fildes)
946 
947 FUNCTION
948    Truncates file at current position.
949 
950 RETURNS
951    Successful completion: 0
952    Otherwise: -1
953 ******************************************************************************/
954 
vc_hostfs_setend(int filedes)955 int vc_hostfs_setend(int filedes)
956 {
957     off_t   currPosn;
958 
959     if (( currPosn = lseek( filedes, 0, SEEK_CUR )) != (off_t)-1 )
960     {
961         if ( ftruncate( filedes, currPosn ) == 0 )
962         {
963             return 0;
964         }
965     }
966    return -1;
967 }
968 
969 /******************************************************************************
970 NAME
971    vc_hostfs_totalspace64
972 
973 SYNOPSIS
974    int64_t vc_hostfs_totalspace64(const char *path)
975 
976 FUNCTION
977    Returns the total amount of space on the physical file system that contains
978    path.
979 
980 RETURNS
981    Successful completion: total space
982    Otherwise: -1
983 ******************************************************************************/
984 
vc_hostfs_totalspace64(const char * inPath)985 int64_t vc_hostfs_totalspace64(const char *inPath)
986 {
987    char *path = strdup( inPath );
988    int64_t ret = -1;
989    struct statfs fsStat;
990 
991    // Replace all '\' with '/'
992    if (path)
993    {
994       backslash_to_slash( path );
995 
996       ret = statfs( path, &fsStat );
997 
998       if (ret == 0)
999       {
1000          ret = fsStat.f_bsize * fsStat.f_blocks;
1001       }
1002       else
1003       {
1004          ret = -1;
1005       }
1006    }
1007 
1008    DEBUG_MINOR( "vc_hostfs_totalspace for '%s' returning %" PRId64 "", path, ret );
1009 
1010    if (path)
1011       free( path );
1012    return ret;
1013 }
1014 
1015 /******************************************************************************
1016 NAME
1017    vc_hostfs_totalspace
1018 
1019 SYNOPSIS
1020    int vc_hostfs_totalspace(const char *path)
1021 
1022 FUNCTION
1023    Returns the total amount of space on the physical file system that contains
1024    path.
1025 
1026 RETURNS
1027    Successful completion: total space
1028    Otherwise: -1
1029 ******************************************************************************/
1030 
vc_hostfs_totalspace(const char * inPath)1031 int vc_hostfs_totalspace(const char *inPath)
1032 {
1033    int ret;
1034    int64_t totalSpace = vc_hostfs_totalspace64(inPath);
1035 
1036    // Saturate return value (need this in case we have a large file system)
1037    if (totalSpace > (int64_t) INT_MAX)
1038    {
1039       ret = INT_MAX;
1040    }
1041    else
1042    {
1043       ret = (int) totalSpace;
1044    }
1045    return ret;
1046 }
1047 
1048 /******************************************************************************
1049 NAME
1050    backslash_to_slash
1051 
1052 SYNOPSIS
1053    void backslash_to_slash( char *s )
1054 
1055 FUNCTION
1056    Convert all '\' in a string to '/'.
1057 
1058 RETURNS
1059    None.
1060 ******************************************************************************/
1061 
backslash_to_slash(char * s)1062 static void backslash_to_slash( char *s )
1063 {
1064    while ( *s != '\0' )
1065    {
1066        if ( *s == '\\' )
1067        {
1068            *s = '/';
1069        }
1070        s++;
1071    }
1072 }
1073 
1074 /******************************************************************************
1075 NAME
1076    vc_hostfs_scandisk
1077 
1078 SYNOPSIS
1079    void vc_hostfs_scandisk(const char *path)
1080 
1081 FUNCTION
1082    Invalidates any cluster chains in the FAT that are not referenced
1083    in any directory structures
1084 
1085 RETURNS
1086    Void
1087 ******************************************************************************/
1088 
vc_hostfs_scandisk(const char * path)1089 void vc_hostfs_scandisk(const char *path)
1090 {
1091    (void)path;
1092 
1093    // not yet implemented
1094 }
1095 
1096 /******************************************************************************
1097 NAME
1098    vc_hostfs_chkdsk
1099 
1100 SYNOPSIS
1101    int vc_hostfs_chkdsk(const char *path, int fix_errors)
1102 
1103 FUNCTION
1104    Checks whether or not a FAT filesystem is corrupt or not. If fix_errors
1105    is TRUE behaves exactly as vc_filesys_scandisk.
1106 
1107 RETURNS
1108    Successful completion: 0
1109    Otherwise: indicates failure
1110 ******************************************************************************/
1111 
vc_hostfs_chkdsk(const char * path,int fix_errors)1112 int vc_hostfs_chkdsk(const char *path, int fix_errors)
1113 {
1114    (void)path;
1115    (void)fix_errors;
1116    return 0;
1117 }
1118