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