diff options
Diffstat (limited to 'fs/fat/rockbox_wrapper.c')
-rw-r--r-- | fs/fat/rockbox_wrapper.c | 657 |
1 files changed, 657 insertions, 0 deletions
diff --git a/fs/fat/rockbox_wrapper.c b/fs/fat/rockbox_wrapper.c new file mode 100644 index 000000000..cfd46d602 --- /dev/null +++ b/fs/fat/rockbox_wrapper.c @@ -0,0 +1,657 @@ +/*------------------------------------------------------------------------------ + * (C) Copyright 2006 + * Keith Outwater, (outwater4@comcast.net) + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <config.h> + +/* + * FIXME: The pwd, cd and file path builder code needs to be fixed/implemented. + */ + +#include <asm/errno.h> +#include <asm/string.h> +#include <fat.h> +#include <linux/string.h> +#include "rockbox_debug.h" +#include <rockbox_mv.h> + +/* Routines, exported or not... */ +long file_fat_size(const char *filename); + +extern int errno; /* see board/<board>/<board>.c */ + +const char *month[] = { + "(null)", + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +}; + +/* + * The current working directory. + * Should we keep track of this on a per-partiton basis? + */ +char file_cwd[ROCKBOX_CWD_LEN + 1] = "/"; + +#ifndef __HAVE_ARCH_STRNICMP +/* + * Convert a character to lower case + */ +__inline__ static char +_tolower(char c) +{ + if ((c >= 'A') && (c <= 'Z')) { + c = (c - 'A') + 'a'; + } + return c; +} + +/** + * strnicmp - Case insensitive, length-limited string comparison + * @s1: One string + * @s2: The other string + * @len: the maximum number of characters to compare + * + * FIXME: replaces strnicmp() in rockbox-3.1. check is keep best one + */ +int +strnicmp(const char *s1, const char *s2, size_t len) +{ + /* + * Yes, Virginia, it had better be unsigned + */ + unsigned char c1, c2; + + c1 = 0; + c2 = 0; + if (len) { + do { + c1 = *s1; + c2 = *s2; + s1++; + s2++; + if (!c1) + break; + if (!c2) + break; + if (c1 == c2) + continue; + c1 = _tolower(c1); + c2 = _tolower(c2); + if (c1 != c2) + break; + } while (--len); + } + return (int) c1 - (int) c2; +} +#endif + +/** + * strcasecmp - Case insensitive string comparison + * @s1: One string + * @s2: The other string + */ +int strcasecmp(const char *s1, const char *s2) +{ + while (*s1 != '\0' && _tolower(*s1) == _tolower(*s2)) { + s1++; + s2++; + } + + return _tolower(*(unsigned char *) s1) - _tolower(*(unsigned char *) s2); +} + + +/** + * strncasecmp - Case insensitive, length-limited string comparison + * @s1: One string + * @s2: The other string + * @len: the maximum number of characters to compare + * + * FIXME: replaces strnicmp() in rockbox-3.1. check is keep best one + */ +int strncasecmp(const char *s1, const char *s2, size_t n) +{ + if(!n) + return 0; + + while (n-- != 0 && _tolower(*s1) == _tolower(*s2)) { + if(n == 0 || *s1 == '\0') + break; + s1++; + s2++; + } + + return _tolower(*(unsigned char *) s1) - _tolower(*(unsigned char *) s2); +} + + +/* + * U-Boot 'fat ls' wrapper. Code lifted from rockbox "filetree.c" + * Return number of bytes read on success, -1 on error. + */ +int +file_fat_ls(const char *dirname) +{ + uint i, max_entries; + int name_buffer_used = 0; + DIR *dir; + + struct tree_context { + int filesindir; + int dirsindir; + int dirlength; + void *dircache; /* unused, see below */ + char name_buffer[2048]; + int name_buffer_size; + bool dirfull; + } c; + + struct entry { + short attr; /* FAT attributes + file type flags */ + unsigned long time_write; /* Last write time */ + char *name; + }; + + if (*dirname == '/') { + while(*(dirname+1) == '/') dirname++; + } + if (strlen(dirname) > ROCKBOX_CWD_LEN) { + errno = ENAMETOOLONG; + return -1; + } + + dir = opendir(dirname); + if (!dir) { + /* maybe it's a regular file */ + i = file_fat_size(dirname); + if (i != -1) { + printf("%9lu %s\n", (ulong) i, dirname); + return 0; + } +#ifdef CONFIG_STRERROR + fprintf(stderr, "Error: ls() of \"%s\" failed: %s\n", + dirname, strerror(errno)); +#endif + return -1; + } + + c.dirsindir = 0; + c.dirfull = false; + c.name_buffer_size = sizeof (c.name_buffer) - 1; + + /* + * FIXME: figure out max i + */ + max_entries = (strncmp("/", dirname, 2)==0) ? 512 : 0xFFFFFFFF; + for (i = 0; i < max_entries; i++) { + int len; + struct dirent *entry = readdir(dir); +#if 0 /* dircache unused and not initialized ! */ + struct entry *dptr = + (struct entry *) (c.dircache + i * sizeof (struct entry)); +#endif + if (!entry) + break; + + len = strlen((char*)entry->d_name); + printf("%9lu %s %02d %4d %02d:%02d:%02d [%s] %s\n", + entry->size, + month[(entry->wrtdate >> 5) & 0xf], + entry->wrtdate & 0xf, + ((entry->wrtdate >> 9) & 0x7f) + 1980, + (entry->wrttime >> 11) & 0x1f, + (entry->wrttime >> 5) & 0x3f, entry->wrttime & 0x1f, + (entry->attribute & FAT_ATTR_DIRECTORY) ? "dir " : "file", + entry->d_name); +#if 0 + /* + * skip directories . and .. + */ + if ((entry->attribute & FAT_ATTR_DIRECTORY) && + (((len == 1) && + (!strncmp(entry->d_name, ".", 1))) || + ((len == 2) && (!strncmp(entry->d_name, "..", 2))))) { + i--; + continue; + } + + /* + * Skip FAT volume ID + */ + if (entry->attribute & FAT_ATTR_VOLUME_ID) { + i--; + continue; + } +#endif + +#if 0 /* dircache unused and not initialized ! */ + dptr->attr = entry->attribute; +#endif + if (len > c.name_buffer_size - name_buffer_used - 1) { + /* + * Tell the world that we ran out of buffer space + */ + c.dirfull = true; + fat_dprintf("Dir buffer is full\n"); + break; + } + +#if 0 /* dircache unused and not initialized ! */ + dptr->name = &c.name_buffer[name_buffer_used]; + dptr->time_write = + (long) entry->wrtdate << 16 | (long) entry->wrttime; + strcpy(dptr->name, (char*)entry->d_name); +#endif + name_buffer_used += len + 1; + + /* + * count the remaining dirs + */ +#if 0 /* dircache unused and not initialized ! */ + if (dptr->attr & FAT_ATTR_DIRECTORY) +#else + if (entry->attribute & FAT_ATTR_DIRECTORY) +#endif + c.dirsindir++; + } + + c.filesindir = i; + c.dirlength = i; + closedir(dir); + printf("\n%d file%s %d dir%s", + c.filesindir, c.filesindir == 1 ? "," : "s,", + c.dirsindir, c.dirsindir == 1 ? "\n" : "s\n"); + return 0; +} + +/* + * return target file size (negative if failure) + * Return number of bytes of the target file, -1 on error. + */ +long +file_fat_size(const char *filename) +{ + int fd; + long file_size; + + printf("Reading %s\n", filename); + fd = open(filename, O_RDONLY); + if (fd < 0) { +#ifdef CONFIG_STRERROR + fprintf(stderr, "Error: open() of \"%s\" failed: %s\n", + filename, strerror(errno)); +#endif + return -1; + } + + file_size = filesize(fd); + if (file_size < 0) { + fat_eprintf("Call to filesize() failed\n"); + close(fd); + return -1; + } + + close(fd); + return file_size; +} + + +/* + * U-Boot 'file read' wrapper. + * Return number of bytes read on success, -1 on error. + */ +long +file_fat_read(const char *filename, void *buffer, unsigned long maxsize) +{ + int fd; + long bytes; + long file_size; + + printf("Reading %s\n", filename); + fd = open(filename, O_RDONLY); + if (fd < 0) { +#ifdef CONFIG_STRERROR + fprintf(stderr, "Error: open() of \"%s\" failed: %s\n", + filename, strerror(errno)); +#endif + return -1; + } + + /* + * If the number of bytes to read is zero, read the entire file. + */ + if (maxsize == 0) { + file_size = filesize(fd); + if (file_size < 0) { + fat_eprintf("Call to filesize() failed\n"); + return -1; + } + + maxsize = (unsigned long) file_size; + fat_dprintf("Reading entire file (%lu bytes)\n", maxsize); + } + + bytes = (long) read(fd, buffer, (size_t) maxsize); + if ((unsigned long) bytes != maxsize) { +#ifdef CONFIG_STRERROR + fprintf(stderr, "Read error: %s\n", strerror(errno)); +#endif + close(fd); + return -1; + } + + close(fd); + return bytes; +} + +/* + * U-Boot 'file write' wrapper. + * Return number of bytes written on success, -1 on error. + */ +long +file_fat_write(const char *filename, void *buffer, unsigned long maxsize) +{ + int fd; + long bytes; + + printf("Writing %s\n", filename); + fd = open(filename, O_WRONLY | O_CREAT); + if (fd < 0) { +#ifdef CONFIG_STRERROR + fprintf(stderr, "Open of \"%s\" failed: %s\n", + filename, strerror(errno)); +#endif + return -1; + } + + bytes = (long) write(fd, buffer, (size_t) maxsize); + if ((unsigned long) bytes != maxsize) { +#ifdef CONFIG_STRERROR + fprintf(stderr, "Write failed: %s\n", strerror(errno)); +#endif + close(fd); + return -1; + } + + close(fd); + return bytes; +} + +int +file_fat_pwd(void) +{ + printf("%s\n", file_cwd); + return 1; +} + +/* + * U-Boot 'file rm' wrapper. + * Return 1 on success, -1 on error and set 'errno'. + */ +int +file_fat_rm(const char *filename) +{ + int rc; + + fat_dprintf("Remove \"%s\"\n", filename); + rc = remove(filename); + if (rc < 0) { +#ifdef CONFIG_STRERROR + fprintf(stderr, "Removal of \"%s\" failed: %s\n", + filename, strerror(errno)); +#endif + return -1; + } + + return 1; +} + +/* + * U-Boot 'file mkdir' wrapper. + * Return 1 on success, -1 on error and set 'errno'. + */ +int +file_fat_mkdir(const char *dirname) +{ + int rc; + + fat_dprintf("Make dir \"%s\"\n", dirname); + rc = mkdir(dirname); + if (rc < 0) { +#ifdef CONFIG_STRERROR + fprintf(stderr, "Creation of \"%s\" failed: %s\n", + dirname, strerror(errno)); +#endif + return -1; + } + + return 1; +} + +/* + * U-Boot 'file rmdir' wrapper. + * Return 1 on success, -1 on error and set 'errno'. + */ +int +file_fat_rmdir(const char *dirname) +{ + int rc; + + fat_dprintf("Remove dir \"%s\"\n", dirname); + rc = rmdir(dirname); + if (rc < 0) { +#ifdef CONFIG_STRERROR + fprintf(stderr, "Removal of \"%s\" failed: %s\n", + dirname, strerror(errno)); +#endif + return -1; + } + + return 1; +} + +static void +pathcpy(char *dest, const char *src) +{ + char *origdest = dest; + + do { + if (dest - file_cwd >= CWD_LEN) { + *dest = '\0'; + return; + } + + *(dest) = *(src); + if (*src == '\0') { + if (dest-- != origdest && ISDIRDELIM(*dest)) { + *dest = '\0'; + } + return; + } + + ++dest; + if (ISDIRDELIM(*src)) { + while (ISDIRDELIM(*src)) + src++; + } else { + src++; + } + } while (1); +} + +/* + * U-Boot 'file cd' wrapper. + */ +int +file_fat_cd(const char *path) +{ + if (ISDIRDELIM(*path)) { + while (ISDIRDELIM(*path)) + path++; + strncpy(file_cwd + 1, path, CWD_LEN - 1); + } else { + const char *origpath = path; + char *tmpstr = file_cwd; + int back = 0; + + while (*tmpstr != '\0') + tmpstr++; + + do { + tmpstr--; + } while (ISDIRDELIM(*tmpstr)); + + while (*path == '.') { + path++; + while (*path == '.') { + path++; + back++; + } + + if (*path != '\0' && !ISDIRDELIM(*path)) { + path = origpath; + back = 0; + break; + } + + while (ISDIRDELIM(*path)) + path++; + + origpath = path; + } + + while (back--) { + /* + * Strip off path component + */ + while (!ISDIRDELIM(*tmpstr)) { + tmpstr--; + } + + if (tmpstr == file_cwd) { + /* + * Incremented again right after the loop. + */ + tmpstr--; + break; + } + + /* + * Skip delimiters + */ + while (ISDIRDELIM(*tmpstr)) + tmpstr--; + } + + tmpstr++; + if (*path == '\0') { + if (tmpstr == file_cwd) { + *tmpstr = '/'; + tmpstr++; + } + + *tmpstr = '\0'; + return 0; + } + + *tmpstr = '/'; + pathcpy(tmpstr + 1, path); + } + + printf("New CWD is '%s'\n", file_cwd); + return 0; +} + +/* + * U-Boot 'file mv' wrapper. + * Return 1 on success, -1 on error and set 'errno'. + */ +int +file_fat_mv(const char *oldname, const char *newpath) +{ + int rc; + + fat_dprintf("Move \'%s\" to \"%s\"\n", oldname, newpath); + rc = rename(oldname, newpath); + if (rc < 0) { +#ifdef CONFIG_STRERROR + fprintf(stderr, "Failed to move \"%s\" to \"%s\" : %s\n", + oldname, newpath, strerror(errno)); +#endif + return -1; + } + + return 1; +} + + +extern int fat_mount(IF_MV2(int volume,) IF_MV2(int drive,) long startsector); +int rockbox_fat_mount(long startsector); +/* @brief Map rockbox_fat_mount() (fs/fat/fat.c) on fat_mount() (fs/fat/rockbox_fat.c) + */ +int rockbox_fat_mount(long startsector) +{ +#ifdef HAVE_MULTIVOLUME +#error "MULTIVOLUME from rockbox package not ported to U-boot" +#else + return fat_mount(IF_MV2(volume,) IF_MV2(drive,) startsector); +#endif +} + +/* @brief Get filesystem size and free left space + * @return partition size in kByte + */ +unsigned int rockbox_fat_size(void) +{ +#ifdef HAVE_MULTIVOLUME +#error "MULTIVOLUME from rockbox package not ported to U-boot" +#else + unsigned long tsize; + fat_size(IF_MV2(int volume,) &tsize, NULL); + return (unsigned int) tsize; +#endif +} + +/* @brief Return available free space. + * @param[in] if size==0, compute free space, else check if enougth free space + * @return if size==0, ret free space, else return true if enougth free space + * @internals + * Input and returned size values are formated in kByte. + */ +unsigned int rockbox_fat_free(unsigned long size) +{ +#ifdef HAVE_MULTIVOLUME +#error "MULTIVOLUME from rockbox package not ported to U-boot" +#else + if (size) { + return fat_check_free(IF_MV2(int volume,) size); + } else { + unsigned long tfree; + fat_recalc_free(IF_MV2(int volume,)); + fat_size(IF_MV2(int volume,) NULL, &tfree); + return (unsigned int) tfree; + } +#endif +} + + |