1/*
2Copyright (c) 2012, Broadcom Europe Ltd
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, 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
16THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23ON 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
25SOFTWARE, 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
68VCOS_LOG_CAT_T hostfs_log_cat;
69
70/******************************************************************************
71Global data.
72******************************************************************************/
73
74/******************************************************************************
75Local 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
86struct 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
100typedef 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/******************************************************************************
109Static data.
110******************************************************************************/
111
112static file_info_t *p_file_info_table = NULL;
113static int file_info_table_len = 0;
114
115/******************************************************************************
116Static functions.
117******************************************************************************/
118
119static void backslash_to_slash( char *s );
120
121/******************************************************************************
122Global functions.
123******************************************************************************/
124
125/******************************************************************************
126NAME
127 vc_hostfs_init
128
129SYNOPSIS
130 void vc_hostfs_init(void)
131
132FUNCTION
133 Initialises the host to accept requests from Videocore
134
135RETURNS
136 void
137******************************************************************************/
138
139void 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
169void 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/******************************************************************************
180NAME
181 vc_hostfs_close
182
183SYNOPSIS
184 int vc_hostfs_close(int fildes)
185
186FUNCTION
187 Deallocates the file descriptor to a file.
188
189RETURNS
190 Successful completion: 0
191 Otherwise: -1
192******************************************************************************/
193
194int vc_hostfs_close(int fildes)
195{
196 DEBUG_MINOR("vc_hostfs_close(%d)", fildes);
197 return close(fildes);
198}
199
200/******************************************************************************
201NAME
202 vc_hostfs_lseek
203
204SYNOPSIS
205 long vc_hostfs_lseek(int fildes, long offset, int whence)
206
207FUNCTION
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
214RETURNS
215 Successful completion: offset
216 Otherwise: -1
217******************************************************************************/
218
219long 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/******************************************************************************
226NAME
227 vc_hostfs_lseek64
228
229SYNOPSIS
230 int64_t vc_hostfs_lseek64(int fildes, int64_t offset, int whence)
231
232FUNCTION
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
239RETURNS
240 Successful completion: offset
241 Otherwise: -1
242******************************************************************************/
243
244int64_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/******************************************************************************
295NAME
296 vc_hostfs_open
297
298SYNOPSIS
299 int vc_hostfs_open(const char *path, int vc_oflag)
300
301FUNCTION
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
307RETURNS
308 Successful completion: file descriptor
309 Otherwise: -1
310******************************************************************************/
311
312int 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/******************************************************************************
424NAME
425 vc_hostfs_read
426
427SYNOPSIS
428 int vc_hostfs_read(int fildes, void *buf, unsigned int nbyte)
429
430FUNCTION
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
436RETURNS
437 Successful completion: number of bytes read
438 Otherwise: -1
439******************************************************************************/
440
441int 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/******************************************************************************
464NAME
465 vc_hostfs_write
466
467SYNOPSIS
468 int vc_hostfs_write(int fildes, const void *buf, unsigned int nbyte)
469
470FUNCTION
471 Attempts to write nbyte bytes from the buffer pointed to by buf to file
472 associated with the file descriptor, fildes.
473
474RETURNS
475 Successful completion: number of bytes written
476 Otherwise: -1
477******************************************************************************/
478
479int 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/******************************************************************************
487NAME
488 vc_hostfs_closedir
489
490SYNOPSIS
491 int vc_hostfs_closedir(void *dhandle)
492
493FUNCTION
494 Ends a directory list iteration.
495
496RETURNS
497 Successful completion: 0
498 Otherwise: -1
499******************************************************************************/
500
501int 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/******************************************************************************
520NAME
521 vc_hostfs_format
522
523SYNOPSIS
524 int vc_hostfs_format(const char *path)
525
526FUNCTION
527 Formats the physical file system that contains path.
528
529RETURNS
530 Successful completion: 0
531 Otherwise: -1
532******************************************************************************/
533
534int vc_hostfs_format(const char *path)
535{
536 DEBUG_MINOR("vc_hostfs_format: '%s' not implemented", path);
537 return -1;
538}
539
540/******************************************************************************
541NAME
542 vc_hostfs_freespace
543
544SYNOPSIS
545 int vc_hostfs_freespace(const char *path)
546
547FUNCTION
548 Returns the amount of free space on the physical file system that contains
549 path.
550
551RETURNS
552 Successful completion: free space
553 Otherwise: -1
554******************************************************************************/
555
556int 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/******************************************************************************
580NAME
581 vc_hostfs_freespace
582
583SYNOPSIS
584 int vc_hostfs_freespace(const char *path)
585
586FUNCTION
587 Returns the amount of free space on the physical file system that contains
588 path.
589
590RETURNS
591 Successful completion: free space
592 Otherwise: -1
593******************************************************************************/
594int64_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/******************************************************************************
622NAME
623 vc_hostfs_get_attr
624
625SYNOPSIS
626 int vc_hostfs_get_attr(const char *path, fattributes_t *attr)
627
628FUNCTION
629 Gets the file/directory attributes.
630
631RETURNS
632 Successful completion: 0
633 Otherwise: -1
634******************************************************************************/
635
636int 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/******************************************************************************
663NAME
664 vc_hostfs_mkdir
665
666SYNOPSIS
667 int vc_hostfs_mkdir(const char *path)
668
669FUNCTION
670 Creates a new directory named by the pathname pointed to by path.
671
672RETURNS
673 Successful completion: 0
674 Otherwise: -1
675******************************************************************************/
676
677int 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/******************************************************************************
688NAME
689 vc_hostfs_opendir
690
691SYNOPSIS
692 void *vc_hostfs_opendir(const char *dirname)
693
694FUNCTION
695 Starts a directory list iteration of sub-directories.
696
697RETURNS
698 Successful completion: dhandle (pointer)
699 Otherwise: NULL
700******************************************************************************/
701
702void *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/******************************************************************************
747NAME
748 vc_hostfs_readdir_r
749
750SYNOPSIS
751 struct dirent *vc_hostfs_readdir_r(void *dhandle, struct dirent *result)
752
753FUNCTION
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
760RETURNS
761 Successful completion: result
762 End of directory stream: NULL
763******************************************************************************/
764
765struct 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/******************************************************************************
818NAME
819 vc_hostfs_remove
820
821SYNOPSIS
822 int vc_hostfs_remove(const char *path)
823
824FUNCTION
825 Removes a file or a directory. A directory must be empty before it can be
826 deleted.
827
828RETURNS
829 Successful completion: 0
830 Otherwise: -1
831******************************************************************************/
832
833int 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/******************************************************************************
854NAME
855 vc_hostfs_rename
856
857SYNOPSIS
858 int vc_hostfs_rename(const char *old, const char *new)
859
860FUNCTION
861 Changes the name of a file. The old and new pathnames must be on the same
862 physical file system.
863
864RETURNS
865 Successful completion: 0
866 Otherwise: -1
867******************************************************************************/
868
869int 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/******************************************************************************
896NAME
897 vc_hostfs_set_attr
898
899SYNOPSIS
900 int vc_hostfs_set_attr(const char *path, fattributes_t attr)
901
902FUNCTION
903 Sets file/directory attributes.
904
905RETURNS
906 Successful completion: 0
907 Otherwise: -1
908******************************************************************************/
909
910int 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/******************************************************************************
951NAME
952 vc_hostfs_setend
953
954SYNOPSIS
955 int vc_hostfs_setend(int fildes)
956
957FUNCTION
958 Truncates file at current position.
959
960RETURNS
961 Successful completion: 0
962 Otherwise: -1
963******************************************************************************/
964
965int 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/******************************************************************************
981NAME
982 vc_hostfs_totalspace64
983
984SYNOPSIS
985 int64_t vc_hostfs_totalspace64(const char *path)
986
987FUNCTION
988 Returns the total amount of space on the physical file system that contains
989 path.
990
991RETURNS
992 Successful completion: total space
993 Otherwise: -1
994******************************************************************************/
995
996int64_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/******************************************************************************
1028NAME
1029 vc_hostfs_totalspace
1030
1031SYNOPSIS
1032 int vc_hostfs_totalspace(const char *path)
1033
1034FUNCTION
1035 Returns the total amount of space on the physical file system that contains
1036 path.
1037
1038RETURNS
1039 Successful completion: total space
1040 Otherwise: -1
1041******************************************************************************/
1042
1043int 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/******************************************************************************
1061NAME
1062 backslash_to_slash
1063
1064SYNOPSIS
1065 void backslash_to_slash( char *s )
1066
1067FUNCTION
1068 Convert all '\' in a string to '/'.
1069
1070RETURNS
1071 None.
1072******************************************************************************/
1073
1074static 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/******************************************************************************
1087NAME
1088 vc_hostfs_scandisk
1089
1090SYNOPSIS
1091 void vc_hostfs_scandisk(const char *path)
1092
1093FUNCTION
1094 Invalidates any cluster chains in the FAT that are not referenced
1095 in any directory structures
1096
1097RETURNS
1098 Void
1099******************************************************************************/
1100
1101void vc_hostfs_scandisk(const char *path)
1102{
1103 (void)path;
1104
1105 // not yet implemented
1106}
1107
1108
1109/******************************************************************************
1110NAME
1111 vc_hostfs_chkdsk
1112
1113SYNOPSIS
1114 int vc_hostfs_chkdsk(const char *path, int fix_errors)
1115
1116FUNCTION
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
1120RETURNS
1121 Successful completion: 0
1122 Otherwise: indicates failure
1123******************************************************************************/
1124
1125int vc_hostfs_chkdsk(const char *path, int fix_errors)
1126{
1127 (void)path;
1128 (void)fix_errors;
1129 return 0;
1130}
1131
1132