diff options
Diffstat (limited to 'firmware/include/file_internal.h')
-rw-r--r-- | firmware/include/file_internal.h | 371 |
1 files changed, 371 insertions, 0 deletions
diff --git a/firmware/include/file_internal.h b/firmware/include/file_internal.h new file mode 100644 index 0000000000..d1bb67406a --- /dev/null +++ b/firmware/include/file_internal.h @@ -0,0 +1,371 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2014 by Michael Sevakis + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#ifndef _FILE_INTERNAL_H_ +#define _FILE_INTERNAL_H_ + +#include <sys/types.h> +#include <stdlib.h> +#include "mv.h" +#include "linked_list.h" +#include "mutex.h" +#include "mrsw_lock.h" +#include "fs_attr.h" +#include "fat.h" +#ifdef HAVE_DIRCACHE +#include "dircache.h" +#endif + +/** Tuneable parameters **/ + +/* limits for number of open descriptors - if you increase these values, make + certain that the disk cache has enough available buffers */ +#define MAX_OPEN_FILES 11 +#define MAX_OPEN_DIRS 12 +#define MAX_OPEN_HANDLES (MAX_OPEN_FILES+MAX_OPEN_DIRS) + +/* internal functions open streams as well; make sure they don't fail if all + user descs are busy; this needs to be at least the greatest quantity needed + at once by all internal functions */ +#ifdef HAVE_DIRCACHE +#define AUX_FILEOBJS 3 +#else +#define AUX_FILEOBJS 2 +#endif + +/* number of components statically allocated to handle the vast majority + of path depths; should maybe be tuned for >= 90th percentile but for now, + imma just guessing based on something like: + root + 'Music' + 'Artist' + 'Album' + 'Disc N' + filename */ +#define STATIC_PATHCOMP_NUM 6 + +#define MAX_NAME 255 + +/* unsigned value that will also hold the off_t range we need without + overflow */ +#define file_size_t uint32_t + +#ifdef __USE_FILE_OFFSET64 +/* if we want, we can deal with files up to 2^32-1 bytes-- the full FAT16/32 + range */ +#define FILE_SIZE_MAX (0xffffffffu) +#else +/* file contents and size will be preserved by the APIs so long as ftruncate() + isn't used; bytes passed 2^31-1 will not accessible nor will writes succeed + that would extend the file beyond the max for a 32-bit off_t */ +#define FILE_SIZE_MAX (0x7fffffffu) +#endif + +/* if file is "large(ish)", then get rid of the contents now rather than + lazily when the file is synced or closed in order to free-up space */ +#define O_TRUNC_THRESH 65536 + +/* default attributes when creating new files and directories */ +#define ATTR_NEW_FILE (ATTR_ARCHIVE) +#define ATTR_NEW_DIRECTORY (ATTR_DIRECTORY) + +#define ATTR_MOUNT_POINT (ATTR_VOLUME | ATTR_DIRECTORY) + +/** File sector cache **/ + +enum filestr_cache_flags +{ + FSC_DIRTY = 0x1, /* buffer is dirty (needs writeback) */ + FSC_NEW = 0x2, /* buffer is new (never yet written) */ +}; + +struct filestr_cache +{ + uint8_t *buffer; /* buffer to hold sector */ + unsigned long sector; /* file sector that is in buffer */ + unsigned int flags; /* FSC_* bits */ +}; + +void file_cache_init(struct filestr_cache *cachep); +void file_cache_reset(struct filestr_cache *cachep); +void file_cache_alloc(struct filestr_cache *cachep); +void file_cache_free(struct filestr_cache *cachep); + + +/** Common bitflags used throughout **/ + +/* bitflags used by open files and descriptors */ +enum fildes_and_obj_flags +{ + /* used in descriptor and common */ + FDO_BUSY = 0x0001, /* descriptor/object is in use */ + /* only used in individual stream descriptor */ + FD_WRITE = 0x0002, /* descriptor has write mode */ + FD_WRONLY = 0x0004, /* descriptor is write mode only */ + FD_APPEND = 0x0008, /* descriptor is append mode */ + /* only used as common flags */ + FO_DIRECTORY = 0x0010, /* fileobj is a directory */ + FO_TRUNC = 0x0020, /* fileobj is opened to be truncated */ + FO_REMOVED = 0x0040, /* fileobj was deleted while open */ + FO_SINGLE = 0x0080, /* fileobj has only one stream open */ + FDO_MASK = 0x00ff, + /* bitflags that instruct various 'open' functions how to behave */ + FF_FILE = 0x0000, /* expect file; accept file only */ + FF_DIR = 0x0100, /* expect dir; accept dir only */ + FF_ANYTYPE = 0x0200, /* succeed if either file or dir */ + FF_TYPEMASK = 0x0300, /* mask of typeflags */ + FF_CREAT = 0x0400, /* create if file doesn't exist */ + FF_EXCL = 0x0800, /* fail if creating and file exists */ + FF_CHECKPREFIX = 0x1000, /* detect if file is prefix of path */ + FF_NOISO = 0x2000, /* do not decode ISO filenames to UTF-8 */ + FF_MASK = 0x3f00, + /* special values used in isolation */ + FV_NONEXIST = 0x8000, /* closed but not freed (unmounted) */ + FV_OPENSYSROOT = 0xc001, /* open sysroot, volume 0 not mounted */ +}; + + +/** Common data structures used throughout **/ + +/* basic file information about its location */ +struct file_base_info +{ + union { +#ifdef HAVE_MULTIVOLUME + int volume; /* file's volume (overlaps fatfile.volume) */ +#endif +#if CONFIG_PLATFORM & PLATFORM_NATIVE + struct fat_file fatfile; /* FS driver file info */ +#endif + }; +#ifdef HAVE_DIRCACHE + struct dircache_file dcfile; /* dircache file info */ +#endif +}; + +#define BASEINFO_VOL(infop) \ + IF_MV_VOL((infop)->volume) + +/* open files binding item */ +struct file_base_binding +{ + struct ll_node node; /* list item node (first!) */ + struct file_base_info info; /* basic file info */ +}; + +#define BASEBINDING_VOL(bindp) \ + BASEINFO_VOL(&(bindp)->info) + +/* directory scanning position info */ +struct dirscan_info +{ +#if CONFIG_PLATFORM & PLATFORM_NATIVE + struct fat_dirscan_info fatscan; /* FS driver scan info */ +#endif +#ifdef HAVE_DIRCACHE + struct dircache_file dcscan; /* dircache scan info */ +#endif +}; + +/* describes the file as an open stream */ +struct filestr_base +{ + struct ll_node node; /* list item node (first!) */ + uint16_t flags; /* FD_* bits of this stream */ + uint16_t unused; /* not used */ + struct filestr_cache cache; /* stream-local cache */ + struct filestr_cache *cachep; /* the cache in use (local or shared) */ + struct file_base_info *infop; /* base file information */ + struct fat_filestr fatstr; /* FS driver information */ + struct file_base_binding *bindp; /* common binding for file/dir */ + struct mutex *mtx; /* serialization for this stream */ +}; + +void filestr_base_init(struct filestr_base *stream); +void filestr_base_destroy(struct filestr_base *stream); +void filestr_alloc_cache(struct filestr_base *stream); +void filestr_free_cache(struct filestr_base *stream); +void filestr_assign_cache(struct filestr_base *stream, + struct filestr_cache *cachep); +void filestr_copy_cache(struct filestr_base *stream, + struct filestr_cache *cachep); +void filestr_discard_cache(struct filestr_base *stream); + +/* allocates a cache buffer if needed and returns the cache pointer */ +static inline struct filestr_cache * +filestr_get_cache(struct filestr_base *stream) +{ + struct filestr_cache *cachep = stream->cachep; + + if (!cachep->buffer) + filestr_alloc_cache(stream); + + return cachep; +} + +static inline void filestr_lock(struct filestr_base *stream) +{ + mutex_lock(stream->mtx); +} + +static inline void filestr_unlock(struct filestr_base *stream) +{ + mutex_unlock(stream->mtx); +} + +/* stream lock doesn't have to be used if getting RW lock writer access */ +#define FILESTR_WRITER 0 +#define FILESTR_READER 1 + +#define FILESTR_LOCK(type, stream) \ + ({ if (FILESTR_##type) filestr_lock(stream); }) + +#define FILESTR_UNLOCK(type, stream) \ + ({ if (FILESTR_##type) filestr_unlock(stream); }) + +#define ATTR_PREFIX (0x8000) /* out of the way of all ATTR_* bits */ + +/* structure to return detailed information about what you opened */ +struct path_component_info +{ + const char *name; /* pointer to name within 'path' */ + size_t length; /* length of component within 'path' */ + file_size_t filesize; /* size of the opened file (0 if dir) */ + unsigned int attr; /* attributes of this component */ + struct file_base_info *prefixp; /* base info to check as prefix (IN) */ + struct file_base_info parentinfo; /* parent directory info of file */ +}; + +int open_stream_internal(const char *path, unsigned int callflags, + struct filestr_base *stream, + struct path_component_info *compinfo); +int close_stream_internal(struct filestr_base *stream); +int create_stream_internal(struct file_base_info *parentinfop, + const char *basename, size_t length, + unsigned int attr, unsigned int callflags, + struct filestr_base *stream); +int remove_stream_internal(const char *path, struct filestr_base *stream, + unsigned int callflags); +int test_stream_exists_internal(const char *path, unsigned int callflags); + +int open_noiso_internal(const char *path, int oflag); /* file.c */ + +struct dirent; +int uncached_readdir_dirent(struct filestr_base *stream, + struct dirscan_info *scanp, + struct dirent *entry); +void uncached_rewinddir_dirent(struct dirscan_info *scanp); + +int uncached_readdir_internal(struct filestr_base *stream, + struct file_base_info *infop, + struct fat_direntry *fatent); +void uncached_rewinddir_internal(struct file_base_info *infop); + +int test_dir_empty_internal(struct filestr_base *stream); + +struct dirinfo_internal +{ + unsigned int attr; + file_size_t size; + uint16_t wrtdate; + uint16_t wrttime; +}; + +/** Synchronization used throughout **/ + +/* acquire the filesystem lock as READER */ +static inline void file_internal_lock_READER(void) +{ + extern struct mrsw_lock file_internal_mrsw; + mrsw_read_acquire(&file_internal_mrsw); +} + +/* release the filesystem lock as READER */ +static inline void file_internal_unlock_READER(void) +{ + extern struct mrsw_lock file_internal_mrsw; + mrsw_read_release(&file_internal_mrsw); +} + +/* acquire the filesystem lock as WRITER */ +static inline void file_internal_lock_WRITER(void) +{ + extern struct mrsw_lock file_internal_mrsw; + mrsw_write_acquire(&file_internal_mrsw); +} + +/* release the filesystem lock as WRITER */ +static inline void file_internal_unlock_WRITER(void) +{ + extern struct mrsw_lock file_internal_mrsw; + mrsw_write_release(&file_internal_mrsw); +} + +#define ERRNO 0 /* maintain errno value */ +#define RC 0 /* maintain rc value */ + +/* NOTES: if _errno is a non-constant expression, it must set an error + * number and not return the ERRNO constant which will merely set + * errno to zero, not preserve the current value; if you must set + * errno to zero, set it explicitly, not in the macro + * + * if _rc is constant-expression evaluation to 'RC', then rc will + * NOT be altered; i.e. if you must set rc to zero, set it explicitly, + * not in the macro + */ + +/* set errno and rc and proceed to the "file_error:" label */ +#define FILE_ERROR(_errno, _rc) \ + ({ __builtin_constant_p(_errno) ? \ + ({ if ((_errno) != ERRNO) errno = (_errno); }) : \ + ({ errno = (_errno); }); \ + __builtin_constant_p(_rc) ? \ + ({ if ((_rc) != RC) rc = (_rc); }) : \ + ({ rc = (_rc); }); \ + goto file_error; }) + +/* set errno and return a value at the point of invocation */ +#define FILE_ERROR_RETURN(_errno, _rc...) \ + ({ __builtin_constant_p(_errno) ? \ + ({ if ((_errno) != ERRNO) errno = (_errno); }) : \ + ({ errno = (_errno); }); \ + return _rc; }) + + +/** Misc. stuff **/ + +/* iterate through all the volumes if volume < 0, else just the given volume */ +#define FOR_EACH_VOLUME(volume, i) \ + for (int i = (IF_MV_VOL(volume) >= 0 ? IF_MV_VOL(volume) : 0), \ + _end = (IF_MV_VOL(volume) >= 0 ? i : NUM_VOLUMES-1); \ + i <= _end; i++) + +/* return a pointer to the static struct fat_direntry */ +static inline struct fat_direntry *get_dir_fatent(void) +{ + extern struct fat_direntry dir_fatent; + return &dir_fatent; +} + +void iso_decode_d_name(char *d_name); + +#ifdef HAVE_DIRCACHE +void empty_dirent(struct dirent *entry); +void fill_dirinfo_native(struct dirinfo_native *din); +#endif /* HAVE_DIRCACHE */ + +void filesystem_init(void) INIT_ATTR; + +#endif /* _FILE_INTERNAL_H_ */ |