372 lines
13 KiB
C
372 lines
13 KiB
C
|
/***************************************************************************
|
||
|
* __________ __ ___.
|
||
|
* 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_ */
|