2 Copyright (c) 2012, Broadcom Europe Ltd
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.
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.
28 #define VCOS_LOG_CATEGORY (&hostfs_log_cat)
30 #ifndef _LARGEFILE_SOURCE
31 #define _LARGEFILE_SOURCE
33 #ifndef _LARGEFILE64_SOURCE
34 #define _LARGEFILE64_SOURCE
36 #define _FILE_OFFSET_BITS 64 /* So we get lseek and lseek64 */
45 #include <sys/statfs.h>
52 #if defined(__GLIBC__) && !defined( __USE_FILE_OFFSET64 )
53 #error "__USE_FILE_OFFSET64 isn't defined"
56 #include "interface/vcos/vcos.h"
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"
66 #include "vc_fileservice_defs.h"
68 VCOS_LOG_CAT_T hostfs_log_cat;
70 /******************************************************************************
72 ******************************************************************************/
74 /******************************************************************************
75 Local types and defines.
76 ******************************************************************************/
78 //#define DEBUG_LEVEL 1
79 #define DEBUG_MINOR(...) vcos_log_info(__VA_ARGS__)
80 #define DEBUG_MAJOR(...) vcos_log_warn(__VA_ARGS__)
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()).
90 char pathbuf[PATH_MAX];
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.
102 int is_fifo; // non-zero if file is a FIFO
103 uint64_t read_offset; // read offset into file
106 #define FILE_INFO_TABLE_CHUNK_LEN 20
108 /******************************************************************************
110 ******************************************************************************/
112 static file_info_t *p_file_info_table = NULL;
113 static int file_info_table_len = 0;
115 /******************************************************************************
117 ******************************************************************************/
119 static void backslash_to_slash( char *s );
121 /******************************************************************************
123 ******************************************************************************/
125 /******************************************************************************
130 void vc_hostfs_init(void)
133 Initialises the host to accept requests from Videocore
137 ******************************************************************************/
139 void vc_hostfs_init(void)
141 // This hostfs module is not thread safe - it allocaes a block
142 // of memory and uses it without any kind of locking.
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
148 const char *thread_name = vcos_thread_get_name(vcos_thread_current());
149 if (strcmp(thread_name, "FILESYS") != 0 && strcmp(thread_name, "HFilesys") != 0)
151 fprintf(stderr,"%s: vc_hostfs is deprecated. Please use stdio\n",
152 vcos_thread_get_name(vcos_thread_current()));
155 vcos_log_register("hostfs", &hostfs_log_cat);
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)
162 file_info_table_len = FILE_INFO_TABLE_CHUNK_LEN;
166 /** Terminate this library. Clean up resources.
169 void vc_hostfs_exit(void)
171 vcos_log_unregister(&hostfs_log_cat);
172 if (p_file_info_table)
174 free(p_file_info_table);
175 p_file_info_table = NULL;
179 /******************************************************************************
184 int vc_hostfs_close(int fildes)
187 Deallocates the file descriptor to a file.
190 Successful completion: 0
192 ******************************************************************************/
194 int vc_hostfs_close(int fildes)
196 DEBUG_MINOR("vc_hostfs_close(%d)", fildes);
197 return close(fildes);
200 /******************************************************************************
205 long vc_hostfs_lseek(int fildes, long offset, int whence)
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.
215 Successful completion: offset
217 ******************************************************************************/
219 long vc_hostfs_lseek(int fildes, long offset, int whence)
221 return (long) vc_hostfs_lseek64( fildes, (int64_t) offset, whence);
225 /******************************************************************************
230 int64_t vc_hostfs_lseek64(int fildes, int64_t offset, int whence)
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.
240 Successful completion: offset
242 ******************************************************************************/
244 int64_t vc_hostfs_lseek64(int fildes, int64_t offset, int whence)
246 DEBUG_MINOR("vc_hostfs_lseek(%d,%" PRId64 ",%d)", fildes, offset, whence);
247 if (fildes >= file_info_table_len)
249 // File descriptor not in table, so this is an error
250 DEBUG_MAJOR("vc_hostfs_lseek: invalid fildes %d", fildes);
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;
259 if (p_file_info_table[fildes].is_fifo)
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
265 if (whence == SEEK_SET)
267 read_offset = offset;
269 else if (whence == SEEK_CUR)
271 read_offset += offset;
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);
282 // File is not a FIFO, so do the seek
283 read_offset = lseek64(fildes, offset, whence);
285 p_file_info_table[fildes].read_offset = read_offset;
286 DEBUG_MINOR("vc_hostfs_lseek returning %" PRId64 ")", read_offset);
294 /******************************************************************************
299 int vc_hostfs_open(const char *path, int vc_oflag)
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
308 Successful completion: file descriptor
310 ******************************************************************************/
312 int vc_hostfs_open(const char *inPath, int vc_oflag)
314 char *path = strdup( inPath );
316 int flags = 0, ret=errno;
317 struct stat fileStat;
319 // Replace all '\' with '/'
320 backslash_to_slash( path );
323 s = path + strlen( path );
324 if (( s - path ) >= 4 )
326 if ( strcasecmp( &s[ -4 ], ".vll" ) == 0 )
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 ;
332 s--; // backup to the last character (*s is on the '\0')
333 while (( s >= path ) && ( *s != '/' ))
341 DEBUG_MINOR("vc_hostfs_open: '%s'", path);
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;
351 //while (*path == '\\') path++; // do not want initial '\'
353 ret = open(path, flags, S_IRUSR | S_IWUSR );
355 ret = open(path, flags );
359 DEBUG_MINOR("vc_hostfs_open(%s,%d) = %d", path, vc_oflag, ret);
363 DEBUG_MINOR("vc_hostfs_open(%s,%d) = %d", path, vc_oflag, ret);
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
370 // File was successfully opened
371 if (ret >= file_info_table_len)
373 file_info_t *p_new_file_info_table = p_file_info_table;
374 int new_file_info_table_len = file_info_table_len;
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)
382 DEBUG_MAJOR("vc_hostfs_open: file_info_table calloc failed");
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;
395 assert( ret < file_info_table_len );
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;
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)
408 DEBUG_MINOR("vc_hostfs_open: fstat failed: %s", strerror(errno));
410 else if (S_ISFIFO( fileStat.st_mode ))
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);
423 /******************************************************************************
428 int vc_hostfs_read(int fildes, void *buf, unsigned int nbyte)
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
437 Successful completion: number of bytes read
439 ******************************************************************************/
441 int vc_hostfs_read(int fildes, void *buf, unsigned int nbyte)
443 if (fildes >= file_info_table_len)
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);
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);
457 p_file_info_table[fildes].read_offset += (long) ret;
463 /******************************************************************************
468 int vc_hostfs_write(int fildes, const void *buf, unsigned int nbyte)
471 Attempts to write nbyte bytes from the buffer pointed to by buf to file
472 associated with the file descriptor, fildes.
475 Successful completion: number of bytes written
477 ******************************************************************************/
479 int vc_hostfs_write(int fildes, const void *buf, unsigned int nbyte)
481 int ret = (int) write(fildes, buf, nbyte);
482 DEBUG_MINOR("vc_hostfs_write(%d,%p,%u) = %d", fildes, buf, nbyte, ret);
486 /******************************************************************************
491 int vc_hostfs_closedir(void *dhandle)
494 Ends a directory list iteration.
497 Successful completion: 0
499 ******************************************************************************/
501 int vc_hostfs_closedir(void *dhandle)
503 struct fs_dir *fsdir = (struct fs_dir *)dhandle;
506 DEBUG_MINOR( "vc_hostfs_closedir(%p)", dhandle );
508 if (dhandle && fsdir->dhandle)
510 (void)closedir(fsdir->dhandle);
511 fsdir->dhandle = NULL;
519 /******************************************************************************
524 int vc_hostfs_format(const char *path)
527 Formats the physical file system that contains path.
530 Successful completion: 0
532 ******************************************************************************/
534 int vc_hostfs_format(const char *path)
536 DEBUG_MINOR("vc_hostfs_format: '%s' not implemented", path);
540 /******************************************************************************
545 int vc_hostfs_freespace(const char *path)
548 Returns the amount of free space on the physical file system that contains
552 Successful completion: free space
554 ******************************************************************************/
556 int vc_hostfs_freespace(const char *inPath)
560 int64_t freeSpace = vc_hostfs_freespace64( inPath );
562 // Saturate return value (need this in case we have a large file system)
563 if (freeSpace > (int64_t) INT_MAX)
569 ret = (int) freeSpace;
579 /******************************************************************************
584 int vc_hostfs_freespace(const char *path)
587 Returns the amount of free space on the physical file system that contains
591 Successful completion: free space
593 ******************************************************************************/
594 int64_t vc_hostfs_freespace64(const char *inPath)
596 char *path = strdup( inPath );
598 struct statfs fsStat;
600 // Replace all '\' with '/'
601 backslash_to_slash( path );
603 ret = (int64_t) statfs( path, &fsStat );
607 ret = fsStat.f_bsize * fsStat.f_bavail;
614 DEBUG_MINOR( "vc_hostfs_freespace64 for '%s' returning %" PRId64 "", path, ret );
621 /******************************************************************************
626 int vc_hostfs_get_attr(const char *path, fattributes_t *attr)
629 Gets the file/directory attributes.
632 Successful completion: 0
634 ******************************************************************************/
636 int vc_hostfs_get_attr(const char *path, fattributes_t *attr)
640 DEBUG_MINOR("vc_hostfs_get_attr: '%s'", path );
645 if ( stat( path, &sb ) == 0 )
647 if ( S_ISDIR( sb.st_mode ))
649 *attr |= ATTR_DIRENT;
652 if (( sb.st_mode & S_IWUSR ) == 0 )
654 *attr |= ATTR_RDONLY;
662 /******************************************************************************
667 int vc_hostfs_mkdir(const char *path)
670 Creates a new directory named by the pathname pointed to by path.
673 Successful completion: 0
675 ******************************************************************************/
677 int vc_hostfs_mkdir(const char *path)
679 DEBUG_MINOR( "vc_hostfs_mkdir: '%s'", path );
680 if ( mkdir( path, 0777 ) == 0 )
687 /******************************************************************************
692 void *vc_hostfs_opendir(const char *dirname)
695 Starts a directory list iteration of sub-directories.
698 Successful completion: dhandle (pointer)
700 ******************************************************************************/
702 void *vc_hostfs_opendir(const char *dirname)
704 struct fs_dir *fsdir = NULL;
706 DEBUG_MINOR( "vc_hostfs_opendir: '%s'", dirname );
708 if (dirname && dirname[0])
710 fsdir = (struct fs_dir *)malloc(sizeof(struct fs_dir));
715 int len = strlen(dirname);
717 memcpy(fsdir->pathbuf, dirname, len);
719 backslash_to_slash(fsdir->pathbuf);
721 /* Remove any trailing slashes */
722 while (fsdir->pathbuf[len - 1] == '/')
725 fsdir->pathbuf[len] = '\0';
727 dhandle = opendir(fsdir->pathbuf);
728 DEBUG_MINOR( "opendir: '%s' = %p", fsdir->pathbuf, dhandle );
732 fsdir->pathlen = len;
733 fsdir->dhandle = dhandle;
746 /******************************************************************************
751 struct dirent *vc_hostfs_readdir_r(void *dhandle, struct dirent *result)
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.
761 Successful completion: result
762 End of directory stream: NULL
763 ******************************************************************************/
765 struct fs_dirent *vc_hostfs_readdir_r(void *dhandle, struct fs_dirent *result)
767 struct fs_dir *fsdir = (struct fs_dir *)dhandle;
769 DEBUG_MINOR( "vc_hostfs_readdir_r(%p)", fsdir );
775 while ((dent = readdir(fsdir->dhandle)) != NULL)
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';
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 );
804 DEBUG_MINOR( "vc_hostfs_readdir_r() = NULL" );
805 rewinddir(fsdir->dhandle);
817 /******************************************************************************
822 int vc_hostfs_remove(const char *path)
825 Removes a file or a directory. A directory must be empty before it can be
829 Successful completion: 0
831 ******************************************************************************/
833 int vc_hostfs_remove(const char *path)
835 char *pathbuf = strdup(path);
838 DEBUG_MINOR( "vc_hostfs_remove: '%s'", path );
842 backslash_to_slash(pathbuf);
844 if ( unlink( pathbuf ) == 0 )
853 /******************************************************************************
858 int vc_hostfs_rename(const char *old, const char *new)
861 Changes the name of a file. The old and new pathnames must be on the same
862 physical file system.
865 Successful completion: 0
867 ******************************************************************************/
869 int vc_hostfs_rename(const char *old, const char *new)
871 char *oldbuf = strdup(old);
872 char *newbuf = strdup(new);
875 DEBUG_MINOR( "vc_hostfs_rename: '%s' to '%s'", old, new );
877 if (oldbuf && newbuf)
879 backslash_to_slash(oldbuf);
880 backslash_to_slash(newbuf);
882 if ( rename( oldbuf, newbuf ) == 0 )
895 /******************************************************************************
900 int vc_hostfs_set_attr(const char *path, fattributes_t attr)
903 Sets file/directory attributes.
906 Successful completion: 0
908 ******************************************************************************/
910 int vc_hostfs_set_attr(const char *path, fattributes_t attr)
912 char *pathbuf = strdup(path);
915 DEBUG_MINOR( "vc_hostfs_set_attr: '%s', %x", path, attr );
922 backslash_to_slash(pathbuf);
924 if ( stat( path, &sb ) == 0 )
928 if ( attr & ATTR_RDONLY )
937 /* coverity[toctou] Not doing anything security-relevant here,
938 * so the race condition is harmless */
939 if ( chmod( path, mode ) == 0 )
950 /******************************************************************************
955 int vc_hostfs_setend(int fildes)
958 Truncates file at current position.
961 Successful completion: 0
963 ******************************************************************************/
965 int vc_hostfs_setend(int filedes)
969 if (( currPosn = lseek( filedes, 0, SEEK_CUR )) != (off_t)-1 )
971 if ( ftruncate( filedes, currPosn ) == 0 )
980 /******************************************************************************
982 vc_hostfs_totalspace64
985 int64_t vc_hostfs_totalspace64(const char *path)
988 Returns the total amount of space on the physical file system that contains
992 Successful completion: total space
994 ******************************************************************************/
996 int64_t vc_hostfs_totalspace64(const char *inPath)
998 char *path = strdup( inPath );
1000 struct statfs fsStat;
1002 // Replace all '\' with '/'
1005 backslash_to_slash( path );
1007 ret = statfs( path, &fsStat );
1011 ret = fsStat.f_bsize * fsStat.f_blocks;
1019 DEBUG_MINOR( "vc_hostfs_totalspace for '%s' returning %" PRId64 "", path, ret );
1027 /******************************************************************************
1029 vc_hostfs_totalspace
1032 int vc_hostfs_totalspace(const char *path)
1035 Returns the total amount of space on the physical file system that contains
1039 Successful completion: total space
1041 ******************************************************************************/
1043 int vc_hostfs_totalspace(const char *inPath)
1046 int64_t totalSpace = vc_hostfs_totalspace64(inPath);
1048 // Saturate return value (need this in case we have a large file system)
1049 if (totalSpace > (int64_t) INT_MAX)
1055 ret = (int) totalSpace;
1060 /******************************************************************************
1065 void backslash_to_slash( char *s )
1068 Convert all '\' in a string to '/'.
1072 ******************************************************************************/
1074 static void backslash_to_slash( char *s )
1076 while ( *s != '\0' )
1086 /******************************************************************************
1091 void vc_hostfs_scandisk(const char *path)
1094 Invalidates any cluster chains in the FAT that are not referenced
1095 in any directory structures
1099 ******************************************************************************/
1101 void vc_hostfs_scandisk(const char *path)
1105 // not yet implemented
1109 /******************************************************************************
1114 int vc_hostfs_chkdsk(const char *path, int fix_errors)
1117 Checks whether or not a FAT filesystem is corrupt or not. If fix_errors
1118 is TRUE behaves exactly as vc_filesys_scandisk.
1121 Successful completion: 0
1122 Otherwise: indicates failure
1123 ******************************************************************************/
1125 int vc_hostfs_chkdsk(const char *path, int fix_errors)