fs: folder support, part 2 (path normalization and root)

This commit is contained in:
Lephe 2021-12-23 01:19:45 +01:00
parent 9cae0040b5
commit aed90d9b3c
No known key found for this signature in database
GPG key ID: 1BBA026E13FC0495
7 changed files with 177 additions and 19 deletions

View file

@ -87,6 +87,26 @@ void fs_free_descriptor(int fd);
itself. */
int open_generic(fs_descriptor_type_t *type, void *data, int reuse_fd);
/* fs_path_normalize(): Normalize a path to eliminate ., .. and redundant /
This function creates a copy of the specified path which is an absolute path
with redundant / removed and . and .. components resolved. For instance:
"" -> "/"
"/subfolder/./" -> "/subfolder"
"/../subfolder/../subfolder" -> "/subfolder"
"/../../x/y/../t" -> "/x/t"
The new string is created with malloc() and should be free()'d after use. */
char *fs_path_normalize(char const *path);
/* fs_path_normalize_fc(): Normalize a path and translate it to FONTCHARACTER
This function is similar to fs_path_normalize(), but it creates a 16-bit
string that starts with \\fls0\ rather than /, and can be used in direct
calls to BFile. */
uint16_t *fs_path_normalize_fc(char const *path);
#ifdef __cplusplus
}
#endif

View file

@ -6,6 +6,7 @@
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include "util.h"
#include "fugue.h"
@ -63,7 +64,8 @@ off_t fugue_dir_lseek(void *data, off_t offset, int whence)
if(whence == SEEK_END)
offset += dp->count;
if(offset < 0 || offset >= dp->count) {
/* dp->count, being at the end of the enumeration, is a valid offset */
if(offset < 0 || offset >= dp->count + 1) {
errno = EINVAL;
return -1;
}
@ -97,6 +99,7 @@ const fs_descriptor_type_t fugue_dir_descriptor_type = {
void *fugue_dir_explore(char const *path)
{
struct BFile_FileInfo info;
char *wildcard=NULL;
uint16_t *fc_path=NULL, *search=NULL;
dir_t *dp = malloc(sizeof *dp);
@ -111,7 +114,12 @@ void *fugue_dir_explore(char const *path)
fc_path = malloc(512 * sizeof *fc_path);
if(!fc_path) goto alloc_failure;
search = utf8_to_fc_alloc(u"\\\\fls0\\", path, u"\\*");
wildcard = malloc(strlen(path) + 3);
if(!wildcard) goto alloc_failure;
strcpy(wildcard, path);
strcat(wildcard, "/*");
search = fs_path_normalize_fc(wildcard);
if(!search) goto alloc_failure;
rc = BFile_FindFirst(search, &sd, fc_path, &info);
@ -142,6 +150,8 @@ void *fugue_dir_explore(char const *path)
if(info.type == BFile_Type_File)
ent->d_type = DT_REG;
if(info.type == BFile_Type_Archived)
ent->d_type = DT_REG;
if(info.type == BFile_Type_Directory)
ent->d_type = DT_DIR;
if(info.type == BFile_Type_Dot)
@ -160,6 +170,7 @@ alloc_failure:
errno = ENOMEM;
fugue_dir_close(dp);
end:
free(wildcard);
free(search);
free(fc_path);
if(sd >= 0)

View file

@ -1,5 +1,6 @@
#include <gint/hardware.h>
#include <gint/bfile.h>
#include <gint/fs.h>
#include <errno.h>
#include "util.h"
@ -7,7 +8,7 @@ int fugue_mkdir(char const *path, GUNUSED mode_t mode)
{
ENOTSUP_IF_NOT_FUGUE(-1);
uint16_t *fcpath = utf8_to_fc_alloc(u"\\\\fls0\\", path, NULL);
uint16_t *fcpath = fs_path_normalize_fc(path);
if(!fcpath) {
errno = ENOMEM;
return -1;
@ -16,8 +17,10 @@ int fugue_mkdir(char const *path, GUNUSED mode_t mode)
int rc = BFile_Create(fcpath, BFile_Folder, NULL);
if(rc < 0) {
errno = bfile_error_to_errno(rc);
free(fcpath);
return -1;
}
free(fcpath);
return 0;
}

View file

@ -2,6 +2,7 @@
#include <gint/bfile.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include "util.h"
#include "fugue.h"
@ -11,7 +12,7 @@ int fugue_open(char const *path, int flags, GUNUSED mode_t mode)
{
ENOTSUP_IF_NOT_FUGUE(-1);
uint16_t *fcpath = utf8_to_fc_alloc(u"\\\\fls0\\", path, NULL);
uint16_t *fcpath = fs_path_normalize_fc(path);
int fugue_fd, err, rc=-1, type, fd=-1;
if(!fcpath) {
@ -31,21 +32,32 @@ int fugue_open(char const *path, int flags, GUNUSED mode_t mode)
/* Truncation requires the file to be removed/recreated */
bool trunc = (flags & O_TRUNC) && (flags & O_CREAT);
/* Stat the entry */
bool exists = (BFile_Ext_Stat(fcpath, &type, NULL) == 0);
if(!exists) type = -1;
/* Stat the entry. A special case is needed for the root, which doesn't
respond well. fs_path_normalize_fc() normalizes the path so we just
have to check for the fixed string "\\fls0\". */
bool exists;
if(!memcmp(fcpath, u"\\\\fls0\\", 16)) {
exists = true;
type = BFile_Type_Directory;
}
else {
exists = (BFile_Ext_Stat(fcpath, &type, NULL) == 0);
if(!exists) type = -1;
}
/* If the entry exists and O_EXCL was requested, fail. */
if(exists && excl) {
errno = EEXIST;
return -1;
rc = -1;
goto end;
}
/* If the entry is not a directory but O_DIRECTORY is set, fail. If the
directory doesn't exist, we fail regardless of O_CREAT. */
if((flags & O_DIRECTORY) && type != BFile_Type_Directory) {
errno = (exists ? ENOTDIR : ENOENT);
return -1;
rc = -1;
goto end;
}
/* If the entry is a directory, open it as such */
@ -83,6 +95,7 @@ int fugue_open(char const *path, int flags, GUNUSED mode_t mode)
err = BFile_Create(fcpath, BFile_File, &size);
if(err < 0) {
errno = bfile_error_to_errno(err);
rc = -1;
goto end;
}
fugue_fd = BFile_Open(fcpath, bfile_mode);
@ -104,7 +117,7 @@ int fugue_open(char const *path, int flags, GUNUSED mode_t mode)
.type = &fugue_descriptor_type,
.data = (void *)fugue_fd,
};
fd = fs_create_descriptor(&data);
rc = fd = fs_create_descriptor(&data);
if(fd == -1) {
BFile_Close(fugue_fd);
@ -112,7 +125,6 @@ int fugue_open(char const *path, int flags, GUNUSED mode_t mode)
goto end;
}
rc = fd;
end:
free(fcpath);
return rc;

View file

@ -1,5 +1,6 @@
#include <gint/hardware.h>
#include <gint/bfile.h>
#include <gint/fs.h>
#include <errno.h>
#include "util.h"
@ -7,7 +8,7 @@ int fugue_unlink(char const *path)
{
ENOTSUP_IF_NOT_FUGUE(-1);
uint16_t *fcpath = utf8_to_fc_alloc(u"\\\\fls0\\", path, NULL);
uint16_t *fcpath = fs_path_normalize_fc(path);
if(!fcpath) {
errno = ENOMEM;
return -1;
@ -16,9 +17,11 @@ int fugue_unlink(char const *path)
int err = BFile_Remove(fcpath);
if(err < 0) {
errno = bfile_error_to_errno(err);
free(fcpath);
return -1;
}
free(fcpath);
return 0;
}

View file

@ -2,6 +2,7 @@
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdarg.h>
#include <gint/bfile.h>
int bfile_error_to_errno(int e)
@ -52,7 +53,7 @@ void utf8_to_fc(uint16_t *fc, char const *utf8, size_t fc_len)
fc[j++] = utf8[i];
}
while(j < fc_len)
if(j < fc_len)
fc[j++] = 0;
}
@ -67,7 +68,7 @@ void fc_to_utf8(char *utf8, uint16_t const *fc, size_t utf8_len)
utf8[j++] = fc[i];
}
while(j < utf8_len)
if(j < utf8_len)
utf8[j++] = 0;
}
@ -106,3 +107,111 @@ char *fc_to_utf8_alloc(uint16_t const *fc)
fc_to_utf8(utf8, fc, len+1);
return utf8;
}
/* Split a path into components. Only the top array needs to be freed */
char **fs_split_components(char *path, int *count)
{
char **comps = NULL;
*count = 0;
int allocated = 0;
char *token = strtok(path, "/");
while(token) {
if(*count + 1 > allocated) {
allocated += 8;
char **new_comps = realloc(comps,
allocated * sizeof *comps);
if(!new_comps) {
*count = -1;
return NULL;
}
comps = new_comps;
}
comps[*count] = token;
*count += 1;
token = strtok(NULL, "/");
}
return comps;
}
/* Generalization of fs_path_normalize(). Returns a [char *] if use_fc=false,
otherwise returns an [uint16_t *]. */
static void *path_normalize(char const *path, bool use_fc)
{
char *path_dup = strdup(path);
if(!path_dup) return NULL;
int components=0;
char **comps = fs_split_components(path_dup, &components);
if(components == -1) {
free(path_dup);
return NULL;
}
/* Now rewrite comps[] to skip "." and apply ".." */
int wr = 0;
for(int rd=0; rd < components; rd++) {
char *comp = comps[rd];
if(!strcmp(comp, "."))
continue;
else if(!strcmp(comp, ".."))
wr -= (wr > 0);
else
comps[wr++] = comp;
}
/* Count total length */
int length = (use_fc ? 7 : 1) + (wr >= 1 ? wr - 1 : 0);
for(int i = 0; i < wr; i++) {
length += utf8_len(comps[i]);
}
/* Allocate string then copy */
if(use_fc) {
uint16_t *fc = malloc((length + 1) * sizeof *fc);
uint16_t *fc_init = fc;
memcpy(fc, u"\\\\fls0\\", 7*2);
fc += 7;
for(int i = 0; i < wr; i++) {
if(i > 0) *fc++ = '\\';
utf8_to_fc(fc, comps[i], (size_t)-1);
fc += utf8_len(comps[i]);
}
*fc++ = 0;
free(path_dup);
free(comps);
return fc_init;
}
else {
char *utf8 = malloc(length + 1);
char *utf8_init = utf8;
*utf8++ = '/';
for(int i = 0; i < wr; i++) {
if(i > 0) *utf8++ = '/';
strcpy(utf8, comps[i]);
utf8 += utf8_len(comps[i]);
}
*utf8++ = 0;
free(path_dup);
free(comps);
return utf8_init;
}
}
char *fs_path_normalize(char const *path)
{
return path_normalize(path, false);
}
uint16_t *fs_path_normalize_fc(char const *path)
{
return path_normalize(path, true);
}

View file

@ -14,7 +14,7 @@ extern "C" {
if(gint[HWFS] != HWFS_FUGUE) { errno = ENOTSUP; return (rc); }
/* Translate common BFile error codes to errno values. */
extern int bfile_error_to_errno(int bfile_error_code);
int bfile_error_to_errno(int bfile_error_code);
/* TODO: These functions do not actually translate special characters between
encodings, they simply strip them. */
@ -26,17 +26,17 @@ size_t fc_len(uint16_t const *fc);
/* Convert UTF-8 to FONTCHARACTER; outputs fc_len characters with padding. If
fc[fc_len-1] is not 0 after the call, then fc is too short. */
extern void utf8_to_fc(uint16_t *fc, char const *utf8, size_t fc_len);
void utf8_to_fc(uint16_t *fc, char const *utf8, size_t fc_len);
/* Same in the other direction. */
extern void fc_to_utf8(char *utf8, uint16_t const *fc, size_t utf8_len);
void fc_to_utf8(char *utf8, uint16_t const *fc, size_t utf8_len);
/* Same as utf8_to_fc() but allocates a string with malloc(). */
extern uint16_t *utf8_to_fc_alloc(uint16_t *prefix, char const *utf8,
uint16_t *utf8_to_fc_alloc(uint16_t *prefix, char const *utf8,
uint16_t *suffix);
/* Same as fc_to_utf8() but allocates a string with malloc(). */
extern char *fc_to_utf8_alloc(uint16_t const *fc);
char *fc_to_utf8_alloc(uint16_t const *fc);
#ifdef __cplusplus
}