gint/src/fs/fugue/fugue_open.c
2025-04-12 08:47:17 +02:00

180 lines
4.1 KiB
C

#include <gint/fs.h>
#include <gint/bfile.h>
#include <gint/gint.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include "util.h"
#include "fugue.h"
#include "bfilecp.h"
GUNUSED static int new_file_size;
int fugue_open(char const *path, int flags, GUNUSED mode_t mode)
{
ENOTSUP_IF_NOT_FUGUE(-1);
uint16_t *fcpath = fs_path_normalize_fc(path);
int fugue_fd, rc=-1, type, fd=-1;
if(!fcpath) {
errno = ENOMEM;
return -1;
}
#if GINT_OS_CP
char *normpath = fs_path_normalize_opt(path, "\\fls0\\", '\\');
if(!normpath) {
errno = ENOMEM;
free(fcpath);
return -1;
}
# define NORMALIZED_PATH normpath
#else
# define NORMALIZED_PATH fcpath
#endif
/* Open mode */
int bfile_mode = BFile_ReadOnly;
if(flags & O_WRONLY)
bfile_mode = BFile_WriteOnly;
else if(flags & O_RDWR)
bfile_mode = BFile_ReadWrite;
#if GINT_OS_CP
if(flags & O_APPEND)
bfile_mode |= BFile_AppendFlag;
#endif
gint_world_enter(GINT_WORLD_OS);
/* 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 GINT_OS_CP
if(!memcmp(fcpath, u"\\fls0\\", 14)) {
#else
if(!memcmp(fcpath, u"\\\\fls0\\", 16)) {
#endif
exists = true;
type = BFile_Type_Directory;
}
else {
exists = (BFile_Ext_Stat(fcpath, &type, NULL) == 0);
if(!exists) type = -1;
}
/* O_EXCL: Succeed *only* if we're creating the file. Only has an
effect when combined with O_CREAT, unsurprisingly. */
if(exists && (flags & O_EXCL) && (flags & O_CREAT)) {
errno = EEXIST;
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);
rc = -1;
goto end;
}
/* If the entry is a directory, open it as such */
if(type == BFile_Type_Directory) {
void *dp = fugue_dir_explore(path);
if(!dp) {
rc = -1;
goto end;
}
fs_descriptor_t data = {
.type = &fugue_dir_descriptor_type,
.data = dp,
};
fd = fs_create_descriptor(&data);
rc = fd;
goto end;
}
/* We now have a regular file. Before opening it, we may need to:
1. Delete it to truncate it (BFile never truncates AFAIK)
- If O_TRUNC and the file exists (obviously).
- Deleting and recreating for truncation purposes is allowed when
not setting O_CREAT. In case of error midway, good luck.
2. Create it (except fx-CP 400 which can create in BFile_Open())
- If it didn't exist and O_CREAT was allowed; or
- if it did exist but was removed for truncation in step 1.
3. Finally, open the file. */
/* Delete for truncation. */
if((flags & O_TRUNC) && exists) {
BFile_Remove(NORMALIZED_PATH);
/* Whatever happens now, allow recreating the file. */
exists = false;
flags |= O_CREAT;
}
/* Create the file now, either directly with BFile_Create(), or with
the appropriate flag on fx-CP 400. */
if((flags & O_CREAT) && !exists) {
#if GINT_OS_CP
bfile_mode |= BFile_CreateFlag;
#else
int err = BFile_Create(fcpath, BFile_File, &new_file_size);
if(err < 0) {
errno = bfile_error_to_errno(err);
rc = -1;
goto end;
}
#endif
}
/* Proceed with the final BFile_Open() call. */
fugue_fd = BFile_Open(NORMALIZED_PATH, bfile_mode);
if(fugue_fd < 0) {
errno = bfile_error_to_errno(fugue_fd);
rc = -1;
goto end;
}
/* If O_APPEND is set, move to the end of the file */
// TODO: O_APPEND should move the cursor before *each* write
int pos = 0;
#if !GINT_OS_CP
if((flags & O_APPEND)) {
pos = BFile_Size(fugue_fd);
BFile_Seek(fugue_fd, pos);
}
#endif
/* Return the now-open file descriptor */
fugue_fd_t *data = malloc(sizeof *data);
if(!data) {
BFile_Close(fugue_fd);
goto end;
}
data->fd = fugue_fd;
data->pos = pos;
fs_descriptor_t fd_data = {
.type = &fugue_descriptor_type,
.data = data,
};
rc = fd = fs_create_descriptor(&fd_data);
if(fd == -1) {
BFile_Close(fugue_fd);
free(data);
errno = ENFILE;
goto end;
}
end:
gint_world_leave();
#if GINT_OS_CP
free(normpath);
#endif
free(fcpath);
return rc;
}