Initial version of libomxil-vc4 for RPI3
[platform/adaptation/broadcom/libomxil-vc4.git] / interface / vmcs_host / linux / vcfilesys.c
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
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
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
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
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 /******************************************************************************
226 NAME
227    vc_hostfs_lseek64
228
229 SYNOPSIS
230    int64_t vc_hostfs_lseek64(int fildes, int64_t offset, int whence)
231
232 FUNCTION
233    Sets the file pointer associated with the open file specified by fildes.  If
234    the file is a FIFO (Linux does not support seeking on a FIFO) then, for the
235    benefit of the Videocore streaming file handlers which do a number of null seeks,
236    that is, seeks to the current position, the return value is faked without an
237    actual seek being done.
238
239 RETURNS
240    Successful completion: offset
241    Otherwise: -1
242 ******************************************************************************/
243
244 int64_t vc_hostfs_lseek64(int fildes, int64_t offset, int whence)
245 {
246    DEBUG_MINOR("vc_hostfs_lseek(%d,%" PRId64 ",%d)", fildes, offset, whence);
247    if (fildes >= file_info_table_len)
248    {
249       // File descriptor not in table, so this is an error
250       DEBUG_MAJOR("vc_hostfs_lseek: invalid fildes %d", fildes);
251       return -1;
252    }
253    else
254    {
255       // There is entry in the file info table for this file descriptor, so go
256       // ahead and handle the seek
257       int64_t read_offset = p_file_info_table[fildes].read_offset;
258
259       if (p_file_info_table[fildes].is_fifo)
260       {
261          // The Videocore is attempting to seek on a FIFO.  FIFOs don't support seeking
262          // but, for the benefit of certain Videocore "streaming" file handlers, we
263          // will fake limited FIFO seek functionality by computing where a seek
264          // would take us to
265          if (whence == SEEK_SET)
266          {
267             read_offset = offset;
268          }
269          else if (whence == SEEK_CUR)
270          {
271             read_offset += offset;
272          }
273          else
274          {
275             // seeking to the end of FIFO makes no sense, so this is an error
276             DEBUG_MAJOR("vc_hostfs_lseek(%d,%lld,%d): SEEK_END not supported on FIFO", fildes, (long long)offset, whence);
277             return -1;
278          }
279       }
280       else
281       {
282          // File is not a FIFO, so do the seek
283          read_offset = lseek64(fildes, offset, whence);
284       }
285       p_file_info_table[fildes].read_offset = read_offset;
286       DEBUG_MINOR("vc_hostfs_lseek returning %" PRId64 ")", read_offset);
287       return read_offset;
288    }
289 }
290
291
292
293
294 /******************************************************************************
295 NAME
296    vc_hostfs_open
297
298 SYNOPSIS
299    int vc_hostfs_open(const char *path, int vc_oflag)
300
301 FUNCTION
302    Establishes a connection between a file and a file descriptor.  For the benefit
303    of faking out seeks on a FIFO, we will need to keep track of the read offset for
304    all reads, and to facilitate this each opened file is given an entry in a local
305    file info table.
306
307 RETURNS
308    Successful completion: file descriptor
309    Otherwise: -1
310 ******************************************************************************/
311
312 int vc_hostfs_open(const char *inPath, int vc_oflag)
313 {
314    char *path = strdup( inPath );
315    //char *s;
316    int flags = 0, ret=errno;
317    struct stat fileStat;
318
319    // Replace all '\' with '/'
320    backslash_to_slash( path );
321
322 #if 0
323    s = path + strlen( path );
324    if (( s - path ) >= 4 )
325    {
326       if ( strcasecmp( &s[ -4 ], ".vll" ) == 0 )
327       {
328          // The Videocore is asking for a .vll file. Since it isn't consistent with
329          // the case, we convert .vll files to all lowercase.
330           "vc_hostfs_open: '%s'", path ;
331
332          s--;    // backup to the last character (*s is on the '\0')
333          while (( s >= path ) && ( *s != '/' ))
334          {
335             *s = tolower( *s );
336             s--;
337          }
338       }
339    }
340 #endif
341    DEBUG_MINOR("vc_hostfs_open: '%s'", path);
342
343    flags = O_RDONLY;
344    if (vc_oflag & VC_O_WRONLY)  flags =  O_WRONLY;
345    if (vc_oflag & VC_O_RDWR)    flags =  O_RDWR;
346    if (vc_oflag & VC_O_APPEND)  flags |= O_APPEND;
347    if (vc_oflag & VC_O_CREAT)   flags |= O_CREAT;
348    if (vc_oflag & VC_O_TRUNC)   flags |= O_TRUNC;
349    if (vc_oflag & VC_O_EXCL)    flags |= O_EXCL;
350
351    //while (*path == '\\') path++; // do not want initial '\'
352    if (flags & O_CREAT)
353       ret = open(path, flags, S_IRUSR | S_IWUSR );
354    else
355       ret = open(path, flags );
356
357    if (ret < 0 )
358    {
359       DEBUG_MINOR("vc_hostfs_open(%s,%d) = %d", path, vc_oflag, ret);
360    }
361    else
362    {
363       DEBUG_MINOR("vc_hostfs_open(%s,%d) = %d", path, vc_oflag, ret);
364    }
365
366    // If the file was successfully open then initialize its entry in
367    // the file info table.  If necessary, we expand the size of the table
368    if (ret >= 0)
369    {
370       // File was successfully opened
371       if (ret >= file_info_table_len)
372       {
373          file_info_t *p_new_file_info_table = p_file_info_table;
374          int new_file_info_table_len = file_info_table_len;
375
376          // try and allocate a bigger buffer for the file info table
377          new_file_info_table_len += FILE_INFO_TABLE_CHUNK_LEN;
378          p_new_file_info_table = calloc( (size_t)new_file_info_table_len, sizeof( file_info_t ) );
379          if (p_new_file_info_table == NULL)
380          {
381             // calloc failed
382             DEBUG_MAJOR("vc_hostfs_open: file_info_table calloc failed");
383             assert( 0 );
384          }
385          else
386          {
387             // calloc successful, so copy data from previous buffer to new buffer,
388             // free previous buffer and update ptr and len info
389             memcpy( p_new_file_info_table, p_file_info_table, sizeof( file_info_t ) * file_info_table_len );
390             free( p_file_info_table );
391             p_file_info_table = p_new_file_info_table;
392             file_info_table_len = new_file_info_table_len;
393          }
394       }
395       assert( ret < file_info_table_len );
396       {
397          // initialize this file's entry in the file info table
398          p_file_info_table[ret].is_fifo = 0;
399          p_file_info_table[ret].read_offset = 0;
400       }
401
402       // Check whether the file is a FIFO.  A FIFO does not support seeking
403       // but we will fake, to the extent supported by the buffered file system
404       // on the Videocore, limited FIFO seek functionality.  This is for the benefit
405       // of certain Videocore "streaming" file handlers.
406       if (fstat( ret, &fileStat ) != 0)
407       {
408          DEBUG_MINOR("vc_hostfs_open: fstat failed: %s", strerror(errno));
409       }
410       else if (S_ISFIFO( fileStat.st_mode ))
411       {
412          // file is a FIFO, so note its fildes for future reference
413          p_file_info_table[ret].is_fifo = 1;
414          DEBUG_MINOR("vc_hostfs_open: file with fildes %d is a FIFO", ret);
415       }
416    }
417
418    free( path );
419
420    return ret;
421 }
422
423 /******************************************************************************
424 NAME
425    vc_hostfs_read
426
427 SYNOPSIS
428    int vc_hostfs_read(int fildes, void *buf, unsigned int nbyte)
429
430 FUNCTION
431    Attempts to read nbyte bytes from the file associated with the file
432    descriptor, fildes, into the buffer pointed to by buf.  For the benefit
433    of faking out seeks on a FIFO, we keep track of the read offset for all
434    reads.
435
436 RETURNS
437    Successful completion: number of bytes read
438    Otherwise: -1
439 ******************************************************************************/
440
441 int vc_hostfs_read(int fildes, void *buf, unsigned int nbyte)
442 {
443    if (fildes >= file_info_table_len)
444    {
445       // File descriptor not in table, so this is an error
446       DEBUG_MAJOR("vc_hostfs_read(%d,%p,%u): invalid fildes", fildes, buf, nbyte);
447       return -1;
448    }
449    else
450    {
451       // There is entry in the file info table for this file descriptor, so go
452       // ahead and handle the read
453       int ret = (int) read(fildes, buf, nbyte);
454       DEBUG_MINOR("vc_hostfs_read(%d,%p,%u) = %d", fildes, buf, nbyte, ret);
455       if (ret > 0)
456       {
457          p_file_info_table[fildes].read_offset += (long) ret;
458       }
459       return ret;
460    }
461 }
462
463 /******************************************************************************
464 NAME
465    vc_hostfs_write
466
467 SYNOPSIS
468    int vc_hostfs_write(int fildes, const void *buf, unsigned int nbyte)
469
470 FUNCTION
471    Attempts to write nbyte bytes from the buffer pointed to by buf to file
472    associated with the file descriptor, fildes.
473
474 RETURNS
475    Successful completion: number of bytes written
476    Otherwise: -1
477 ******************************************************************************/
478
479 int vc_hostfs_write(int fildes, const void *buf, unsigned int nbyte)
480 {
481    int ret = (int) write(fildes, buf, nbyte);
482    DEBUG_MINOR("vc_hostfs_write(%d,%p,%u) = %d", fildes, buf, nbyte, ret);
483    return ret;
484 }
485
486 /******************************************************************************
487 NAME
488    vc_hostfs_closedir
489
490 SYNOPSIS
491    int vc_hostfs_closedir(void *dhandle)
492
493 FUNCTION
494    Ends a directory list iteration.
495
496 RETURNS
497    Successful completion: 0
498    Otherwise: -1
499 ******************************************************************************/
500
501 int vc_hostfs_closedir(void *dhandle)
502 {
503    struct fs_dir *fsdir = (struct fs_dir *)dhandle;
504    int ret = -1;
505
506    DEBUG_MINOR( "vc_hostfs_closedir(%p)", dhandle );
507
508    if (dhandle && fsdir->dhandle)
509    {
510       (void)closedir(fsdir->dhandle);
511       fsdir->dhandle = NULL;
512       free(fsdir);
513       ret = 0;
514    }
515
516    return ret;
517 }
518
519 /******************************************************************************
520 NAME
521    vc_hostfs_format
522
523 SYNOPSIS
524    int vc_hostfs_format(const char *path)
525
526 FUNCTION
527    Formats the physical file system that contains path.
528
529 RETURNS
530    Successful completion: 0
531    Otherwise: -1
532 ******************************************************************************/
533
534 int vc_hostfs_format(const char *path)
535 {
536    DEBUG_MINOR("vc_hostfs_format: '%s' not implemented", path);
537    return -1;
538 }
539
540 /******************************************************************************
541 NAME
542    vc_hostfs_freespace
543
544 SYNOPSIS
545    int vc_hostfs_freespace(const char *path)
546
547 FUNCTION
548    Returns the amount of free space on the physical file system that contains
549    path.
550
551 RETURNS
552    Successful completion: free space
553    Otherwise: -1
554 ******************************************************************************/
555
556 int vc_hostfs_freespace(const char *inPath)
557 {
558    int ret;
559
560    int64_t freeSpace =  vc_hostfs_freespace64( inPath );
561
562    // Saturate return value (need this in case we have a large file system)
563    if (freeSpace > (int64_t) INT_MAX)
564    {
565       ret = INT_MAX;
566    }
567    else
568    {
569       ret = (int) freeSpace;
570    }
571
572    return ret;
573 }
574
575
576
577
578
579 /******************************************************************************
580 NAME
581    vc_hostfs_freespace
582
583 SYNOPSIS
584    int vc_hostfs_freespace(const char *path)
585
586 FUNCTION
587    Returns the amount of free space on the physical file system that contains
588    path.
589
590 RETURNS
591    Successful completion: free space
592    Otherwise: -1
593 ******************************************************************************/
594 int64_t vc_hostfs_freespace64(const char *inPath)
595 {
596    char *path = strdup( inPath );
597    int64_t ret;
598    struct statfs fsStat;
599
600    // Replace all '\' with '/'
601    backslash_to_slash( path );
602
603    ret = (int64_t) statfs( path, &fsStat );
604
605    if (ret == 0)
606    {
607       ret = fsStat.f_bsize * fsStat.f_bavail;
608    }
609    else
610    {
611       ret = -1;
612    }
613
614    DEBUG_MINOR( "vc_hostfs_freespace64 for '%s' returning %" PRId64 "", path, ret );
615
616    free( path );
617    return ret;
618 }
619
620
621 /******************************************************************************
622 NAME
623    vc_hostfs_get_attr
624
625 SYNOPSIS
626    int vc_hostfs_get_attr(const char *path, fattributes_t *attr)
627
628 FUNCTION
629    Gets the file/directory attributes.
630
631 RETURNS
632    Successful completion: 0
633    Otherwise: -1
634 ******************************************************************************/
635
636 int vc_hostfs_get_attr(const char *path, fattributes_t *attr)
637 {
638     struct stat sb;
639
640     DEBUG_MINOR("vc_hostfs_get_attr: '%s'", path );
641
642
643     *attr = 0;
644
645     if ( stat( path, &sb ) == 0 )
646     {
647         if ( S_ISDIR( sb.st_mode ))
648         {
649             *attr |= ATTR_DIRENT;
650         }
651
652         if (( sb.st_mode & S_IWUSR  ) == 0 )
653         {
654             *attr |= ATTR_RDONLY;
655         }
656
657         return 0;
658     }
659     return -1;
660 }
661
662 /******************************************************************************
663 NAME
664    vc_hostfs_mkdir
665
666 SYNOPSIS
667    int vc_hostfs_mkdir(const char *path)
668
669 FUNCTION
670    Creates a new directory named by the pathname pointed to by path.
671
672 RETURNS
673    Successful completion: 0
674    Otherwise: -1
675 ******************************************************************************/
676
677 int vc_hostfs_mkdir(const char *path)
678 {
679     DEBUG_MINOR( "vc_hostfs_mkdir: '%s'",  path );
680     if ( mkdir( path, 0777 ) == 0 )
681     {
682         return 0;
683     }
684     return -1;
685 }
686
687 /******************************************************************************
688 NAME
689    vc_hostfs_opendir
690
691 SYNOPSIS
692    void *vc_hostfs_opendir(const char *dirname)
693
694 FUNCTION
695    Starts a directory list iteration of sub-directories.
696
697 RETURNS
698    Successful completion: dhandle (pointer)
699    Otherwise: NULL
700 ******************************************************************************/
701
702 void *vc_hostfs_opendir(const char *dirname)
703 {
704    struct fs_dir *fsdir = NULL;
705
706    DEBUG_MINOR( "vc_hostfs_opendir: '%s'", dirname );
707
708    if (dirname && dirname[0])
709    {
710       fsdir = (struct fs_dir *)malloc(sizeof(struct fs_dir));
711
712       if (fsdir)
713       {
714          DIR *dhandle;
715          int len = strlen(dirname);
716
717          memcpy(fsdir->pathbuf, dirname, len);
718
719          backslash_to_slash(fsdir->pathbuf);
720
721          /* Remove any trailing slashes */
722          while (fsdir->pathbuf[len - 1] == '/')
723             len--;
724
725          fsdir->pathbuf[len] = '\0';
726
727          dhandle = opendir(fsdir->pathbuf);
728          DEBUG_MINOR( "opendir: '%s' = %p", fsdir->pathbuf, dhandle );
729
730          if (dhandle)
731          {
732             fsdir->pathlen = len;
733             fsdir->dhandle = dhandle;
734          }
735          else
736          {
737             free(fsdir);
738             fsdir = NULL;
739          }
740       }
741    }
742
743    return fsdir;
744 }
745
746 /******************************************************************************
747 NAME
748    vc_hostfs_readdir_r
749
750 SYNOPSIS
751    struct dirent *vc_hostfs_readdir_r(void *dhandle, struct dirent *result)
752
753 FUNCTION
754    Fills in the passed result structure with details of the directory entry
755    at the current psition in the directory stream specified by the argument
756    dhandle, and positions the directory stream at the next entry. If the last
757    sub-directory has been reached it ends the iteration and begins a new one
758    for files in the directory.
759
760 RETURNS
761    Successful completion: result
762    End of directory stream: NULL
763 ******************************************************************************/
764
765 struct fs_dirent *vc_hostfs_readdir_r(void *dhandle, struct fs_dirent *result)
766 {
767    struct fs_dir *fsdir = (struct fs_dir *)dhandle;
768
769    DEBUG_MINOR( "vc_hostfs_readdir_r(%p)", fsdir );
770
771    if (fsdir && result)
772    {
773       struct dirent *dent;
774
775       while ((dent = readdir(fsdir->dhandle)) != NULL)
776       {
777          struct stat statbuf;
778          int ret;
779
780          /* Append the filename, and stat the resulting path */
781          fsdir->pathbuf[fsdir->pathlen] = '/';
782          vcos_safe_strcpy(fsdir->pathbuf, dent->d_name, sizeof(fsdir->pathbuf), fsdir->pathlen + 1);
783          ret = stat(fsdir->pathbuf, &statbuf);
784          fsdir->pathbuf[fsdir->pathlen] = '\0';
785
786          if (ret == 0)
787          {
788             vcos_safe_strcpy(result->d_name, dent->d_name, sizeof(result->d_name), 0);
789             result->d_size = (statbuf.st_size <= 0xffffffff) ? (unsigned int)statbuf.st_size : 0xffffffff;
790             result->d_attrib = ATTR_NORMAL;
791             if ((statbuf.st_mode & S_IWUSR) == 0)
792                result->d_attrib |= ATTR_RDONLY;
793             if (statbuf.st_mode & S_IFDIR)
794                result->d_attrib |= ATTR_DIRENT;
795             result->d_creatime = statbuf.st_ctime;
796             result->d_modtime = statbuf.st_mtime;
797             DEBUG_MINOR( "vc_hostfs_readdir_r() = '%s', %x, %x", result->d_name, result->d_size, result->d_attrib );
798             break;
799          }
800       }
801
802       if (!dent)
803       {
804          DEBUG_MINOR( "vc_hostfs_readdir_r() = NULL" );
805          rewinddir(fsdir->dhandle);
806          result = NULL;
807       }
808    }
809    else
810    {
811       result = NULL;
812    }
813
814    return result;
815 }
816
817 /******************************************************************************
818 NAME
819    vc_hostfs_remove
820
821 SYNOPSIS
822    int vc_hostfs_remove(const char *path)
823
824 FUNCTION
825    Removes a file or a directory. A directory must be empty before it can be
826    deleted.
827
828 RETURNS
829    Successful completion: 0
830    Otherwise: -1
831 ******************************************************************************/
832
833 int vc_hostfs_remove(const char *path)
834 {
835     char *pathbuf = strdup(path);
836     int ret = -1;
837
838     DEBUG_MINOR( "vc_hostfs_remove: '%s'", path );
839
840     if (pathbuf)
841     {
842        backslash_to_slash(pathbuf);
843
844        if ( unlink( pathbuf ) == 0 )
845           ret = 0;
846     }
847
848     free(pathbuf);
849
850     return ret;
851 }
852
853 /******************************************************************************
854 NAME
855    vc_hostfs_rename
856
857 SYNOPSIS
858    int vc_hostfs_rename(const char *old, const char *new)
859
860 FUNCTION
861    Changes the name of a file. The old and new pathnames must be on the same
862    physical file system.
863
864 RETURNS
865    Successful completion: 0
866    Otherwise: -1
867 ******************************************************************************/
868
869 int vc_hostfs_rename(const char *old, const char *new)
870 {
871     char *oldbuf = strdup(old);
872     char *newbuf = strdup(new);
873     int ret = -1;
874
875     DEBUG_MINOR( "vc_hostfs_rename: '%s' to '%s'", old, new );
876
877     if (oldbuf && newbuf)
878     {
879        backslash_to_slash(oldbuf);
880        backslash_to_slash(newbuf);
881
882        if ( rename( oldbuf, newbuf ) == 0 )
883           ret = 0;
884     }
885
886     if (oldbuf)
887        free(oldbuf);
888
889     if (newbuf)
890        free(newbuf);
891
892     return ret;
893 }
894
895 /******************************************************************************
896 NAME
897    vc_hostfs_set_attr
898
899 SYNOPSIS
900    int vc_hostfs_set_attr(const char *path, fattributes_t attr)
901
902 FUNCTION
903    Sets file/directory attributes.
904
905 RETURNS
906    Successful completion: 0
907    Otherwise: -1
908 ******************************************************************************/
909
910 int vc_hostfs_set_attr(const char *path, fattributes_t attr)
911 {
912    char *pathbuf = strdup(path);
913    int ret = -1;
914
915    DEBUG_MINOR( "vc_hostfs_set_attr: '%s', %x", path, attr );
916
917    if (pathbuf)
918    {
919       mode_t mode = 0;
920       struct stat sb;
921
922       backslash_to_slash(pathbuf);
923
924       if ( stat( path, &sb ) == 0 )
925       {
926          mode = sb.st_mode;
927
928          if ( attr & ATTR_RDONLY )
929          {
930             mode &= ~S_IWUSR;
931          }
932          else
933          {
934             mode |= S_IWUSR;
935          }
936
937          /* coverity[toctou] Not doing anything security-relevant here,
938           * so the race condition is harmless */
939          if ( chmod( path, mode ) == 0 )
940             ret = 0;
941       }
942    }
943
944    if (pathbuf)
945       free(pathbuf);
946
947    return ret;
948 }
949
950 /******************************************************************************
951 NAME
952    vc_hostfs_setend
953
954 SYNOPSIS
955    int vc_hostfs_setend(int fildes)
956
957 FUNCTION
958    Truncates file at current position.
959
960 RETURNS
961    Successful completion: 0
962    Otherwise: -1
963 ******************************************************************************/
964
965 int vc_hostfs_setend(int filedes)
966 {
967     off_t   currPosn;
968
969     if (( currPosn = lseek( filedes, 0, SEEK_CUR )) != (off_t)-1 )
970     {
971         if ( ftruncate( filedes, currPosn ) == 0 )
972         {
973             return 0;
974         }
975     }
976    return -1;
977 }
978
979
980 /******************************************************************************
981 NAME
982    vc_hostfs_totalspace64
983
984 SYNOPSIS
985    int64_t vc_hostfs_totalspace64(const char *path)
986
987 FUNCTION
988    Returns the total amount of space on the physical file system that contains
989    path.
990
991 RETURNS
992    Successful completion: total space
993    Otherwise: -1
994 ******************************************************************************/
995
996 int64_t vc_hostfs_totalspace64(const char *inPath)
997 {
998    char *path = strdup( inPath );
999    int64_t ret = -1;
1000    struct statfs fsStat;
1001
1002    // Replace all '\' with '/'
1003    if (path)
1004    {
1005       backslash_to_slash( path );
1006
1007       ret = statfs( path, &fsStat );
1008
1009       if (ret == 0)
1010       {
1011          ret = fsStat.f_bsize * fsStat.f_blocks;
1012       }
1013       else
1014       {
1015          ret = -1;
1016       }
1017    }
1018
1019    DEBUG_MINOR( "vc_hostfs_totalspace for '%s' returning %" PRId64 "", path, ret );
1020
1021    if (path)
1022       free( path );
1023    return ret;
1024 }
1025
1026
1027 /******************************************************************************
1028 NAME
1029    vc_hostfs_totalspace
1030
1031 SYNOPSIS
1032    int vc_hostfs_totalspace(const char *path)
1033
1034 FUNCTION
1035    Returns the total amount of space on the physical file system that contains
1036    path.
1037
1038 RETURNS
1039    Successful completion: total space
1040    Otherwise: -1
1041 ******************************************************************************/
1042
1043 int vc_hostfs_totalspace(const char *inPath)
1044 {
1045    int ret;
1046    int64_t totalSpace = vc_hostfs_totalspace64(inPath);
1047
1048    // Saturate return value (need this in case we have a large file system)
1049    if (totalSpace > (int64_t) INT_MAX)
1050    {
1051       ret = INT_MAX;
1052    }
1053    else
1054    {
1055       ret = (int) totalSpace;
1056    }
1057    return ret;
1058 }
1059
1060 /******************************************************************************
1061 NAME
1062    backslash_to_slash
1063
1064 SYNOPSIS
1065    void backslash_to_slash( char *s )
1066
1067 FUNCTION
1068    Convert all '\' in a string to '/'.
1069
1070 RETURNS
1071    None.
1072 ******************************************************************************/
1073
1074 static void backslash_to_slash( char *s )
1075 {
1076    while ( *s != '\0' )
1077    {
1078        if ( *s == '\\' )
1079        {
1080            *s = '/';
1081        }
1082        s++;
1083    }
1084 }
1085
1086 /******************************************************************************
1087 NAME
1088    vc_hostfs_scandisk
1089
1090 SYNOPSIS
1091    void vc_hostfs_scandisk(const char *path)
1092
1093 FUNCTION
1094    Invalidates any cluster chains in the FAT that are not referenced
1095    in any directory structures
1096
1097 RETURNS
1098    Void
1099 ******************************************************************************/
1100
1101 void vc_hostfs_scandisk(const char *path)
1102 {
1103    (void)path;
1104
1105    // not yet implemented
1106 }
1107
1108
1109 /******************************************************************************
1110 NAME
1111    vc_hostfs_chkdsk
1112
1113 SYNOPSIS
1114    int vc_hostfs_chkdsk(const char *path, int fix_errors)
1115
1116 FUNCTION
1117    Checks whether or not a FAT filesystem is corrupt or not. If fix_errors
1118    is TRUE behaves exactly as vc_filesys_scandisk.
1119
1120 RETURNS
1121    Successful completion: 0
1122    Otherwise: indicates failure
1123 ******************************************************************************/
1124
1125 int vc_hostfs_chkdsk(const char *path, int fix_errors)
1126 {
1127    (void)path;
1128    (void)fix_errors;
1129    return 0;
1130 }
1131