| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398 |
- /*
- * Copyright (C) 2004-2006 by egnite Software GmbH. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the copyright holders nor the names of
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
- * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * For additional information see http://www.ethernut.de/
- */
- /*!
- * \file fs/pnutfs.c
- * \brief Peanut File System.
- *
- * \verbatim
- *
- * $Log$
- * Revision 1.16 2009/02/13 14:52:05 haraldkipp
- * Include memdebug.h for heap management debugging support.
- *
- * Revision 1.15 2009/01/17 11:26:46 haraldkipp
- * Getting rid of two remaining BSD types in favor of stdint.
- * Replaced 'u_int' by 'unsinged int' and 'uptr_t' by 'uintptr_t'.
- *
- * Revision 1.14 2008/08/27 06:45:23 thornen
- * Added bank support for MMnet03..04 MMnet102..104
- *
- * Revision 1.13 2008/08/26 17:36:45 haraldkipp
- * Revoked changes 2008/08/26 by thornen.
- *
- * Revision 1.11 2008/08/11 06:59:42 haraldkipp
- * BSD types replaced by stdint types (feature request #1282721).
- *
- * Revision 1.10 2006/08/01 07:43:48 haraldkipp
- * PNUT file system failed after some modifications done previously for the
- * PHAT file system. During directory open, the NUTFILE structure must be
- * allocated in the file system driver. PnutDirRead() must return -1 if the
- * end of a directory is reached. Reading unused directory entries must update
- * the file position pointer.
- *
- * Revision 1.9 2006/03/02 20:01:17 haraldkipp
- * Added implementation of dev_size makes _filelength() work, which in turn
- * enables the use of this file system in pro/httpd.c.
- *
- * Revision 1.8 2006/01/05 16:45:20 haraldkipp
- * Dynamic NUTFILE allocation for detached block device.
- *
- * Revision 1.7 2005/09/08 10:12:44 olereinhardt
- * Added #ifdef statement in NutSegBufEnable to avoid compiler warning
- * if no banked mem is used.
- *
- * Revision 1.6 2005/09/07 16:23:41 christianwelzel
- * Added support for MMnet02. Bankswitching is now handled in bankmem.h
- *
- * Revision 1.5 2005/08/02 17:46:47 haraldkipp
- * Major API documentation update.
- *
- * Revision 1.4 2005/05/16 08:33:59 haraldkipp
- * Added banking support for Arthernet.
- *
- * Revision 1.3 2005/02/21 11:10:21 olereinhardt
- * Changed deckaration of the "root" variable to compile with unix emulation
- *
- * Revision 1.2 2005/02/07 18:57:47 haraldkipp
- * ICCAVR compile errors fixed
- *
- * Revision 1.1 2005/02/05 20:35:21 haraldkipp
- * Peanut added
- *
- * \endverbatim
- */
- #include <compiler.h>
- #include <stdlib.h>
- #include <string.h>
- #include <time.h>
- #include <fcntl.h>
- #include <dirent.h>
- #include <errno.h>
- #include <memdebug.h>
- #include <sys/stat.h>
- #include <sys/file.h>
- #include <sys/bankmem.h>
- #include <fs/fs.h>
- #include <dev/pnut.h>
- /*!
- * \addtogroup xgPNut
- */
- /*@{*/
- /*!
- * \name Peanut File System Configuration
- *
- * The Nut/OS Configurator may be used to override the default values.
- */
- /*@{*/
- /*! \brief Size of a filesystem block.
- *
- * \showinitializer
- */
- #ifndef PNUT_BLOCK_SIZE
- #define PNUT_BLOCK_SIZE 512
- #endif
- /*!
- * \brief Size of a directory entry.
- *
- *
- * \showinitializer
- */
- #ifndef PNUT_DIRENT_SIZE
- #define PNUT_DIRENT_SIZE 32
- #endif
- /*!
- * \brief Maximum number of blocks per node.
- *
- * Peanut supports only one node per file. Thus, this number
- * multiplied by PNUT_BLOCK_SIZE specifies the maximum file
- * size.
- *
- * Changings this number will change the size of the node
- * structure, which must fit into a single filesystem block.
- *
- * \showinitializer
- */
- #ifndef PNUT_BLOCKS_PER_NODE
- #define PNUT_BLOCKS_PER_NODE 250
- #endif
- #ifndef PNUTBANK_COUNT
- #ifdef ARTHERNET1
- /* Default for Arthernet 1. */
- #define PNUTBANK_COUNT 15
- #elif MMNET02 || MMNET03 || MMNET04 ||\
- MMNET102 || MMNET103 || MMNET104
- /* Default for MMnte02. */
- #define PNUTBANK_COUNT 6
- #else
- /* Default for Ethernet 2. */
- #define PNUTBANK_COUNT 30
- #endif
- #endif
- /*@}*/
- #ifndef SEEK_SET
- # define SEEK_SET 0 /* Seek from beginning of file. */
- # define SEEK_CUR 1 /* Seek from current position. */
- # define SEEK_END 2 /* Set file pointer to EOF plus "offset" */
- #endif
- #define NODETYPE_REG 0
- #define NODETYPE_DIR 1
- typedef short PNUT_BLKNUM;
- /*!
- * \brief Node structure.
- *
- * The Peanut filesystem divides the total space into blocks. It
- * distinguishes between unallocated blocks, data blocks and blocks
- * containing node informations. Each allocated data block is
- * associated to a node block. Unallocated blocks are collected
- * in a linked list.
- *
- * Peanut knows two types of nodes, file nodes and directory nodes.
- * Each file node represents a file entry and the associated data
- * blocks contain the contents of this file. Each directory node
- * represents a directory and the associated data blocks contain
- * the entries in this directory. Each directory entry in use
- * contains a link to to a node block.
- *
- * \note The node structure must fit into a block.
- */
- typedef struct {
- uint8_t node_type; /*!< Type of this node, 0=file, 1=dir */
- uint8_t node_refs; /*!< Reference counter: Number of active open calls. */
- /*!
- * \brief Number of links.
- *
- * For file nodes this number keeps track of the number of directory
- * entries pointing to it. PNUT currently doesn't support link type
- * entries, thus in file entries this is always 1.
- *
- * For directory nodes the number represents the number
- * of subdirectories plus 2.
- */
- uint16_t node_links;
- uint32_t node_size; /*!< Total size of the data area. */
- time_t node_mtime; /*!< Time of last modification. */
- PNUT_BLKNUM node_blocks[PNUT_BLOCKS_PER_NODE]; /*!< Allocated data blocks. */
- } PNUT_NODE;
- /*!
- * \brief Maximum length of a base name.
- *
- * This is a calculated value and depends on the definition of the
- * block size and the size of a directory entry.
- */
- #define PNUT_MAX_NAMELEN (PNUT_DIRENT_SIZE - sizeof(PNUT_BLKNUM) - sizeof(uint8_t) - 1)
- /*!
- * \brief Directory entry structure.
- *
- * Each directory node contains a list of data blocks. Each of these
- * data blocks contains a number of directory entries. Each directory
- * entry points to a node block, which may be a file node or a
- * directory node.
- */
- typedef struct {
- /*!
- * \brief Node block index of this entry.
- *
- * This may point to a file or directory node.
- */
- PNUT_BLKNUM dir_node;
- /*!
- * \brief Allocation flag.
- *
- * Set to one if this entry is in use.
- */
- uint8_t dir_inuse;
- /*!
- * \brief Name of this entry.
- *
- * Contains the name ot the file or directory.
- */
- char dir_name[PNUT_MAX_NAMELEN + 1];
- } PNUT_DIRENTRY;
- /*!
- * \brief Maximum size of a file or directory.
- *
- * This is a calculated value and depends on the definition of the
- * block size and the number of blocks per directory entry.
- */
- #define PNUT_MAX_FILESIZE (PNUT_BLOCKS_PER_NODE * PNUT_BLOCK_SIZE)
- /*!
- * \brief Directory search result information structure.
- */
- typedef struct {
- /*!
- * \brief Node of the entry found.
- */
- PNUT_BLKNUM fr_node;
- /*!
- * \brief Parent node of the entry found.
- */
- PNUT_BLKNUM fr_pnode;
- /*!
- * \brief Last path component.
- *
- * Set if all parents found.
- */
- const char *fr_name;
- } PNUT_FINDRESULT;
- /*!
- * \brief PNUT file descriptor type.
- */
- typedef struct _PNUTFILE PNUTFILE;
- /*!
- * \brief PNUT file descriptor structure.
- */
- struct _PNUTFILE {
- PNUT_BLKNUM f_node; /* Node of the file or directory. */
- uint32_t f_pos; /* Current file pointer position. */
- unsigned int f_flag; /* File mode. */
- };
- /*! \brief Root node of the filesystem. */
- static PNUT_BLKNUM root;
- /* -------------------- Banking hardware routines ------------------- */
- /*! \brief Size of each memory bank. */
- #ifndef NUTBANK_SIZE
- #define NUTBANK_SIZE 16384
- #endif
- /*!
- * \brief Total number of blocks on this device.
- *
- * This value is calulated by multiplying the number of memory banks
- * reserved for the file system by the number of blocks per bank.
- *
- * For example, if all 30 banks, which are available on Ethernut 2, are
- * reserved for PNUT and if the size of a block is 512 bytes, then
- * 960 blocks are available.
- */
- #ifndef PNUT_TOTAL_BLOCKS
- #define PNUT_TOTAL_BLOCKS (PNUTBANK_COUNT * (NUTBANK_SIZE / PNUT_BLOCK_SIZE))
- #endif
- #define BLOCKS_PER_BANK (NUTBANK_SIZE / PNUT_BLOCK_SIZE)
- #ifndef NUTBANK_SR
- #define NUTBANK_SR 0xFF00
- #endif
- #ifndef NUTBANK_START
- #define NUTBANK_START 0x8000
- #endif
- #define NUTBANK_PTR ((char *)NUTBANK_START)
- /*!
- * \brief Make the bank visible, which contains the specified block.
- *
- * On Ethernut 2 we select a bank by writing the bank number to
- * the bank register base address plus the bank number.
- *
- * For example, writing 0x12 to 0xFF12 selects bank 18 (0x12),
- * assuming that 0xFF00 is the bank register base address.
- *
- * \param blk Block number to access.
- */
- void BankSelect(PNUT_BLKNUM blk)
- {
- // This is a hack to avoid compiler warning if no banking is enabled...
- // But I don't like moving code to header files at all.. (Ole Reinhardt)
- #if NUTBANK_COUNT
- int bank = blk / BLOCKS_PER_BANK;
- #endif
- // Bankswitching is now handled in bankmem.h
- NutSegBufEnable(bank);
- }
- /*!
- * \brief Select specified bank and return pointer to block.
- */
- PNUT_NODE *BankNodePointer(PNUT_BLKNUM blk)
- {
- if (blk < 0) {
- return NULL;
- }
- BankSelect(blk);
- return (PNUT_NODE *) & NUTBANK_PTR[(blk % BLOCKS_PER_BANK) * PNUT_BLOCK_SIZE];
- }
- /* ------------------------ Block routines ------------------------ */
- /*
- * \brief Index of the first free block.
- *
- * All free blocks are collected in a linked list. This global variable
- * contains the index of the first free block. Each free block contains
- * nothing but the index of the next free block, which is -1 in the last
- * member of this list.
- *
- * If this variable is set to -1, then there are no more free blocks
- * available. During file system initialization all available blocks are
- * added to this list.
- */
- static PNUT_BLKNUM blockFreeList = -1;
- /*!
- * \brief Allocate a block.
- *
- * Removes the block in front of the list of free blocks.
- *
- * \return Index of the free block or -1 if none is available.
- */
- static PNUT_BLKNUM PnutBlockAlloc(void)
- {
- PNUT_BLKNUM rc = blockFreeList;
- if (rc >= 0) {
- PNUT_BLKNUM *bankptr = (PNUT_BLKNUM *) BankNodePointer(blockFreeList);
- blockFreeList = *bankptr;
- /* Clear the block contents. */
- memset(bankptr, 0, PNUT_BLOCK_SIZE);
- }
- return rc;
- }
- /*!
- * \brief Release a block.
- *
- * Inserts a given block in front of the list of free blocks.
- *
- * \param blk Index of the block to be released.
- */
- static void PnutBlockRelease(PNUT_BLKNUM blk)
- {
- PNUT_BLKNUM *bankptr = (PNUT_BLKNUM *) BankNodePointer(blk);
- *bankptr = blockFreeList;
- blockFreeList = blk;
- }
- /* ------------------------ Node routines ------------------------ */
- /*!
- * \brief Allocate a node.
- *
- * Creates a new node of a specified type.
- *
- * \param type Type of this node, either NODETYPE_REG or NODETYPE_DIR.
- *
- * \return Block index of this node or -1 if there are no more blocks.
- */
- static PNUT_BLKNUM PnutNodeAlloc(uint8_t type)
- {
- PNUT_BLKNUM node = PnutBlockAlloc();
- if (node >= 0) {
- int i;
- PNUT_NODE *np = BankNodePointer(node);
- /* Clear the data block list of this node. */
- for (i = 0; i < PNUT_BLOCKS_PER_NODE; i++) {
- np->node_blocks[i] = -1;
- }
- /* Set the type and the last modification date. */
- np->node_type = type;
- np->node_mtime = time(0);
- }
- return node;
- }
- /*
- * \brief Free all blocks of a specified node.
- */
- static void PnutNodeDataRelease(PNUT_BLKNUM node)
- {
- int i;
- for (i = 0; i < PNUT_BLOCKS_PER_NODE; i++) {
- if (BankNodePointer(node)->node_blocks[i] >= 0) {
- PnutBlockRelease(BankNodePointer(node)->node_blocks[i]);
- BankNodePointer(node)->node_blocks[i] = -1;
- }
- }
- BankNodePointer(node)->node_size = 0;
- BankNodePointer(node)->node_mtime = time(0);
- }
- /*!
- * \brief Release a node and all its data blocks.
- */
- static void PnutNodeRelease(PNUT_BLKNUM node)
- {
- PnutNodeDataRelease(node);
- PnutBlockRelease(node);
- }
- /*!
- * \brief Retrieve a pointer into a node's data block.
- *
- * This function also returns the number of bytes available within
- * the data block at the retrieved pointer position.
- *
- * \param node Node number.
- * \param pos Data position.
- * \param buffer Receives the pointer to the specified position.
- * \param size Receives the remaining number of bytes within the block.
- * \param create If the position is beyond the current data size and if
- * this parameter is not equal zero, then a new data block
- * will be allocated.
- *
- * \return 0 on success, otherwise returns an error code.
- */
- static int PnutNodeGetDataPtr(PNUT_BLKNUM node, uint32_t pos, uint8_t **buffer, size_t * size, int create)
- {
- int blkidx; /* Number of full blocks */
- int rc = EINVAL;
- *buffer = NULL;
- *size = 0;
- /* Calculate the block index. Make sure it fits in our maximum file size. */
- if ((blkidx = pos / PNUT_BLOCK_SIZE) < PNUT_BLOCKS_PER_NODE) {
- PNUT_BLKNUM blk;
- /* Get the data block number. Optionally allocate a new block. */
- if ((blk = BankNodePointer(node)->node_blocks[blkidx]) < 0 && create) {
- if ((blk = PnutBlockAlloc()) < 0) {
- rc = ENOSPC;
- } else {
- BankNodePointer(node)->node_blocks[blkidx] = blk;
- }
- }
- /*
- * If we got a valid block, then set the pointer at the specified
- * position and the remaining bytes within this block.
- */
- if (blk >= 0) {
- uint8_t *blkptr = (uint8_t *) BankNodePointer(blk);
- int blkpos = pos % PNUT_BLOCK_SIZE; /* Position within block */
- *buffer = blkptr + blkpos;
- *size = PNUT_BLOCK_SIZE - blkpos;
- rc = 0;
- }
- }
- return rc;
- }
- /* ------------------------ Directory routines ------------------------ */
- /*!
- * \brief Check if a directory is empty.
- */
- static int PnutDirIsEmpty(PNUT_BLKNUM node)
- {
- int rc = 1;
- uint32_t pos;
- size_t size;
- PNUT_DIRENTRY *entry = NULL;
- /* Loop through the data blocks of this directory node. */
- for (pos = 0; pos < BankNodePointer(node)->node_size; pos += PNUT_DIRENT_SIZE) {
- /* Get the pointer to the next directory entry. */
- uint8_t *ptr = (uint8_t *)entry;
- if (PnutNodeGetDataPtr(node, pos, &ptr, &size, 0) || size == 0) {
- /* No more entries. */
- break;
- }
- entry = (PNUT_DIRENTRY *)ptr;
- if (size >= PNUT_DIRENT_SIZE) {
- if (entry->dir_inuse && strcmp(entry->dir_name, ".") && strcmp(entry->dir_name, "..")) {
- /* Entry found */
- rc = 0;
- break;
- }
- }
- }
- return rc;
- }
- /*!
- * \brief Find a directory entry with a specified name.
- *
- * \param node Node of the directory to search.
- * \param path Path name of the entry to retrieve.
- * \param len Number of characters in the path name to consider,
- * starting from the beginning. If the path name
- * contains more than one component, the caller must
- * set this parameter to the length of the first one.
- * \param entry Receives the pointer to the directory entry.
- *
- * \return 0 on succes, otherwise an error code is returned.
- */
- static int PnutDirFindEntry(PNUT_BLKNUM node, const char *path, size_t len, PNUT_DIRENTRY ** entry)
- {
- int rc = ENOENT;
- uint32_t pos;
- size_t size;
- /* Loop through the data blocks of this directory node. */
- for (pos = 0; pos < BankNodePointer(node)->node_size; pos += PNUT_DIRENT_SIZE) {
- /* Get the pointer to the next directory entry. */
- uint8_t *ptr = (uint8_t *)(*entry);
- if (PnutNodeGetDataPtr(node, pos, &ptr, &size, 0) || size == 0) {
- /* No more entries. */
- break;
- }
- *entry = (PNUT_DIRENTRY *)ptr;
- /* Compare this entry. */
- if (size >= PNUT_DIRENT_SIZE) { /* Skip entries across block boundaries. */
- if ((*entry)->dir_inuse && /* Skip unused entries. */
- strlen((*entry)->dir_name) == len && /* Skip non-matching names. */
- strncmp((*entry)->dir_name, path, len) == 0) {
- /* Requested entry found */
- rc = 0;
- break;
- }
- }
- }
- return rc;
- }
- /*
- * \brief Find directory entry of a specified path.
- *
- * \param node First node block to search.
- * \param path Path to find.
- * \param result Search result.
- *
- * \return Error code.
- */
- static int PnutDirFindPath(PNUT_BLKNUM node, const char *path, PNUT_FINDRESULT * result)
- {
- int rc = 0;
- size_t len;
- PNUT_DIRENTRY *entry = NULL;
- result->fr_pnode = node;
- result->fr_node = -1;
- result->fr_name = NULL;
- while (*path == '/') {
- path++;
- }
- if (*path == 0) {
- path = ".";
- }
- /*
- * Loop for each path component.
- */
- while (*path) {
- const char *cp;
- /* Make sure that this is a directory node. */
- if (BankNodePointer(node)->node_type != NODETYPE_DIR) {
- rc = ENOTDIR;
- break;
- }
- /* Retrieve the length of first path component. */
- for (len = 0, cp = path; *cp && *cp != '/'; cp++) {
- len++;
- }
- /*
- * If this component is last, we found all parents.
- * Keep the last one stored in the result.
- */
- if (*cp == 0) {
- result->fr_name = path;
- }
- /* Try to find this component. */
- if ((rc = PnutDirFindEntry(node, path, len, &entry)) != 0) {
- rc = ENOENT;
- break;
- }
- /* Component found. Return if this was the last one. */
- result->fr_node = entry->dir_node;
- if (*cp == 0) {
- break;
- }
- /* Not the last component. Store node as previous node. */
- result->fr_pnode = result->fr_node;
- /* Move forward to the next path component. */
- node = result->fr_node;
- path += len;
- while (*path == '/') {
- path++;
- }
- }
- return rc;
- }
- /*!
- * \brief Open a directory.
- *
- * \param dev Specifies the file system device.
- * \param dir Pointer to the internal directory information structure.
- * The pathname of the directory to open must have been
- * copied to the data buffer prior calling this routine.
- *
- * \return 0 on success, -1 otherwise.
- */
- static int PnutDirOpen(NUTDEVICE * dev, DIR * dir)
- {
- PNUTFILE *fp;
- PNUT_FINDRESULT found;
- int rc;
- /* Locate the entry in our directory structure. */
- if ((rc = PnutDirFindPath(root, dir->dd_buf, &found)) != 0) {
- errno = rc;
- rc = -1;
- } else {
- if (BankNodePointer(found.fr_node)->node_type != NODETYPE_DIR) {
- errno = ENOTDIR;
- rc = -1;
- }
- /* Allocate a PNUT file descriptor. */
- else if ((fp = malloc(sizeof(PNUTFILE))) == 0) {
- rc = -1;
- }
- /*
- * Initialize the descriptor and store it in the directory
- * information structure.
- */
- else {
- fp->f_node = found.fr_node;
- fp->f_pos = 0;
- if ((dir->dd_fd = malloc(sizeof(NUTFILE))) == 0) {
- free(fp);
- rc = -1;
- }
- else {
- memset(dir->dd_fd, 0, sizeof(NUTFILE));
- dir->dd_fd->nf_dev = dev;
- dir->dd_fd->nf_fcb = fp;
- /* Keep track of the number of open calls. */
- BankNodePointer(fp->f_node)->node_refs++;
- }
- }
- }
- return rc;
- }
- /*!
- * \brief Close a directory.
- */
- static int PnutDirClose(DIR * dir)
- {
- if (dir && dir->dd_fd) {
- if (dir->dd_fd->nf_fcb) {
- PNUTFILE *fp = dir->dd_fd->nf_fcb;
- BankNodePointer(fp->f_node)->node_refs--;
- free(fp);
- }
- free(dir->dd_fd);
- return 0;
- }
- return EINVAL;
- }
- /*!
- * \brief Read the next directory entry.
- */
- static int PnutDirRead(DIR * dir)
- {
- int rc = -1;
- uint32_t pos;
- PNUT_DIRENTRY *entry = NULL;
- size_t size;
- PNUTFILE *fp = dir->dd_fd->nf_fcb;
- struct dirent *ent = (struct dirent *) dir->dd_buf;
- ent->d_name[0] = 0;
- for (pos = fp->f_pos; pos < BankNodePointer(fp->f_node)->node_size; pos += PNUT_DIRENT_SIZE) {
- uint8_t *ptr = (uint8_t *)entry;
- /* Retrieve the entry at the current position. */
- rc = PnutNodeGetDataPtr(fp->f_node, pos, &ptr, &size, 0);
- if (rc || size == 0) {
- /* No more entries. */
- rc = -1;
- break;
- }
- entry = (PNUT_DIRENTRY *)ptr;
- fp->f_pos = pos + PNUT_DIRENT_SIZE;
- /* Skip entries across block boundaries and unused entires. */
- if (size >= PNUT_DIRENT_SIZE && entry->dir_inuse) {
- memset(ent, 0, sizeof(struct dirent));
- ent->d_fileno = entry->dir_node;
- ent->d_namlen = (uint8_t) strlen(entry->dir_name);
- strcpy(ent->d_name, entry->dir_name);
- break;
- }
- }
- return rc;
- }
- /*!
- * \brief Add directory entry.
- *
- * \param dnode The entry is added to this directory node.
- * \param name The entry's name.
- * \param enode The entry's node.
- *
- * \return 0 if successful. Otherwise returns an error code.
- */
- static int PnutDirAddEntry(PNUT_BLKNUM dnode, const char *name, PNUT_BLKNUM enode)
- {
- int rc = 0;
- uint32_t pos = 0;
- size_t size;
- PNUT_DIRENTRY *entry = NULL;
- PNUT_NODE *np;
- /*
- * Loop through all directory entries until an unused one
- * is found. If required, a new data block is allocated,
- * so success is guaranteed unless we run out of free
- * blocks.
- */
- for (;;) {
- /*
- * Get the data pointer to the specified position. Automatically
- * create a new block if we reached the end of the data.
- */
- uint8_t *ptr = (uint8_t *)entry;
- if ((rc = PnutNodeGetDataPtr(dnode, pos, &ptr, &size, 1)) != 0) {
- break;
- }
- entry = (PNUT_DIRENTRY *)ptr;
- pos += PNUT_DIRENT_SIZE;
- /* Process entry if it doesn't cross block boundaries. */
- if (size >= PNUT_DIRENT_SIZE) {
- /* Did we find a previously released or a newly allocated entry? */
- if (entry->dir_inuse == 0) {
- /* Update the entry. */
- entry->dir_node = enode;
- entry->dir_inuse = 1;
- strcpy(entry->dir_name, name);
- /* Update the directory node. */
- np = BankNodePointer(dnode);
- np->node_mtime = time(0);
- if (pos > np->node_size) {
- np->node_size = pos;
- }
- /* Update the entry's node. */
- np = BankNodePointer(enode);
- np->node_links++;
- break;
- }
- }
- }
- return rc;
- }
- /*!
- * \brief Remove a directory entry.
- *
- * Entries referring to nodes with active open calls are not deleted.
- *
- * Entries referring to directory nodes with more than two links left
- * are not deleted.
- *
- * File nodes are released if the entry to remove was the last link
- * to them.
- *
- * \param node Remove the entry from the directory node in this block.
- * \param name Name of the entry to be removed.
- *
- * \return 0 if successful. Otherwise returns an error code.
- */
- static int PnutDirDelEntry(PNUT_BLKNUM node, const char *name)
- {
- int rc;
- PNUT_DIRENTRY *entry = NULL;
- PNUT_NODE *rnp;
- PNUT_BLKNUM rnode;
- /* Make sure this entry exists. */
- if ((rc = PnutDirFindEntry(node, name, strlen(name), &entry)) != 0) {
- return rc;
- }
- /* The node to remove. */
- rnode = entry->dir_node;
- rnp = BankNodePointer(rnode);
- /* Make sure that this node has no pending open calls. */
- if (rnp->node_refs) {
- return EACCES;
- }
- /* We only remove empty directories. */
- if (rnp->node_type == NODETYPE_DIR) {
- if (rnp->node_links > 2 || !PnutDirIsEmpty(rnode)) {
- return EACCES;
- }
- rnp = BankNodePointer(node);
- rnp->node_links--;
- PnutNodeRelease(rnode);
- }
- /* Remove a file. */
- else {
- PnutNodeRelease(rnode);
- }
- /* Mark this entry unused. */
- PnutDirFindEntry(node, name, strlen(name), &entry);
- entry->dir_inuse = 0;
- return 0;
- }
- /*!
- * \brief Create a new subdirectory.
- *
- * \param path Full path to the directory.
- *
- * \return 0 on success. Otherwise -1 is returned.
- */
- static int PnutDirCreate(const char *path)
- {
- PNUT_BLKNUM node;
- PNUT_FINDRESULT found;
- int ec;
- /* Return an error if this entry already exists. */
- if ((ec = PnutDirFindPath(root, path, &found)) == 0) {
- ec = EEXIST;
- }
- /* If this component does not exist, we can create it... */
- else if (ec == ENOENT) {
- /* ...provided that all parents had been found. */
- if (found.fr_name) {
- /* Allocate a new node block. */
- if ((node = PnutNodeAlloc(NODETYPE_DIR)) < 0) {
- ec = ENOSPC;
- }
- /* Create a reference to itself. */
- else if ((ec = PnutDirAddEntry(node, ".", node)) != 0) {
- PnutNodeRelease(node);
- }
- /* Create a reference to the parent. */
- else if ((ec = PnutDirAddEntry(node, "..", found.fr_pnode)) != 0) {
- PnutNodeRelease(node);
- }
- /* Create a reference in the parent. */
- else if ((ec = PnutDirAddEntry(found.fr_pnode, found.fr_name, node)) != 0) {
- PnutNodeRelease(node);
- }
- }
- }
- /* Something went wrong. */
- if (ec) {
- errno = ec;
- return -1;
- }
- return 0;
- }
- /* ------------------------ File routines ------------------------ */
- /*!
- * \brief Open a file.
- *
- * This function is called by the low level open routine of the C runtime
- * library, using the _NUTDEVICE::dev_open entry.
- *
- * \param dev Pointer to the NUTDEVICE structure.
- * \param path Pathname of the file to open.
- * \param mode Operation mode. Any of the following values may be or-ed:
- * - \ref _O_RDONLY
- * - \ref _O_WRONLY
- * - \ref _O_RDWR
- * - \ref _O_APPEND
- * - \ref _O_CREAT
- * - \ref _O_TRUNC
- * - \ref _O_EXCL
- * - \ref _O_TEXT
- * - \ref _O_BINARY
- * \param acc Ignored, should be zero.
- *
- * \return Pointer to a NUTFILE structure if successful or NUTFILE_EOF otherwise.
- */
- static NUTFILE *PnutFileOpen(NUTDEVICE * dev, const char *path, int mode, int acc)
- {
- PNUT_BLKNUM node = -1;
- PNUT_FINDRESULT found;
- int rc;
- PNUTFILE *file;
- NUTFILE *nfp = NUTFILE_EOF;
- /* Try to find an exisiting file. */
- if ((rc = PnutDirFindPath(root, path, &found)) == 0) {
- /* Return an error, if this is no regular file. */
- if (BankNodePointer(found.fr_node)->node_type != NODETYPE_REG) {
- errno = EISDIR;
- }
- /*
- * We return an error, if the file exists and _O_EXCL has been
- * set with _O_CREAT.
- */
- else if ((mode & (_O_CREAT | _O_EXCL)) == (_O_CREAT | _O_EXCL)) {
- errno = EEXIST;
- } else {
- node = found.fr_node;
- if (mode & _O_TRUNC) {
- PnutNodeDataRelease(node);
- }
- }
- }
- else if (rc == ENOENT) {
- /*
- * If the search failed on the last path component, then
- * PnutDirFindPath() set fr_name. Only in this case we can
- * create a new file.
- */
- if (found.fr_name && (mode & _O_CREAT)) {
- node = PnutNodeAlloc(NODETYPE_REG);
- if (node < 0) {
- errno = ENOSPC;
- return NUTFILE_EOF;
- }
- rc = PnutDirAddEntry(found.fr_pnode, found.fr_name, node);
- if (rc) {
- PnutBlockRelease(node);
- }
- }
- }
- if (rc == 0 && node >= 0) {
- if ((file = malloc(sizeof(PNUTFILE))) != 0) {
- file->f_flag |= mode & (_O_RDONLY | _O_WRONLY | _O_APPEND);
- file->f_pos = (mode & _O_APPEND) ? BankNodePointer(node)->node_size : 0;
- file->f_node = node;
- if ((nfp = malloc(sizeof(NUTFILE))) == 0) {
- free(file);
- nfp = NUTFILE_EOF;
- } else {
- nfp->nf_dev = dev;
- nfp->nf_fcb = file;
- /* Keep track of the number of open calls. */
- BankNodePointer(node)->node_refs++;
- }
- }
- }
- return nfp;
- }
- /*!
- * \brief Close a file.
- */
- static int PnutFileClose(NUTFILE * nfp)
- {
- if (nfp != NUTFILE_EOF) {
- PNUTFILE *fp = nfp->nf_fcb;
- if (fp) {
- BankNodePointer(fp->f_node)->node_refs--;
- free(fp);
- }
- free(nfp);
- return 0;
- }
- return EINVAL;
- }
- /*!
- * \brief Delete a file or directory.
- *
- * \param path Full path to the directory.
- *
- * \return 0 on success. Otherwise -1 is returned.
- */
- static int PnutDelete(char *path)
- {
- int ec;
- PNUT_FINDRESULT found;
- if ((ec = PnutDirFindPath(root, path, &found)) == 0) {
- ec = PnutDirDelEntry(found.fr_pnode, found.fr_name);
- }
- if (ec) {
- errno = ec;
- return -1;
- }
- return 0;
- }
- /*!
- * \brief Return information about a specified file entry.
- *
- * \param path Pointer to the full pathname of the file.
- * \param status Pointer to a stat structure for the information returned.
- *
- * \return 0 on success. Otherwise -1 is returned.
- */
- static int PnutStatus(const char *path, struct stat *status)
- {
- int rc;
- PNUT_FINDRESULT found;
- if ((rc = PnutDirFindPath(root, path, &found)) == 0) {
- status->st_mode = BankNodePointer(found.fr_node)->node_type;
- status->st_ino = found.fr_node;
- status->st_nlink = BankNodePointer(found.fr_node)->node_links;
- status->st_size = BankNodePointer(found.fr_node)->node_size;
- status->st_mtime = BankNodePointer(found.fr_node)->node_mtime;
- }
- return rc;
- }
- /*!
- * \brief Return information about a previously opened file.
- *
- * \param nfp Pointer to a \ref NUTFILE structure, obtained by a previous
- * call to PnutFileOpen().
- * \param status Pointer to a stat structure for the information returned.
- *
- * \return 0 on success. Otherwise -1 is returned.
- */
- static int PnutFileStatus(PNUTFILE * fp, struct stat *status)
- {
- status->st_mode = BankNodePointer(fp->f_node)->node_type;
- status->st_ino = fp->f_node;
- status->st_nlink = BankNodePointer(fp->f_node)->node_links;
- status->st_size = BankNodePointer(fp->f_node)->node_size;
- status->st_mtime = BankNodePointer(fp->f_node)->node_mtime;
- return 0;
- }
- /*!
- * \brief Write data to a file.
- *
- * \param nfp Pointer to a \ref NUTFILE structure, obtained by a previous
- * call to PnutFileOpen().
- * \param buffer Pointer to the data to be written. If zero, then the
- * output buffer will be flushed.
- * \param len Number of bytes to write.
- *
- * \return The number of bytes written. A return value of -1 indicates an
- * error.
- */
- static int PnutFileWrite(NUTFILE * nfp, const void *buffer, int len)
- {
- PNUTFILE *fp = nfp->nf_fcb;
- int ec = 0;
- int rc = 0;
- PNUT_BLKNUM node = fp->f_node;
- uint8_t *blkptr;
- size_t blksiz;
- const char *buf = buffer;
- while (len) {
- if ((ec = PnutNodeGetDataPtr(node, fp->f_pos, &blkptr, &blksiz, 1)) != 0) {
- break;
- }
- if (blksiz >= len) {
- blksiz = len;
- len = 0;
- } else {
- len -= blksiz;
- }
- memcpy(blkptr, buf, blksiz);
- rc += blksiz;
- buf += blksiz;
- fp->f_pos += blksiz;
- }
- if (ec == 0 || ec == ENOSPC) {
- if (BankNodePointer(node)->node_size < fp->f_pos) {
- BankNodePointer(node)->node_size = fp->f_pos;
- }
- BankNodePointer(node)->node_mtime = time(0);
- }
- return rc;
- }
- #ifdef __HARVARD_ARCH__
- static int PnutFileWrite_P(NUTFILE * nfp, PGM_P buffer, int len)
- {
- return -1;
- }
- #endif
- /*!
- * \brief Read data from a file.
- *
- * \param nfp Pointer to a ::NUTFILE structure, obtained by a previous
- * call to PnutFileOpen().
- * \param buffer Pointer to the data buffer to fill.
- * \param len Maximum number of bytes to read.
- *
- * \return The number of bytes actually read. A return value of -1 indicates
- * an error.
- */
- static int PnutFileRead(NUTFILE * nfp, void *buffer, int len)
- {
- PNUTFILE *fp = nfp->nf_fcb;
- int ec = 0;
- int rc = 0;
- PNUT_BLKNUM node = fp->f_node;
- uint8_t *blkptr;
- size_t blksiz;
- char *buf = buffer;
- /* Respect end of file. */
- if (len > BankNodePointer(node)->node_size - fp->f_pos) {
- len = (size_t) (BankNodePointer(node)->node_size - fp->f_pos);
- }
- while (len) {
- if ((ec = PnutNodeGetDataPtr(node, fp->f_pos, &blkptr, &blksiz, 0)) != 0) {
- break;
- }
- if (blksiz >= len) {
- blksiz = len;
- len = 0;
- } else {
- len -= blksiz;
- }
- memcpy(buf, blkptr, blksiz);
- rc += blksiz;
- buf += blksiz;
- fp->f_pos += blksiz;
- }
- return rc;
- }
- static int PnutFileSeek(PNUTFILE * fp, long *pos, int whence)
- {
- int rc = 0;
- long npos = *pos;
- switch (whence) {
- case SEEK_CUR:
- npos += fp->f_pos;
- break;
- case SEEK_END:
- npos += BankNodePointer(fp->f_node)->node_size;
- break;
- }
- if (npos < 0 || npos > (long) BankNodePointer(fp->f_node)->node_size) {
- rc = EINVAL;
- } else {
- fp->f_pos = npos;
- *pos = npos;
- }
- return rc;
- }
- /* ------------------------ Special function interface ------------------------ */
- /*!
- * \brief Device specific functions.
- */
- int PnutIOCtl(NUTDEVICE * dev, int req, void *conf)
- {
- int rc = -1;
- switch (req) {
- case FS_STATUS:
- {
- FSCP_STATUS *par = (FSCP_STATUS *) conf;
- rc = PnutStatus(par->par_path, par->par_stp);
- }
- break;
- case FS_DIR_CREATE:
- rc = PnutDirCreate((char *) conf);
- break;
- case FS_DIR_REMOVE:
- rc = PnutDelete((char *) conf);
- break;
- case FS_DIR_OPEN:
- rc = PnutDirOpen(dev, (DIR *) conf);
- break;
- case FS_DIR_CLOSE:
- rc = PnutDirClose((DIR *) conf);
- break;
- case FS_DIR_READ:
- rc = PnutDirRead((DIR *) conf);
- break;
- case FS_FILE_STATUS:
- rc = PnutFileStatus((PNUTFILE *) ((IOCTL_ARG2 *) conf)->arg1, /* */
- (struct stat *) ((IOCTL_ARG2 *) conf)->arg2);
- break;
- case FS_FILE_DELETE:
- rc = PnutDelete((char *) conf);
- break;
- case FS_FILE_SEEK:
- PnutFileSeek((PNUTFILE *) ((IOCTL_ARG3 *) conf)->arg1, /* */
- (long *) ((IOCTL_ARG3 *) conf)->arg2, /* */
- (int) ((IOCTL_ARG3 *) conf)->arg3);
- break;
- }
- return rc;
- }
- /*!
- * \brief Retrieve the size of a previously opened file.
- *
- * This function is called by the low level size routine of the C runtime
- * library, using the _NUTDEVICE::dev_size entry.
- *
- * \param nfp Pointer to a \ref _NUTFILE structure, obtained by a
- * previous call to PhatFileOpen().
- *
- * \return Size of the file.
- */
- static long PnutFileSize(NUTFILE *nfp)
- {
- PNUTFILE *fp = nfp->nf_fcb;
- return BankNodePointer(fp->f_node)->node_size;
- }
- /* ------------------------ Initialization ------------------------ */
- /*!
- * \brief Initialize the Peanut filesystem.
- *
- * Peanut is a volatile RAM filesystem. All contents is lost when
- * power supply is removed. After initialization we start with
- * a clean filesystem.
- *
- * \param Pointer to the device information structure.
- *
- * \return Zero on success. Otherwise an error code is returned.
- */
- static int PnutInit(NUTDEVICE * dev)
- {
- PNUT_BLKNUM i;
- int rc;
- /* Add all blocks to the free list. */
- for (i = 0; i < PNUT_TOTAL_BLOCKS; i++) {
- PnutBlockRelease(i);
- }
- /* Initialize the root directory. */
- if ((root = PnutNodeAlloc(NODETYPE_DIR)) == -1) {
- rc = ENOSPC;
- } else {
- if ((rc = PnutDirAddEntry(root, ".", root)) == 0) {
- rc = PnutDirAddEntry(root, "..", root);
- }
- if (rc) {
- PnutBlockRelease(root);
- }
- }
- return rc;
- }
- /*!
- * \brief Peanut device information structure.
- */
- NUTDEVICE devPnut = {
- 0, /*!< Pointer to next device, dev_next. */
- {'P', 'N', 'U', 'T', 0, 0, 0, 0, 0}
- , /*!< Unique device name, dev_name. */
- IFTYP_RAM, /*!< Type of device, dev_type. Obsolete. */
- 0, /*!< Base address, dev_base. Unused. */
- 0, /*!< First interrupt number, dev_irq. Unused. */
- 0, /*!< Interface control block, dev_icb. Unused. */
- 0, /*!< Driver control block, dev_dcb. Unused. */
- PnutInit, /*!< Driver initialization routine, dev_init. */
- PnutIOCtl, /*!< Driver specific control function, dev_ioctl. */
- PnutFileRead, /*!< Read data from a file, dev_read. */
- PnutFileWrite, /*!< Write data to a file, dev_write. */
- #ifdef __HARVARD_ARCH__
- PnutFileWrite_P, /*!< Write data from program space to a file, dev_write_P. */
- #endif
- PnutFileOpen, /*!< Open a file, dev_open. */
- PnutFileClose, /*!< Close a file, dev_close. */
- PnutFileSize, /*!< Return file size, dev_size. */
- NULL, /*!< Select function, optional, not yet implemented */
- };
- /*@}*/
|