diff --git a/Makefile.am b/Makefile.am index 1398ec3..90830a9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -14,6 +14,7 @@ src_libisofs_la_SOURCES = \ src/node.c \ src/image.h \ src/fsource.h \ + src/fsource.c \ src/fs_local.c \ src/stream.h libinclude_HEADERS = \ diff --git a/TODO b/TODO index fc794bc..ea5140b 100644 --- a/TODO +++ b/TODO @@ -8,6 +8,7 @@ TODO #00001 (tree.h) -> consider adding new timestamps to IsoTreeNode #00002 (tree.h) -> handle deletion of each kind of node #00003 make error.h header public +#00004 (fsource-h) -> Add a get_mime_type() function. FIXME ===== diff --git a/src/error.h b/src/error.h index 306e6ea..f646837 100644 --- a/src/error.h +++ b/src/error.h @@ -14,8 +14,23 @@ */ #define ISO_SUCCESS 1 -#define ISO_NULL_POINTER -1 +#define ISO_ERROR -1 +#define ISO_NULL_POINTER -2 +#define ISO_OUT_OF_MEM -3 +#define ISO_MEM_ERROR -4 +#define ISO_INTERRUPTED -5 +#define ISO_WRONG_ARG_VALUE -6 +#define ISO_FILE_ERROR -100 +#define ISO_FILE_ALREADY_OPENNED -101 +#define ISO_FILE_ACCESS_DENIED -102 +#define ISO_FILE_BAD_PATH -103 +#define ISO_FILE_DOESNT_EXIST -104 +#define ISO_FILE_NOT_OPENNED -105 +#define ISO_FILE_IS_DIR -106 +#define ISO_FILE_READ_ERROR -107 +#define ISO_FILE_IS_NOT_DIR -108 +#define ISO_FILE_IS_NOT_SYMLINK -109 #endif /*LIBISO_ERROR_H_*/ diff --git a/src/fs_local.c b/src/fs_local.c index d335439..960d412 100644 --- a/src/fs_local.c +++ b/src/fs_local.c @@ -6,10 +6,354 @@ * published by the Free Software Foundation. See COPYING file for details. */ - + /* * Filesystem/FileSource implementation to access the local filesystem. */ #include "fsource.h" +#include "error.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct +{ + /* IsoFilesystem *fs; It seems not needed */ + + char *path; + unsigned int openned:2; /* 0: not openned, 1: file, 2:dir */ + union { + int fd; + DIR *dir; + } info; +} _LocalFsFileSource; + +static +const char* lfs_get_path(IsoFileSource *src) +{ + _LocalFsFileSource *data; + data = src->data; + return data->path; +} + +static +int lfs_lstat(IsoFileSource *src, struct stat *info) +{ + _LocalFsFileSource *data; + + if (src == NULL || info == NULL) { + return ISO_NULL_POINTER; + } + data = src->data; + + if (lstat(data->path, info) != 0) { + int err; + + /* error, choose an appropriate return code */ + switch(errno) { + case EACCES: + err = ISO_FILE_ACCESS_DENIED; + break; + case ENOTDIR: + case ENAMETOOLONG: + case ELOOP: + err = ISO_FILE_BAD_PATH; + break; + case ENOENT: + err = ISO_FILE_DOESNT_EXIST; + break; + case EFAULT: + case ENOMEM: + err = ISO_MEM_ERROR; + break; + default: + err = ISO_FILE_ERROR; + break; + } + return err; + } + return ISO_SUCCESS; +} + +static +int lfs_open(IsoFileSource *src) +{ + int err; + struct stat info; + _LocalFsFileSource *data; + + if (src == NULL) { + return ISO_NULL_POINTER; + } + + data = src->data; + if (data->openned) { + return ISO_FILE_ALREADY_OPENNED; + } + + /* is a file or a dir ? */ + err = lfs_lstat(src, &info); + if (err < 0) { + return err; + } + + if (S_ISDIR(info.st_mode)) { + data->info.dir = opendir(data->path); + data->openned = data->info.dir ? 2 : 0; + } else { + data->info.fd = open(data->path, O_RDONLY); + data->openned = data->info.fd != -1 ? 1 : 0; + } + + /* + * check for possible errors, note that many of possible ones are + * parsed in the lstat call above + */ + if (data->openned == 0) { + switch(errno) { + case EACCES: + err = ISO_FILE_ACCESS_DENIED; + break; + case EFAULT: + case ENOMEM: + err = ISO_MEM_ERROR; + break; + default: + err = ISO_FILE_ERROR; + break; + } + return err; + } + + return ISO_SUCCESS; +} + +static +int lfs_close(IsoFileSource *src) +{ + _LocalFsFileSource *data; + + if (src == NULL) { + return ISO_NULL_POINTER; + } + + data = src->data; + switch(data->openned) { + case 1: /* not dir */ + return close(data->info.fd) == 0 ? ISO_SUCCESS : ISO_FILE_ERROR; + case 2: /* directory */ + return closedir(data->info.dir) == 0 ? ISO_SUCCESS : ISO_FILE_ERROR; + default: + return ISO_FILE_NOT_OPENNED; + } +} + +static +int lfs_read(IsoFileSource *src, void *buf, size_t count) +{ + _LocalFsFileSource *data; + + if (src == NULL || buf == NULL) { + return ISO_NULL_POINTER; + } + + data = src->data; + switch(data->openned) { + case 1: /* not dir */ + { + int ret; + ret = read(data->info.fd, buf, count); + if (ret < 0) { + /* error on read */ + switch(errno) { + case EINTR: + ret = ISO_INTERRUPTED; + break; + case EFAULT: + ret = ISO_MEM_ERROR; + break; + case EIO: + ret = ISO_FILE_READ_ERROR; + break; + default: + ret = ISO_FILE_ERROR; + break; + } + } + return ret; + } + case 2: /* directory */ + return ISO_FILE_IS_DIR; + default: + return ISO_FILE_NOT_OPENNED; + } +} + +static +int lfs_readdir(IsoFileSource *src, IsoFileSource **child) +{ + _LocalFsFileSource *data; + + if (src == NULL || child == NULL) { + return ISO_NULL_POINTER; + } + + data = src->data; + switch(data->openned) { + case 1: /* not dir */ + return ISO_FILE_IS_NOT_DIR; + case 2: /* directory */ + { + char *path; + struct dirent *entry; + size_t a, b; + int ret; + + entry = readdir(data->info.dir); + if (entry == NULL) { + if (errno == EBADF) + return ISO_FILE_ERROR; + else + return 0; /* EOF */ + } + + /* constructs the new path */ + a = strlen(data->path); + b = strlen(entry->d_name); + path = malloc(a + b + 2); + if (path == NULL) { + return ISO_MEM_ERROR; + } + strncpy(path, data->path, a); + path[a] = '/'; + path[a + 1] = '\0'; + strncat(path, entry->d_name, b); + + /* create the new FileSrc */ + ret = iso_file_source_new_lfs(path, child); + if (ret < 0) { + free(path); + } + return ret; + } + default: + return ISO_FILE_NOT_OPENNED; + } +} + +static +int lfs_readlink(IsoFileSource *src, char *buf, size_t bufsiz) +{ + int size; + _LocalFsFileSource *data; + + if (src == NULL || buf == NULL) { + return ISO_NULL_POINTER; + } + + if (bufsiz <= 0) { + return ISO_WRONG_ARG_VALUE; + } + + data = src->data; + + /* + * invoke readlink, with bufsiz -1 to reserve an space for + * the NULL character + */ + size = readlink(data->path, buf, bufsiz - 1); + if (size < 0) { + /* error */ + switch(errno) { + case EACCES: + return ISO_FILE_ACCESS_DENIED; + case ENOTDIR: + case ENAMETOOLONG: + case ELOOP: + return ISO_FILE_BAD_PATH; + case ENOENT: + return ISO_FILE_DOESNT_EXIST; + case EINVAL: + return ISO_FILE_IS_NOT_SYMLINK; + case EFAULT: + case ENOMEM: + return ISO_MEM_ERROR; + default: + return ISO_FILE_ERROR; + } + } + + /* NULL-terminate the buf */ + buf[size] = '\0'; + return ISO_SUCCESS; +} + +static +void lfs_free(IsoFileSource *src) +{ + _LocalFsFileSource *data; + + data = src->data; + + /* close the file if it is already openned */ + if (data->openned) { + src->close(src); + } + + free(data->path); + free(data); +} + +/** + * + * @return + * 1 success, < 0 error + */ +int iso_file_source_new_lfs(char *path, IsoFileSource **src) +{ + IsoFileSource *lfs_src; + _LocalFsFileSource *data; + + if (src == NULL) { + return ISO_NULL_POINTER; + } + + /* allocate memory */ + data = malloc(sizeof(_LocalFsFileSource)); + if (data == NULL) { + return ISO_OUT_OF_MEM; + } + lfs_src = malloc(sizeof(IsoFileSource)); + if (lfs_src == NULL) { + free(data); + return ISO_OUT_OF_MEM; + } + + /* fill struct */ + data->path = path; + data->openned = 0; + + lfs_src->refcount = 1; + lfs_src->data = data; + lfs_src->get_path = lfs_get_path; + lfs_src->lstat = lfs_lstat; + lfs_src->open = lfs_open; + lfs_src->close = lfs_close; + lfs_src->read = lfs_read; + lfs_src->readdir = lfs_readdir; + lfs_src->readlink = lfs_readlink; + lfs_src->free = lfs_free; + + /* return */ + *src = lfs_src; + return ISO_SUCCESS; +} + diff --git a/src/fsource.c b/src/fsource.c new file mode 100644 index 0000000..4588aea --- /dev/null +++ b/src/fsource.c @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2007 Vreixo Formoso + * + * This file is part of the libisofs project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See COPYING file for details. + */ + +#include "fsource.h" +#include + +void iso_file_source_ref(IsoFileSource *src) +{ + ++src->refcount; +} + +void iso_file_source_unref(IsoFileSource *src) +{ + if (--src->refcount == 0) { + src->free(src); + free(src); + } +} diff --git a/src/fsource.h b/src/fsource.h index 0aef81f..ea27a55 100644 --- a/src/fsource.h +++ b/src/fsource.h @@ -40,22 +40,52 @@ struct Iso_File_Source { /** + * Get the path, relative to the filesystem this file source + * belongs to. * * @return - * 1 success, < 0 error + * the path, that belong to the IsoFileSource and should not be + * freed by the user. + */ + const char* (*get_path)(IsoFileSource *src); + + /** + * Get information about the file. + * @return + * 1 success, < 0 error + * Error codes: + * ISO_FILE_ACCESS_DENIED + * ISO_FILE_BAD_PATH + * ISO_FILE_DOESNT_EXIST + * ISO_MEM_ERROR + * ISO_FILE_ERROR + * ISO_NULL_POINTER */ int (*lstat)(IsoFileSource *src, struct stat *info); - //stat? - /** * Opens the source. * @return 1 on success, < 0 on error + * Error codes: + * ISO_FILE_ALREADY_OPENNED + * ISO_FILE_ACCESS_DENIED + * ISO_FILE_BAD_PATH + * ISO_FILE_DOESNT_EXIST + * ISO_MEM_ERROR + * ISO_FILE_ERROR + * ISO_NULL_POINTER */ int (*open)(IsoFileSource *src); - - void (*close)(IsoFileSource *src); + /** + * Close a previuously openned file + * @return 1 on success, < 0 on error + * Error codes: + * ISO_FILE_ERROR + * ISO_NULL_POINTER + * ISO_FILE_NOT_OPENNED + */ + int (*close)(IsoFileSource *src); /** * Attempts to read up to count bytes from the given source into @@ -67,6 +97,13 @@ struct Iso_File_Source * * @return * number of bytes read, 0 if EOF, < 0 on error + * Error codes: + * ISO_FILE_ERROR + * ISO_NULL_POINTER + * ISO_FILE_NOT_OPENNED + * ISO_FILE_IS_DIR + * ISO_MEM_ERROR + * ISO_INTERRUPTED */ int (*read)(IsoFileSource *src, void *buf, size_t count); @@ -83,15 +120,63 @@ struct Iso_File_Source * pointer to be filled with the given child. Undefined on error or OEF * @return * 1 on success, 0 if EOF (no more children), < 0 on error + * Error codes: + * ISO_FILE_ERROR + * ISO_NULL_POINTER + * ISO_FILE_NOT_OPENNED + * ISO_FILE_IS_NOT_DIR + * ISO_MEM_ERROR */ int (*readdir)(IsoFileSource *src, IsoFileSource **child); /** + * Read the destination of a symlink. You don't need to open the file + * to call this. + * + * @param buf + * allocated buffer of at least bufsiz bytes. + * The dest. will be copied there, and it will be NULL-terminated + * @param bufsiz + * characters to be copied. Destination link will be truncated if + * it is larger than given size. This include the \0 character. + * @return + * 1 on success, < 0 on error + * Error codes: + * ISO_FILE_ERROR + * ISO_NULL_POINTER + * ISO_WRONG_ARG_VALUE -> if bufsiz <= 0 + * ISO_FILE_IS_NOT_SYMLINK + * ISO_MEM_ERROR + * ISO_FILE_BAD_PATH + * ISO_FILE_DOESNT_EXIST * */ int (*readlink)(IsoFileSource *src, char *buf, size_t bufsiz); + /** + * Free implementation specific data. Should never be called by user. + * Use iso_file_source_unref() instead. + */ + void (*free)(IsoFileSource *src); + + /* + * TODO #00004 Add a get_mime_type() function. + * This can be useful for GUI apps, to choose the icon of the file + */ + + //TODO define the refcount behavior for FileSources. + int refcount; + void *data; }; +void iso_file_source_ref(IsoFileSource *src); +void iso_file_source_unref(IsoFileSource *src); + +/** + * Create a new IsoFileSource from a local filesystem path. + * While this is usually called by corresponding method in IsoFilesystem + * object, for local filesystem it is legal to call this directly. + */ +int iso_file_source_new_lfs(char *path, IsoFileSource **src); #endif /*LIBISO_FSOURCE_H_*/ diff --git a/src/image.h b/src/image.h index bec1d8c..a715680 100644 --- a/src/image.h +++ b/src/image.h @@ -11,6 +11,14 @@ struct IsoImage { + /* + * Image is a context for image manipulation. + * Global objects such as the message_queues must belogn to that + * context. Thus we will have, for example, a msg queue per image, + * so images are completelly independent and can be managed together. + * (Usefull, for example, in Multiple-Document-Interface GUI apps. + * [The stuff we have in init belongs really to image!] + */ };