summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--firmware/common/dir.c2
-rw-r--r--firmware/common/dircache.c351
-rw-r--r--firmware/common/file.c57
-rw-r--r--firmware/common/file_internal.c48
-rw-r--r--firmware/common/fileobj_mgr.c126
-rw-r--r--firmware/drivers/fat.c20
-rw-r--r--firmware/export/fat.h6
-rw-r--r--firmware/include/dircache.h40
-rw-r--r--firmware/include/dircache_redirect.h19
-rw-r--r--firmware/include/file_internal.h91
-rw-r--r--firmware/include/fileobj_mgr.h5
11 files changed, 436 insertions, 329 deletions
diff --git a/firmware/common/dir.c b/firmware/common/dir.c
index da798c71d5..59f7bd747a 100644
--- a/firmware/common/dir.c
+++ b/firmware/common/dir.c
@@ -56,7 +56,7 @@ static struct dirstr_desc * get_dirstr(DIR *dirp)
{
errnum = EFAULT;
}
- else if (dir->stream.flags == FV_NONEXIST)
+ else if (dir->stream.flags & FD_NONEXIST)
{
DEBUGF("dir #%d: nonexistant device\n", (int)(dir - open_streams));
errnum = ENXIO;
diff --git a/firmware/common/dircache.c b/firmware/common/dircache.c
index b93ee73fc6..a3538ff96f 100644
--- a/firmware/common/dircache.c
+++ b/firmware/common/dircache.c
@@ -41,9 +41,7 @@
#include "audio.h"
#include "rbpaths.h"
#include "linked_list.h"
-#ifdef HAVE_EEPROM_SETTINGS
#include "crc32.h"
-#endif
/**
* Cache memory layout:
@@ -1457,7 +1455,7 @@ int dircache_readdir_dirent(struct filestr_base *stream,
unsigned int direntry = scanp->fatscan.entry;
while (1)
{
- if (idx == 0 || direntry == FAT_RW_VAL) /* rewound? */
+ if (idx == 0 || direntry == FAT_DIRSCAN_RW_VAL) /* rewound? */
{
idx = diridx <= 0 ? dcvolp->root_down : get_entry(diridx)->down;
break;
@@ -1487,7 +1485,7 @@ int dircache_readdir_dirent(struct filestr_base *stream,
return 0; /* end of dir */
}
- if (ce->direntry > direntry || direntry == FAT_RW_VAL)
+ if (ce->direntry > direntry || direntry == FAT_DIRSCAN_RW_VAL)
break; /* cache reader is caught up to FS scan */
idx = ce->next;
@@ -1549,7 +1547,12 @@ int dircache_readdir_internal(struct filestr_base *stream,
/* is parent cached? if not, readthrough because nothing is here yet */
if (!dirinfop->dcfile.serialnum)
+ {
+ if (stream->flags & FF_CACHEONLY)
+ goto read_eod;
+
return uncached_readdir_internal(stream, infop, fatent);
+ }
int diridx = dirinfop->dcfile.idx;
unsigned int frontier = diridx < 0 ?
@@ -1562,7 +1565,8 @@ int dircache_readdir_internal(struct filestr_base *stream,
idx = get_entry(idx)->next;
struct dircache_entry *ce = get_entry(idx);
- if (frontier != FRONTIER_SETTLED)
+
+ if (frontier != FRONTIER_SETTLED && !(stream->flags & FF_CACHEONLY))
{
/* the directory being read is reported to be incompletely cached;
readthrough and if the entry exists, return it with its binding
@@ -1577,9 +1581,7 @@ int dircache_readdir_internal(struct filestr_base *stream,
else if (!ce)
{
/* end of dir */
- fat_empty_fat_direntry(fatent);
- infop->fatfile.e.entries = 0;
- return 0;
+ goto read_eod;
}
/* FS entry information that we maintain */
@@ -1612,6 +1614,11 @@ int dircache_readdir_internal(struct filestr_base *stream,
}
return rc;
+
+read_eod:
+ fat_empty_fat_direntry(fatent);
+ infop->fatfile.e.entries = 0;
+ return 0;
}
/**
@@ -2451,30 +2458,41 @@ void dircache_fileop_sync(struct file_base_binding *bindp,
/** Dircache paths and files **/
-#ifdef DIRCACHE_DUMPSTER
-/* helper for dircache_get_path() */
-static ssize_t get_path_sub(int idx, char *buf, size_t size)
+/**
+ * helper for returning a path and serial hash represented by an index
+ */
+struct get_path_sub_data
+{
+ char *buf;
+ size_t size;
+ dc_serial_t serialhash;
+};
+
+static ssize_t get_path_sub(int idx, struct get_path_sub_data *data)
{
if (idx == 0)
- return -2; /* entry is an orphan split from any root */
+ return -1; /* entry is an orphan split from any root */
- ssize_t offset;
+ ssize_t len;
char *cename;
if (idx > 0)
{
- /* go all the way up then move back down from the root */
struct dircache_entry *ce = get_entry(idx);
- offset = get_path_sub(ce->up, buf, size) - 1;
- if (offset < 0)
- return -3;
+
+ data->serialhash = dc_hash_serialnum(ce->serialnum, data->serialhash);
+
+ /* go all the way up then move back down from the root */
+ len = get_path_sub(ce->up, data) - 1;
+ if (len < 0)
+ return -2;
cename = alloca(MAX_NAME + 1);
entry_name_copy(cename, ce);
}
else /* idx < 0 */
{
- offset = 0;
+ len = 0;
cename = "";
#ifdef HAVE_MULTIVOLUME
@@ -2486,14 +2504,14 @@ static ssize_t get_path_sub(int idx, char *buf, size_t size)
get_volume_name(volume, cename);
}
#endif /* HAVE_MULTIVOLUME */
+
+ data->serialhash = dc_hash_serialnum(get_idx_dcvolp(idx)->serialnum,
+ data->serialhash);
}
- return offset + path_append(buf + offset, PA_SEP_HARD, cename,
- size > (size_t)offset ? size - offset : 0);
+ return len + path_append(data->buf + len, PA_SEP_HARD, cename,
+ data->size > (size_t)len ? data->size - len : 0);
}
-#endif /* DIRCACHE_DUMPSTER */
-
-#if 0
/**
* retrieve and validate the file's entry/binding serial number
@@ -2523,201 +2541,173 @@ static dc_serial_t get_file_serialnum(const struct dircache_file *dcfilep)
}
/**
+ * Obtain the hash of the serial numbers of the canonical path, index to root
+ */
+static dc_serial_t get_file_serialhash(const struct dircache_file *dcfilep)
+{
+ int idx = dcfilep->idx;
+
+ dc_serial_t h = DC_SERHASH_START;
+
+ while (idx > 0)
+ {
+ struct dircache_entry *ce = get_entry(idx);
+ h = dc_hash_serialnum(ce->serialnum, h);
+ idx = ce->up;
+ }
+
+ h = dc_hash_serialnum(get_idx_dcvolp(idx)->serialnum, h);
+
+ return h;
+}
+
+/**
+ * Initialize the fileref
+ */
+void dircache_fileref_init(struct dircache_fileref *dcfrefp)
+{
+ dircache_dcfile_init(&dcfrefp->dcfile);
+ dcfrefp->serialhash = DC_SERHASH_START;
+}
+
+/**
* usermode function to construct a full absolute path from dircache into the
* given buffer given the dircache file info
*
* returns:
- * success - the length of the string, not including the trailing null
+ * success - the length of the string, not including the trailing null or the
+ * buffer length required if the buffer is too small (return is >=
+ * size)
* failure - a negative value
*
- * successful return value is as strlcpy()
- *
* errors:
- * ENOENT - the file or directory does not exist
+ * ENOENT - No such file or directory
*/
-ssize_t dircache_get_path(const struct dircache_file *dcfilep, char *buf,
- size_t size)
+ssize_t dircache_get_fileref_path(const struct dircache_fileref *dcfrefp, char *buf,
+ size_t size)
{
+ ssize_t rc;
+
/* if missing buffer space, still return what's needed a la strlcpy */
if (!buf)
size = 0;
else if (size)
*buf = '\0';
- ssize_t len = -1;
-
dircache_lock();
/* first and foremost, there must be a cache and the serial number must
check out */
- if (dircache_runinfo.handle && get_file_serialnum(dcfilep))
- len = get_path_sub(dcfilep->idx, buf, size);
-
- if (len < 0)
- errno = ENOENT;
-
- dircache_unlock();
- return len;
-}
+ if (!dircache_runinfo.handle)
+ FILE_ERROR(ENOENT, -1);
-/**
- * searches the sublist starting at 'idx' for the named component
- */
+ if (get_file_serialnum(&dcfrefp->dcfile) == 0)
+ FILE_ERROR(ENOENT, -2);
-/* helper for get_file_sub() */
-static struct dircache_entry *
-get_file_sub_scan(int idx, const char *name, size_t length, int *idxp)
-{
- struct dircache_entry *ce = get_entry(idx);
- if (ce)
+ struct get_path_sub_data data =
{
- char entname[MAX_NAME+1];
- name = strmemdupa(name, length);
-
- do
- {
- entry_name_copy(entname, ce);
- if (!strcasecmp(entname, name))
- {
- *idxp = idx;
- break;
- }
-
- idx = ce->next;
- }
- while ((ce = get_entry(idx)));
- }
-
- return ce;
-}
-
-/**
- * searches for the subcomponent of *pathp
- */
-
-/* helper for dircache_get_file() */
-static int get_file_sub(const char **pathp, int *downp, int *idxp)
-{
- int rc;
- const char *name;
- rc = parse_path_component(pathp, &name, false);
- if (rc <= 0)
- return rc;
- else if (rc >= MAX_PATH)
- return ENAMETOOLONG; /* that's just unpossible, man */
+ .buf = buf,
+ .size = size,
+ .serialhash = DC_SERHASH_START,
+ };
- struct dircache_entry *ce = get_file_sub_scan(*downp, name, rc, idxp);
+ rc = get_path_sub(dcfrefp->dcfile.idx, &data);
+ if (rc < 0)
+ FILE_ERROR(ENOENT, rc * 10 - 3);
- if (!ce)
- rc = RC_NOT_FOUND; /* not there; tellibry solly */
- else if (!*pathp)
- rc = RC_PATH_ENDED; /* done */
- else if (!(ce->attr & ATTR_DIRECTORY))
- rc = ENOTDIR; /* a parent component must be a directory */
- else
- while ((rc = get_file_sub(pathp, &ce->down, idxp)) == RC_CONTINUE);
+ if (data.serialhash != dcfrefp->serialhash)
+ FILE_ERROR(ENOENT, -4);
- switch (rc)
- {
- case RC_GO_UP: /* hit ".."; drop to previous level */
- return RC_CONTINUE;
- case RC_PATH_ENDED: /* success! */
- return RC_FOUND;
- default: /* component not found or error */
- return rc;
- }
+file_error:
+ dircache_unlock();
+ return rc;
}
/**
- * usermode function to return dircache file info for the given path
+ * Test a path to various levels of rigor and optionally return dircache file
+ * info for the given path
*
* returns:
- * success: the volume number that is specified for the file
+ * success: 0
* failure: a negative value
*
- * errors:
- * ENOENT - the file or directory does not exist or path is empty
- * ENAMETOOLONG - a component of the path is too long
- * ENOTDIR - a component of the path is not a directory
+ * errors (including but not limited to):
+ * EFAULT - Bad address
+ * EINVAL - Invalid argument
+ * ENAMETOOLONG - File or path name too long
+ * ENOENT - No such file or directory
+ * ENOTDIR - Not a directory
+ * ENXIO - No such device or address
*/
-int dircache_get_file(const char *path, struct dircache_file *dcfilep)
+int dircache_search(unsigned int flags, struct dircache_fileref *dcfrefp, const char *path)
{
- if (!path_is_absolute(path) || !dcfilep)
- {
- errno = ENOENT;
- return -1;
- }
+ int rc;
+
+ if (!(flags & (DCS_FILEREF | DCS_CACHED_PATH)))
+ FILE_ERROR_RETURN(EINVAL, -1); /* search nothing? */
dircache_lock();
if (!dircache_runinfo.handle)
+ FILE_ERROR(ENOENT, -2);
+
+ if (flags & DCS_FILEREF)
{
- dircache_unlock();
- errno = ENOENT;
- return -2;
- }
+ if (!dcfrefp)
+ FILE_ERROR(EFAULT, -3);
- int volume = 0;
- int idx = 0;
- dc_serial_t serialnum = 0;
- struct dircache_volume *dcvolp = NULL;
- struct dircache_entry *ce = NULL;
+ if (get_file_serialnum(&dcfrefp->dcfile) != 0)
+ {
+ if (!(flags & _DCS_VERIFY_FLAG))
+ goto file_success; /* no robust verification wanted */
+
+ if (get_file_serialhash(&dcfrefp->dcfile) == dcfrefp->serialhash)
+ goto file_success; /* reference is most likely still valid */
+ }
- int rc = RC_GO_UP;
+ if (!(flags & DCS_CACHED_PATH))
+ FILE_ERROR(ENOENT, -4); /* no path search wanted */
+ }
- while (rc == RC_CONTINUE || rc == RC_GO_UP)
+ if (flags & DCS_CACHED_PATH)
{
- #ifdef HAVE_MULTIVOLUME
- if (rc == RC_GO_UP)
+ const bool update = flags & DCS_UPDATE_FILEREF;
+ struct path_component_info *compinfop = NULL;
+
+ if (update)
{
- volume = path_strip_volume(path, &path, false);
- if (!CHECK_VOL(volume))
- {
- rc = ENXIO;
- break;
- }
- }
- #endif /* HAVE_MULTIVOLUME */
+ if (!dcfrefp)
+ FILE_ERROR(EFAULT, -5);
- dcvolp = DCVOL(volume);
+ compinfop = alloca(sizeof (*compinfop));
+ }
- int *downp = &dcvolp->root_down;
- if (*downp <= 0)
+ struct filestr_base stream;
+ rc = open_stream_internal(path, FF_ANYTYPE | FF_PROBE | FF_SELFINFO |
+ ((flags & _DCS_STORAGE_FLAG) ? 0 : FF_CACHEONLY),
+ &stream, compinfop);
+ if (rc <= 0)
{
- rc = ENXIO;
- break;
+ if (update)
+ dircache_fileref_init(dcfrefp);
+
+ FILE_ERROR(rc ? ERRNO : ENOENT, rc * 10 - 6);
}
- rc = get_file_sub(&path, downp, &idx);
- }
-
- switch (rc)
- {
- case RC_FOUND: /* hit: component found */
- serialnum = ce->serialnum;
- rc = volume;
- break;
- case RC_PATH_ENDED: /* hit: it's a root (volume or system) */
- idx = -volume - 1;
- serialnum = dcvolp->serialnum;
- rc = volume;
- break;
- case RC_NOT_FOUND: /* miss */
- rc = ENOENT;
- default:
- idx = 0;
- errno = rc;
- rc = -3;
- break;
+ if (update)
+ {
+ dcfrefp->dcfile = compinfop->info.dcfile;
+ dcfrefp->serialhash = get_file_serialhash(&compinfop->info.dcfile);
+ }
}
- dcfilep->idx = idx;
- dcfilep->serialnum = serialnum;
+file_success:
+ rc = 0;
+file_error:
dircache_unlock();
- return rc;
+ return rc;
}
-#endif /* 0 */
/** Debug screen/info stuff **/
@@ -2737,13 +2727,12 @@ void dircache_get_info(struct dircache_info *info)
if (!info)
return;
- memset(info, 0, sizeof (*info));
-
dircache_lock();
enum dircache_status status = DIRCACHE_IDLE;
+ info->build_ticks = 0;
- for (unsigned int volume = 0; volume < NUM_VOLUMES; volume++)
+ FOR_EACH_VOLUME(-1, volume)
{
struct dircache_volume *dcvolp = DCVOL(volume);
enum dircache_status volstatus = dcvolp->status;
@@ -2781,11 +2770,18 @@ void dircache_get_info(struct dircache_info *info)
/* report usage only if there is something ready or being built */
if (status != DIRCACHE_IDLE)
{
- info->reserve_used = reserve_buf_used();
info->size = dircache.size;
info->sizeused = dircache.sizeused;
+ info->reserve_used = reserve_buf_used();
info->entry_count = dircache.numentries;
}
+ else
+ {
+ info->size = 0;
+ info->sizeused = 0;
+ info->reserve_used = 0;
+ info->entry_count = 0;
+ }
dircache_unlock();
}
@@ -2817,7 +2813,7 @@ void dircache_dump(void)
dircache_runinfo.bufsize + 1);
/* CSV */
- fdprintf(fdcsv, "\"Index\",\"Serialnum\","
+ fdprintf(fdcsv, "\"Index\",\"Serialnum\",\"Serialhash\","
"\"Path\",\"Frontier\","
"\"Attribute\",\"File Size\","
"\"Mod Date\",\"Mod Time\"\n");
@@ -2833,11 +2829,12 @@ void dircache_dump(void)
get_volume_name(volume, name);
#endif
fdprintf(fdcsv,
- "%d,%lu,"
+ "%d," DC_SERIAL_FMT "," DC_SERIAL_FMT ","
"\"%c" IF_MV("%s") "\",%u,"
"0x%08X,0,"
"\"\",\"\"\n",
-volume-1, dcvolp->serialnum,
+ dc_hash_serialnum(dcvolp->serialnum, DC_SERHASH_START),
PATH_SEPCH, IF_MV(name,) dcvolp->frontier,
ATTR_DIRECTORY | ATTR_VOLUME);
}
@@ -2855,15 +2852,23 @@ void dircache_dump(void)
char buf[DC_MAX_NAME + 2];
*buf = '\0';
int idx = get_index(ce);
- get_path_sub(idx, buf, sizeof (buf));
+
+ struct get_path_sub_data data =
+ {
+ .buf = buf,
+ .size = sizeof (buf),
+ .serialhash = DC_SERHASH_START,
+ };
+
+ get_path_sub(idx, &data);
fdprintf(fdcsv,
- "%d,%lu,"
+ "%d," DC_SERIAL_FMT "," DC_SERIAL_FMT ","
"\"%s\",%u,"
"0x%08X,%lu,"
"%04d/%02d/%02d,"
"%02d:%02d:%02d\n",
- idx, ce->serialnum,
+ idx, ce->serialnum, data.serialhash,
buf, ce->frontier,
ce->attr, (ce->attr & ATTR_DIRECTORY) ?
0ul : (unsigned long)ce->filesize,
diff --git a/firmware/common/file.c b/firmware/common/file.c
index 6444918f1b..1f93824dc8 100644
--- a/firmware/common/file.c
+++ b/firmware/common/file.c
@@ -54,7 +54,7 @@ static struct filestr_desc * get_filestr(int fildes)
return file;
DEBUGF("fildes %d: bad file number\n", fildes);
- errno = (file && file->stream.flags == FV_NONEXIST) ? ENXIO : EBADF;
+ errno = (file && (file->stream.flags & FD_NONEXIST)) ? ENXIO : EBADF;
return NULL;
}
@@ -187,24 +187,28 @@ file_error:
return rc;
}
-/* callback for each file stream to make sure all data is in sync with new
- size */
-void ftruncate_internal_callback(struct filestr_base *stream,
- struct filestr_base *s)
+/* Handle syncing all file's streams to the truncation */
+static void handle_truncate(struct filestr_desc * const file, file_size_t size)
{
- struct filestr_desc *file = (struct filestr_desc *)s;
- file_size_t size = *file->sizep;
-
- /* caches with data beyond new extents are invalid */
- unsigned long sector = file->stream.cachep->sector;
- if (sector != INVALID_SECNUM && sector >= filesize_sectors(size))
- filestr_discard_cache(&file->stream);
+ unsigned long filesectors = filesize_sectors(size);
- /* keep all positions within bounds */
- if (file->offset > size)
- file->offset = size;
-
- (void)stream;
+ struct filestr_base *s = NULL;
+ while ((s = fileobj_get_next_stream(&file->stream, s)))
+ {
+ /* caches with data beyond new extents are invalid */
+ unsigned long sector = s->cachep->sector;
+ if (sector != INVALID_SECNUM && sector >= filesectors)
+ filestr_discard_cache(s);
+
+ /* files outside bounds must be rewound */
+ if (fat_query_sectornum(&s->fatstr) > filesectors)
+ fat_seek_to_stream(&s->fatstr, &file->stream.fatstr);
+
+ /* clip file offset too if needed */
+ struct filestr_desc *f = (struct filestr_desc *)s;
+ if (f->offset > size)
+ f->offset = size;
+ }
}
/* truncate the file to the specified length */
@@ -246,13 +250,17 @@ static int ftruncate_internal(struct filestr_desc *file, file_size_t size,
rc2 = fat_truncate(&file->stream.fatstr);
if (rc2 < 0)
FILE_ERROR(EIO, rc2 * 10 - 3);
+
+ /* never needs to be done this way again since any data beyond the
+ cached size is now gone */
+ fileobj_change_flags(&file->stream, 0, FO_TRUNC);
}
/* else just change the cached file size */
if (truncsize < cursize)
{
*file->sizep = truncsize;
- fileop_ontruncate_internal(&file->stream);
+ handle_truncate(file, truncsize);
}
/* if truncation was partially successful, it effectively destroyed
@@ -299,10 +307,6 @@ static int fsync_internal(struct filestr_desc *file)
int rc2 = ftruncate_internal(file, size, rc == 0);
if (rc2 < 0)
FILE_ERROR(ERRNO, rc2 * 10 - 2);
-
- /* never needs to be done this way again since any data beyond the
- cached size is now gone */
- fileobj_change_flags(&file->stream, 0, FO_TRUNC);
}
file_error:;
@@ -327,8 +331,7 @@ static int close_internal(struct filestr_desc *file)
/* call only when holding WRITER lock (updates directory entries) */
int rc;
- if ((file->stream.flags & FD_WRITE) &&
- !(fileobj_get_flags(&file->stream) & FO_REMOVED))
+ if ((file->stream.flags & (FD_WRITE|FD_NONEXIST)) == FD_WRITE)
{
rc = fsync_internal(file);
if (rc < 0)
@@ -786,6 +789,12 @@ int open_noiso_internal(const char *path, int oflag)
return open_internal_locked(path, oflag, FF_ANYTYPE | FF_NOISO);
}
+void force_close_writer_internal(struct filestr_base *stream)
+{
+ /* only we do writers so we know this is our guy */
+ close_internal((struct filestr_desc *)stream);
+}
+
/** POSIX **/
diff --git a/firmware/common/file_internal.c b/firmware/common/file_internal.c
index f9c6bfb570..aa0edb7ebb 100644
--- a/firmware/common/file_internal.c
+++ b/firmware/common/file_internal.c
@@ -292,7 +292,7 @@ struct pathwalk_component
struct pathwalk_component *nextp; /* parent if in use else next free */
};
-#define WALK_RC_NOT_FOUND 0 /* successfully not found */
+#define WALK_RC_NOT_FOUND 0 /* successfully not found (aid for file creation) */
#define WALK_RC_FOUND 1 /* found and opened */
#define WALK_RC_FOUND_ROOT 2 /* found and opened sys/volume root */
#define WALK_RC_CONT_AT_ROOT 3 /* continue at root level */
@@ -351,7 +351,10 @@ static int fill_path_compinfo(struct pathwalk *walkp,
compinfo->length = compp->length;
compinfo->attr = compp->attr;
compinfo->filesize = walkp->filesize;
- compinfo->parentinfo = (compp->nextp ?: compp)->info;
+ if (!(walkp->callflags & FF_SELFINFO))
+ compinfo->parentinfo = (compp->nextp ?: compp)->info;
+ else
+ compinfo->info = compp->info;
}
return rc;
@@ -393,7 +396,10 @@ static int walk_open_info(struct pathwalk *walkp,
else
callflags &= ~FO_DIRECTORY;
- fileop_onopen_internal(stream, &compp->info, callflags);
+ /* make open official if not simply probing for presence */
+ if (!(callflags & FF_PROBE))
+ fileop_onopen_internal(stream, &compp->info, callflags);
+
return compp->nextp ? WALK_RC_FOUND : WALK_RC_FOUND_ROOT;
}
@@ -511,7 +517,8 @@ walk_path(struct pathwalk *walkp, struct pathwalk_component *compp,
{
/* is ".." */
struct pathwalk_component *parentp = compp->nextp;
- if (!parentp)
+
+ if (!parentp IF_MV( || parentp->attr == ATTR_SYSTEM_ROOT ))
return WALK_RC_CONT_AT_ROOT;
compp->nextp = freep;
@@ -567,6 +574,9 @@ int open_stream_internal(const char *path, unsigned int callflags,
if (!compinfo)
callflags &= ~FF_CHECKPREFIX;
+ /* This lets it be passed quietly to directory scanning */
+ stream->flags = callflags & FF_MASK;
+
struct pathwalk walk;
walk.path = path;
walk.callflags = callflags;
@@ -575,7 +585,7 @@ int open_stream_internal(const char *path, unsigned int callflags,
struct pathwalk_component *rootp = pathwalk_comp_alloc(NULL);
rootp->nextp = NULL;
- rootp->attr = ATTR_DIRECTORY;
+ rootp->attr = ATTR_SYSTEM_ROOT;
#ifdef HAVE_MULTIVOLUME
int volume = 0, rootrc = WALK_RC_FOUND;
@@ -584,7 +594,7 @@ int open_stream_internal(const char *path, unsigned int callflags,
while (1)
{
const char *pathptr = walk.path;
-
+
#ifdef HAVE_MULTIVOLUME
/* this seamlessly integrates secondary filesystems into the
root namespace (e.g. "/<0>/../../<1>/../foo/." :<=> "/foo") */
@@ -596,8 +606,19 @@ int open_stream_internal(const char *path, unsigned int callflags,
FILE_ERROR(ENXIO, -2);
}
- /* the root of this subpath is the system root? */
- rootrc = p == pathptr ? WALK_RC_FOUND_ROOT : WALK_RC_FOUND;
+ if (p == pathptr)
+ {
+ /* the root of this subpath is the system root */
+ rootp->attr = ATTR_SYSTEM_ROOT;
+ rootrc = WALK_RC_FOUND_ROOT;
+ }
+ else
+ {
+ /* this subpath specifies a mount point */
+ rootp->attr = ATTR_MOUNT_POINT;
+ rootrc = WALK_RC_FOUND;
+ }
+
walk.path = p;
#endif /* HAVE_MULTIVOLUME */
@@ -633,11 +654,9 @@ int open_stream_internal(const char *path, unsigned int callflags,
default: /* utter, abject failure :`( */
DEBUGF("Open failed: rc=%d, errno=%d\n", rc, errno);
filestr_base_destroy(stream);
- FILE_ERROR(-rc, -2);
+ FILE_ERROR(-rc, -3);
}
- file_cache_reset(stream->cachep);
-
file_error:
return rc;
}
@@ -757,9 +776,10 @@ int test_stream_exists_internal(const char *path, unsigned int callflags)
{
/* only FF_* flags should be in callflags */
struct filestr_base stream;
- int rc = open_stream_internal(path, callflags, &stream, NULL);
- if (rc > 0)
- close_stream_internal(&stream);
+ int rc = open_stream_internal(path, callflags | FF_PROBE, &stream, NULL);
+
+ if (rc >= 0)
+ filestr_base_destroy(&stream);
return rc;
}
diff --git a/firmware/common/fileobj_mgr.c b/firmware/common/fileobj_mgr.c
index 8e7831d36c..e34a460e10 100644
--- a/firmware/common/fileobj_mgr.c
+++ b/firmware/common/fileobj_mgr.c
@@ -187,7 +187,8 @@ void fileobj_fileop_open(struct filestr_base *stream,
ll_insert_last(&fobp->list, &stream->node);
/* initiate the new stream into the enclave */
- stream->flags = FDO_BUSY | (callflags & (FD_WRITE|FD_WRONLY|FD_APPEND));
+ stream->flags = FDO_BUSY |
+ (callflags & (FF_MASK|FD_WRITE|FD_WRONLY|FD_APPEND));
stream->infop = &fobp->bind.info;
stream->fatstr.fatfilep = &fobp->bind.info.fatfile;
stream->bindp = &fobp->bind;
@@ -202,14 +203,6 @@ void fileobj_fileop_open(struct filestr_base *stream,
fobp->writers = 0;
fobp->size = 0;
- if (callflags & FD_WRITE)
- {
- /* first one is a writer */
- fobp->writers = 1;
- file_cache_init(&fobp->cache);
- filestr_assign_cache(stream, &fobp->cache);
- }
-
fileobj_bind_file(&fobp->bind);
}
else
@@ -225,32 +218,33 @@ void fileobj_fileop_open(struct filestr_base *stream,
DEBUGF("%s - FO_DIRECTORY flag does not match: %p %u\n",
__func__, stream, callflags);
}
+ }
- if (fobp->writers)
- {
- /* already writers present */
- fobp->writers++;
- filestr_assign_cache(stream, &fobp->cache);
- }
- else if (callflags & FD_WRITE)
- {
- /* first writer */
- fobp->writers = 1;
- file_cache_init(&fobp->cache);
- FOR_EACH_STREAM(FIRST, fobp, s)
- filestr_assign_cache(s, &fobp->cache);
- }
- /* else another reader */
+ if ((callflags & FD_WRITE) && ++fobp->writers == 1)
+ {
+ /* first writer */
+ file_cache_init(&fobp->cache);
+ FOR_EACH_STREAM(FIRST, fobp, s)
+ filestr_assign_cache(s, &fobp->cache);
+ }
+ else if (fobp->writers)
+ {
+ /* already writers present */
+ filestr_assign_cache(stream, &fobp->cache);
+ }
+ else
+ {
+ /* another reader and no writers present */
+ file_cache_reset(stream->cachep);
}
}
/* close the stream and free associated resources */
void fileobj_fileop_close(struct filestr_base *stream)
{
- switch (stream->flags)
+ if (!(stream->flags & FDO_BUSY))
{
- case 0: /* not added to manager */
- case FV_NONEXIST: /* forced-closed by unmounting */
+ /* not added to manager or forced-closed by unmounting */
filestr_base_destroy(stream);
return;
}
@@ -260,30 +254,30 @@ void fileobj_fileop_close(struct filestr_base *stream)
ll_remove(&fobp->list, &stream->node);
- if ((foflags & FO_SINGLE) || fobp->writers == 0)
+ if (foflags & FO_SINGLE)
{
- if (foflags & FO_SINGLE)
- {
- /* last stream for file; close everything */
- fileobj_unbind_file(&fobp->bind);
+ /* last stream for file; close everything */
+ fileobj_unbind_file(&fobp->bind);
- if (fobp->writers)
- file_cache_free(&fobp->cache);
+ if (fobp->writers)
+ file_cache_free(&fobp->cache);
- binding_add_to_free_list(fobp);
- }
+ binding_add_to_free_list(fobp);
}
- else if ((stream->flags & FD_WRITE) && --fobp->writers == 0)
+ else
{
- /* only readers remain; switch back to stream-local caching */
- FOR_EACH_STREAM(FIRST, fobp, s)
- filestr_copy_cache(s, &fobp->cache);
+ if ((stream->flags & FD_WRITE) && --fobp->writers == 0)
+ {
+ /* only readers remain; switch back to stream-local caching */
+ FOR_EACH_STREAM(FIRST, fobp, s)
+ filestr_copy_cache(s, &fobp->cache);
- file_cache_free(&fobp->cache);
- }
+ file_cache_free(&fobp->cache);
+ }
- if (!(foflags & FO_SINGLE) && fobp->list.head == fobp->list.tail)
- fobp->flags |= FO_SINGLE; /* only one open stream remaining */
+ if (fobp->list.head == fobp->list.tail)
+ fobp->flags |= FO_SINGLE; /* only one open stream remaining */
+ }
filestr_base_destroy(stream);
}
@@ -320,15 +314,10 @@ void fileobj_fileop_rename(struct filestr_base *stream,
/* informs manager than directory entries have been updated */
void fileobj_fileop_sync(struct filestr_base *stream)
{
- fileobj_sync_parent((const struct file_base_info *[]){ stream->infop }, 1);
-}
+ if (((struct fileobj_binding *)stream->bindp)->flags & FO_REMOVED)
+ return; /* no dir to sync */
-/* inform manager that file has been truncated */
-void fileobj_fileop_truncate(struct filestr_base *stream)
-{
- /* let caller update internal info */
- FOR_EACH_STREAM(FIRST, (struct fileobj_binding *)stream->bindp, s)
- ftruncate_internal_callback(stream, s);
+ fileobj_sync_parent((const struct file_base_info *[]){ stream->infop }, 1);
}
/* query for the pointer to the size storage for the file object */
@@ -340,6 +329,16 @@ file_size_t * fileobj_get_sizep(const struct filestr_base *stream)
return &((struct fileobj_binding *)stream->bindp)->size;
}
+/* iterate the list of streams for this stream's file */
+struct filestr_base * fileobj_get_next_stream(const struct filestr_base *stream,
+ const struct filestr_base *s)
+{
+ if (!stream->bindp)
+ return NULL;
+
+ return s ? STREAM_NEXT(s) : STREAM_FIRST((struct fileobj_binding *)stream->bindp);
+}
+
/* query manager bitflags for the file object */
unsigned int fileobj_get_flags(const struct filestr_base *stream)
{
@@ -349,20 +348,21 @@ unsigned int fileobj_get_flags(const struct filestr_base *stream)
return ((struct fileobj_binding *)stream->bindp)->flags;
}
-/* change manager bitflags for the file object */
+/* change manager bitflags for the file object (permitted only) */
void fileobj_change_flags(struct filestr_base *stream,
unsigned int flags, unsigned int mask)
{
struct fileobj_binding *fobp = (struct fileobj_binding *)stream->bindp;
- if (fobp)
- fobp->flags = (fobp->flags & ~mask) | (flags & mask);
+ if (!fobp)
+ return;
+
+ mask &= FDO_CHG_MASK;
+ fobp->flags = (fobp->flags & ~mask) | (flags & mask);
}
/* mark all open streams on a device as "nonexistant" */
void fileobj_mgr_unmount(IF_MV_NONVOID(int volume))
{
- /* right now, there is nothing else to be freed when marking a descriptor
- as "nonexistant" but a callback could be added if that changes */
FOR_EACH_VOLUME(volume, v)
{
struct fileobj_binding *fobp;
@@ -371,11 +371,17 @@ void fileobj_mgr_unmount(IF_MV_NONVOID(int volume))
struct filestr_base *s;
while ((s = STREAM_FIRST(fobp)))
{
+ /* last ditch effort to preserve FS integrity; we could still
+ be alive (soft unmount, maybe); we get informed early */
+ fileop_onunmount_internal(s);
+
+ if (STREAM_FIRST(fobp) == s)
+ fileop_onclose_internal(s); /* above didn't close it */
+
/* keep it "busy" to avoid races; any valid file/directory
descriptor returned by an open call should always be
- closed by whomever opened it (of course!) */
- fileop_onclose_internal(s);
- s->flags = FV_NONEXIST;
+ closed by whoever opened it (of course!) */
+ s->flags = (s->flags & ~FDO_BUSY) | FD_NONEXIST;
}
}
}
diff --git a/firmware/drivers/fat.c b/firmware/drivers/fat.c
index a090bd5899..fed3baffd4 100644
--- a/firmware/drivers/fat.c
+++ b/firmware/drivers/fat.c
@@ -1927,7 +1927,7 @@ static int free_direntries(struct bpb *fat_bpb, struct fat_file *file)
/* directory entry info is now gone */
file->dircluster = 0;
- file->e.entry = FAT_RW_VAL;
+ file->e.entry = FAT_DIRSCAN_RW_VAL;
file->e.entries = 0;
return 1;
@@ -2521,10 +2521,20 @@ void fat_rewind(struct fat_filestr *filestr)
filestr->lastcluster = filestr->fatfilep->firstcluster;
filestr->lastsector = 0;
filestr->clusternum = 0;
- filestr->sectornum = FAT_RW_VAL;
+ filestr->sectornum = FAT_FILE_RW_VAL;
filestr->eof = false;
}
+void fat_seek_to_stream(struct fat_filestr *filestr,
+ const struct fat_filestr *filestr_seek_to)
+{
+ filestr->lastcluster = filestr_seek_to->lastcluster;
+ filestr->lastsector = filestr_seek_to->lastsector;
+ filestr->clusternum = filestr_seek_to->clusternum;
+ filestr->sectornum = filestr_seek_to->sectornum;
+ filestr->eof = filestr_seek_to->eof;
+}
+
int fat_seek(struct fat_filestr *filestr, unsigned long seeksector)
{
const struct fat_file * const file = filestr->fatfilep;
@@ -2536,7 +2546,7 @@ int fat_seek(struct fat_filestr *filestr, unsigned long seeksector)
long cluster = file->firstcluster;
unsigned long sector = 0;
long clusternum = 0;
- unsigned long sectornum = FAT_RW_VAL;
+ unsigned long sectornum = FAT_FILE_RW_VAL;
#ifdef HAVE_FAT16SUPPORT
if (fat_bpb->is_fat16 && cluster < 0) /* FAT16 root dir */
@@ -2710,7 +2720,7 @@ int fat_readdir(struct fat_filestr *dirstr, struct fat_dirscan_info *scan,
dc_lock_cache();
- while (--scan->entry != FAT_RW_VAL) /* at beginning? */
+ while (--scan->entry != FAT_DIRSCAN_RW_VAL) /* at beginning? */
{
ent = cache_direntry(fat_bpb, dirstr, scan->entry);
@@ -2761,7 +2771,7 @@ fat_error:
void fat_rewinddir(struct fat_dirscan_info *scan)
{
/* rewind the directory scan counter to the beginning */
- scan->entry = FAT_RW_VAL;
+ scan->entry = FAT_DIRSCAN_RW_VAL;
scan->entries = 0;
}
diff --git a/firmware/export/fat.h b/firmware/export/fat.h
index 3aa1e254dc..963c1fe767 100644
--- a/firmware/export/fat.h
+++ b/firmware/export/fat.h
@@ -97,13 +97,15 @@ struct fat_direntry
/* cursor structure used for scanning directories; holds the last-returned
entry information */
+#define FAT_DIRSCAN_RW_VAL (0u - 1u)
+
struct fat_dirscan_info
{
unsigned int entry; /* short dir entry index in parent */
unsigned int entries; /* number of dir entries used */
};
-#define FAT_RW_VAL (0u - 1)
+#define FAT_FILE_RW_VAL (0ul - 1ul)
/* basic FAT file information about where to find a file and who houses it */
struct fat_file
@@ -156,6 +158,8 @@ long fat_readwrite(struct fat_filestr *filestr, unsigned long sectorcount,
void *buf, bool write);
void fat_rewind(struct fat_filestr *filestr);
int fat_seek(struct fat_filestr *filestr, unsigned long sector);
+void fat_seek_to_stream(struct fat_filestr *filestr,
+ const struct fat_filestr *filestr_seek_to);
int fat_truncate(const struct fat_filestr *filestr);
/** Directory stream functions **/
diff --git a/firmware/include/dircache.h b/firmware/include/dircache.h
index 7e8c764e7f..d73c6f6e81 100644
--- a/firmware/include/dircache.h
+++ b/firmware/include/dircache.h
@@ -57,6 +57,15 @@
figure pessimistic */
typedef uint32_t dc_serial_t;
+/* these should agree with size of dc_serial_t */
+#define DC_SERHASH_START 0xffffffff
+
+/* I was originally using FNV hash but decided this is probably okay
+ (for now) */
+#define dc_hash_serialnum(s, h) \
+ ({ dc_serial_t __x = (s); crc_32(&(__x), sizeof(dc_serial_t), (h)); })
+#define DC_SERIAL_FMT "0x%08lX"
+
/**
****************************************************************************/
@@ -132,10 +141,33 @@ void dircache_fileop_sync(struct file_base_binding *infop,
const struct dirinfo_native *dinp);
-/** Dircache paths and files **/
-ssize_t dircache_get_path(const struct dircache_file *dcfilep, char *buf,
- size_t size);
-int dircache_get_file(const char *path, struct dircache_file *dcfilep);
+/** Dircache paths, files and shortcuts **/
+struct dircache_fileref
+{
+ struct dircache_file dcfile;
+ dc_serial_t serialhash; /* Hash of serialnumbers to root */
+};
+
+void dircache_fileref_init(struct dircache_fileref *dcfrefp);
+ssize_t dircache_get_fileref_path(const struct dircache_fileref *dcfrefp,
+ char *buf, size_t size);
+
+/* Bitflags for dircache_search() */
+enum dircache_search_flags
+{
+ DCS_FILEREF = 0x01, /* Check fileref existence and serial number */
+ _DCS_VERIFY_FLAG = 0x02, /* Internal: Only valid with DCS_FILEREF */
+ DCS_FILEREF_VERIFY = 0x03, /* Do DCS_FILEREF check + verify serial hash */
+ DCS_CACHED_PATH = 0x04, /* Check only cache for provided path */
+ _DCS_STORAGE_FLAG = 0x08, /* Internal: Only valid with DCS_CACHED_PATH */
+ DCS_STORAGE_PATH = 0x0c, /* Read-through if needed for provided path */
+ DCS_UPDATE_FILEREF = 0x10, /* If fileref is not valid but path is found or
+ searching a path, update the reference
+ information */
+};
+
+int dircache_search(unsigned int flags, struct dircache_fileref *dcfrefp,
+ const char *path);
/** Debug screen/info stuff **/
diff --git a/firmware/include/dircache_redirect.h b/firmware/include/dircache_redirect.h
index 15fb4bc38d..9fae16b551 100644
--- a/firmware/include/dircache_redirect.h
+++ b/firmware/include/dircache_redirect.h
@@ -24,6 +24,8 @@
/***
** Internal redirects that depend upon whether or not dircache is made
+ **
+ ** Some stuff deals with it, some doesn't right now. This is a nexus point..
**/
/** File binding **/
@@ -119,11 +121,6 @@ static inline void fileop_onsync_internal(struct filestr_base *stream)
#endif
}
-static inline void fileop_ontruncate_internal(struct filestr_base *stream)
-{
- fileobj_fileop_truncate(stream);
-}
-
static inline void volume_onmount_internal(IF_MV_NONVOID(int volume))
{
#ifdef HAVE_DIRCACHE
@@ -134,10 +131,20 @@ static inline void volume_onmount_internal(IF_MV_NONVOID(int volume))
static inline void volume_onunmount_internal(IF_MV_NONVOID(int volume))
{
- fileobj_mgr_unmount(IF_MV(volume));
#ifdef HAVE_DIRCACHE
+ /* First, to avoid update of something about to be destroyed anyway */
dircache_unmount(IF_MV(volume));
#endif
+ fileobj_mgr_unmount(IF_MV(volume));
+}
+
+static inline void fileop_onunmount_internal(struct filestr_base *stream)
+{
+
+ if (stream->flags & FD_WRITE)
+ force_close_writer_internal(stream); /* try to save stuff */
+ else
+ fileop_onclose_internal(stream); /* just readers, bye */
}
diff --git a/firmware/include/file_internal.h b/firmware/include/file_internal.h
index d1bb67406a..acec81206e 100644
--- a/firmware/include/file_internal.h
+++ b/firmware/include/file_internal.h
@@ -81,6 +81,7 @@
#define ATTR_NEW_FILE (ATTR_ARCHIVE)
#define ATTR_NEW_DIRECTORY (ATTR_DIRECTORY)
+#define ATTR_SYSTEM_ROOT (ATTR_SYSROOT | ATTR_DIRECTORY)
#define ATTR_MOUNT_POINT (ATTR_VOLUME | ATTR_DIRECTORY)
/** File sector cache **/
@@ -110,33 +111,35 @@ void file_cache_free(struct filestr_cache *cachep);
enum fildes_and_obj_flags
{
/* used in descriptor and common */
- FDO_BUSY = 0x0001, /* descriptor/object is in use */
+ 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 */
+ FD_WRITE = 0x0002, /* descriptor has write mode */
+ FD_WRONLY = 0x0004, /* descriptor is write mode only */
+ FD_APPEND = 0x0008, /* descriptor is append mode */
+ FD_NONEXIST = 0x8000, /* closed but not freed (uncombined) */
/* 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 */
+ 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 */
+ FDO_CHG_MASK = FO_TRUNC, /* fileobj permitted external change */
+ /* bitflags that instruct various 'open' functions how to behave;
+ * saved in stream flags (only) but not used by manager */
+ FF_FILE = 0x00000000, /* expect file; accept file only */
+ FF_DIR = 0x00010000, /* expect dir; accept dir only */
+ FF_ANYTYPE = 0x00020000, /* succeed if either file or dir */
+ FF_TYPEMASK = 0x00030000, /* mask of typeflags */
+ FF_CREAT = 0x00040000, /* create if file doesn't exist */
+ FF_EXCL = 0x00080000, /* fail if creating and file exists */
+ FF_CHECKPREFIX = 0x00100000, /* detect if file is prefix of path */
+ FF_NOISO = 0x00200000, /* do not decode ISO filenames to UTF-8 */
+ FF_PROBE = 0x00400000, /* only test existence; don't open */
+ FF_CACHEONLY = 0x00800000, /* succeed only if in dircache */
+ FF_SELFINFO = 0x01000000, /* return info on self as well */
+ FF_MASK = 0x01ff0000,
};
-
/** Common data structures used throughout **/
/* basic file information about its location */
@@ -183,8 +186,7 @@ struct dirscan_info
struct filestr_base
{
struct ll_node node; /* list item node (first!) */
- uint16_t flags; /* FD_* bits of this stream */
- uint16_t unused; /* not used */
+ uint32_t flags; /* F[DF]_* bits of this stream */
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 */
@@ -235,17 +237,25 @@ static inline void filestr_unlock(struct filestr_base *stream)
#define FILESTR_UNLOCK(type, stream) \
({ if (FILESTR_##type) filestr_unlock(stream); })
-#define ATTR_PREFIX (0x8000) /* out of the way of all ATTR_* bits */
+/* auxilliary attributes - out of the way of regular ATTR_* bits */
+#define ATTR_SYSROOT (0x8000)
+#define ATTR_PREFIX (0x4000)
/* structure to return detailed information about what you opened */
struct path_component_info
{
- const char *name; /* pointer to name within 'path' */
+ const char *name; /* pointer to name within 'path' (OUT) */
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 */
+ struct file_base_info *prefixp; /* base info to check as prefix
+ (IN if FF_CHECKPREFIX) */
+ union {
+ struct file_base_info parentinfo; /* parent directory base info of file
+ (if not FF_SELFINFO) */
+ struct file_base_info info; /* base info of file itself
+ (if FF_SELFINFO) */
+ };
};
int open_stream_internal(const char *path, unsigned int callflags,
@@ -261,6 +271,7 @@ int remove_stream_internal(const char *path, struct filestr_base *stream,
int test_stream_exists_internal(const char *path, unsigned int callflags);
int open_noiso_internal(const char *path, int oflag); /* file.c */
+void force_close_writer_internal(struct filestr_base *stream); /* file.c */
struct dirent;
int uncached_readdir_dirent(struct filestr_base *stream,
@@ -326,22 +337,26 @@ static inline void file_internal_unlock_WRITER(void)
* not in the macro
*/
+#define FILE_SET_CODE(_name, _keepcode, _value) \
+ ({ __builtin_constant_p(_value) ? \
+ ({ if ((_value) != (_keepcode)) _name = (_value); }) : \
+ ({ _name = (_value); }); })
+
/* 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); }); \
+ ({ FILE_SET_CODE(errno, ERRNO, (_errno)); \
+ FILE_SET_CODE(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; })
+ ({ FILE_SET_CODE(errno, ERRNO, _errno); \
+ return _rc; })
+
+/* set errno and return code, no branching */
+#define FILE_ERROR_SET(_errno, _rc) \
+ ({ FILE_SET_CODE(errno, ERRNO, (_errno)); \
+ FILE_SET_CODE(rc, RC, (_rc)); })
/** Misc. stuff **/
diff --git a/firmware/include/fileobj_mgr.h b/firmware/include/fileobj_mgr.h
index c90a59bea0..627d2df341 100644
--- a/firmware/include/fileobj_mgr.h
+++ b/firmware/include/fileobj_mgr.h
@@ -41,12 +41,11 @@ void fileobj_fileop_rename(struct filestr_base *stream,
void fileobj_fileop_remove(struct filestr_base *stream,
const struct file_base_info *oldinfop);
void fileobj_fileop_sync(struct filestr_base *stream);
-void fileobj_fileop_truncate(struct filestr_base *stream);
-extern void ftruncate_internal_callback(struct filestr_base *stream,
- struct filestr_base *s);
file_size_t * fileobj_get_sizep(const struct filestr_base *stream);
unsigned int fileobj_get_flags(const struct filestr_base *stream);
+struct filestr_base * fileobj_get_next_stream(const struct filestr_base *stream,
+ const struct filestr_base *s);
void fileobj_change_flags(struct filestr_base *stream,
unsigned int flags, unsigned int mask);
void fileobj_mgr_unmount(IF_MV_NONVOID(int volume));