pnutfs.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2004-2006 by egnite Software GmbH. All rights reserved.
00003  *
00004  * Redistribution and use in source and binary forms, with or without
00005  * modification, are permitted provided that the following conditions
00006  * are met:
00007  *
00008  * 1. Redistributions of source code must retain the above copyright
00009  *    notice, this list of conditions and the following disclaimer.
00010  * 2. Redistributions in binary form must reproduce the above copyright
00011  *    notice, this list of conditions and the following disclaimer in the
00012  *    documentation and/or other materials provided with the distribution.
00013  * 3. Neither the name of the copyright holders nor the names of
00014  *    contributors may be used to endorse or promote products derived
00015  *    from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY EGNITE SOFTWARE GMBH AND CONTRIBUTORS
00018  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00019  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00020  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL EGNITE
00021  * SOFTWARE GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00022  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00023  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
00024  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
00025  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00026  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
00027  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00028  * SUCH DAMAGE.
00029  *
00030  * For additional information see http://www.ethernut.de/
00031  */
00032 
00088 #include <compiler.h>
00089 
00090 #include <stdlib.h>
00091 #include <string.h>
00092 #include <time.h>
00093 #include <fcntl.h>
00094 #include <dirent.h>
00095 #include <errno.h>
00096 
00097 #include <sys/stat.h>
00098 #include <sys/file.h>
00099 #include <sys/bankmem.h>
00100 
00101 #include <fs/fs.h>
00102 #include <dev/pnut.h>
00103 
00108 
00115 
00120 #ifndef PNUT_BLOCK_SIZE
00121 #define PNUT_BLOCK_SIZE     512
00122 #endif
00123 
00130 #ifndef PNUT_DIRENT_SIZE
00131 #define PNUT_DIRENT_SIZE  32
00132 #endif
00133 
00146 #ifndef PNUT_BLOCKS_PER_NODE
00147 #define PNUT_BLOCKS_PER_NODE      250
00148 #endif
00149 
00150 #ifndef PNUTBANK_COUNT
00151 #ifdef ARTHERNET1
00152 /* Default for Arthernet 1. */
00153 #define PNUTBANK_COUNT 15
00154 #elif MMNET02  || MMNET03  || MMNET04 ||\
00155       MMNET102 || MMNET103 || MMNET104  
00156 /* Default for MMnte02. */
00157 #define PNUTBANK_COUNT 6
00158 #else
00159 /* Default for Ethernet 2. */
00160 #define PNUTBANK_COUNT 30
00161 #endif
00162 #endif
00163 
00167 #ifndef SEEK_SET
00168 #  define SEEK_SET        0     /* Seek from beginning of file.  */
00169 #  define SEEK_CUR        1     /* Seek from current position.  */
00170 #  define SEEK_END        2     /* Set file pointer to EOF plus "offset" */
00171 #endif
00172 
00173 #define NODETYPE_REG        0
00174 #define NODETYPE_DIR        1
00175 
00176 
00177 typedef short PNUT_BLKNUM;
00178 
00197 typedef struct {
00198     uint8_t node_type;           
00199     uint8_t node_refs;           
00210     uint16_t node_links;
00211     uint32_t node_size;           
00212     time_t node_mtime;          
00213     PNUT_BLKNUM node_blocks[PNUT_BLOCKS_PER_NODE];      
00214 } PNUT_NODE;
00215 
00222 #define PNUT_MAX_NAMELEN    (PNUT_DIRENT_SIZE - sizeof(PNUT_BLKNUM) - sizeof(uint8_t) - 1)
00223 
00232 typedef struct {
00238     PNUT_BLKNUM dir_node;
00244     uint8_t dir_inuse;
00250     char dir_name[PNUT_MAX_NAMELEN + 1];
00251 } PNUT_DIRENTRY;
00252 
00259 #define PNUT_MAX_FILESIZE    (PNUT_BLOCKS_PER_NODE * PNUT_BLOCK_SIZE)
00260 
00264 typedef struct {
00268     PNUT_BLKNUM fr_node;
00269 
00273     PNUT_BLKNUM fr_pnode;
00274 
00280     CONST char *fr_name;
00281 } PNUT_FINDRESULT;
00282 
00286 typedef struct _PNUTFILE PNUTFILE;
00287 
00291 struct _PNUTFILE {
00292     PNUT_BLKNUM f_node;         /* Node of the file or directory. */
00293     uint32_t f_pos;               /* Current file pointer position. */
00294     u_int f_flag;               /* File mode. */
00295 };
00296 
00298 static PNUT_BLKNUM root;
00299 
00300 /* -------------------- Banking hardware routines ------------------- */
00301 
00303 #ifndef NUTBANK_SIZE
00304 #define NUTBANK_SIZE 16384
00305 #endif
00306 
00317 #ifndef PNUT_TOTAL_BLOCKS
00318 #define PNUT_TOTAL_BLOCKS   (PNUTBANK_COUNT * (NUTBANK_SIZE / PNUT_BLOCK_SIZE))
00319 #endif
00320 
00321 #define BLOCKS_PER_BANK  (NUTBANK_SIZE / PNUT_BLOCK_SIZE)
00322 
00323 #ifndef NUTBANK_SR
00324 #define NUTBANK_SR 0xFF00
00325 #endif
00326 
00327 #ifndef NUTBANK_START
00328 #define NUTBANK_START 0x8000
00329 #endif
00330 
00331 #define NUTBANK_PTR ((char *)NUTBANK_START)
00332 
00344 void BankSelect(PNUT_BLKNUM blk)
00345 {
00346 
00347 // This is a hack to avoid compiler warning if no banking is enabled... 
00348 // But I don't like moving code to header files at all.. (Ole Reinhardt)
00349 #if NUTBANK_COUNT 
00350     int bank = blk / BLOCKS_PER_BANK;
00351 #endif
00352     // Bankswitching is now handled in bankmem.h
00353     NutSegBufEnable(bank);
00354 }
00355 
00359 PNUT_NODE *BankNodePointer(PNUT_BLKNUM blk)
00360 {
00361     if (blk < 0) {
00362         return NULL;
00363     }
00364     BankSelect(blk);
00365     return (PNUT_NODE *) & NUTBANK_PTR[(blk % BLOCKS_PER_BANK) * PNUT_BLOCK_SIZE];
00366 }
00367 
00368 
00369 /* ------------------------ Block routines ------------------------ */
00370 
00371 /*
00372  * \brief Index of the first free block.
00373  *
00374  * All free blocks are collected in a linked list. This global variable 
00375  * contains the index of the first free block. Each free block contains 
00376  * nothing but the index of the next free block, which is -1 in the last 
00377  * member of this list.
00378  *
00379  * If this variable is set to -1, then there are no more free blocks 
00380  * available. During file system initialization all available blocks are 
00381  * added to this list.
00382  */
00383 static PNUT_BLKNUM blockFreeList = -1;
00384 
00392 static PNUT_BLKNUM PnutBlockAlloc(void)
00393 {
00394     PNUT_BLKNUM rc = blockFreeList;
00395 
00396     if (rc >= 0) {
00397         PNUT_BLKNUM *bankptr = (PNUT_BLKNUM *) BankNodePointer(blockFreeList);
00398         blockFreeList = *bankptr;
00399         /* Clear the block contents. */
00400         memset(bankptr, 0, PNUT_BLOCK_SIZE);
00401     }
00402     return rc;
00403 }
00404 
00412 static void PnutBlockRelease(PNUT_BLKNUM blk)
00413 {
00414     PNUT_BLKNUM *bankptr = (PNUT_BLKNUM *) BankNodePointer(blk);
00415 
00416     *bankptr = blockFreeList;
00417     blockFreeList = blk;
00418 }
00419 
00420 /* ------------------------ Node routines ------------------------ */
00421 
00431 static PNUT_BLKNUM PnutNodeAlloc(uint8_t type)
00432 {
00433     PNUT_BLKNUM node = PnutBlockAlloc();
00434 
00435     if (node >= 0) {
00436         int i;
00437         PNUT_NODE *np = BankNodePointer(node);
00438 
00439         /* Clear the data block list of this node. */
00440         for (i = 0; i < PNUT_BLOCKS_PER_NODE; i++) {
00441             np->node_blocks[i] = -1;
00442         }
00443 
00444         /* Set the type and the last modification date. */
00445         np->node_type = type;
00446         np->node_mtime = time(0);
00447     }
00448     return node;
00449 }
00450 
00451 /*
00452  * \brief Free all blocks of a specified node.
00453  */
00454 static void PnutNodeDataRelease(PNUT_BLKNUM node)
00455 {
00456     int i;
00457 
00458     for (i = 0; i < PNUT_BLOCKS_PER_NODE; i++) {
00459         if (BankNodePointer(node)->node_blocks[i] >= 0) {
00460             PnutBlockRelease(BankNodePointer(node)->node_blocks[i]);
00461             BankNodePointer(node)->node_blocks[i] = -1;
00462         }
00463     }
00464     BankNodePointer(node)->node_size = 0;
00465     BankNodePointer(node)->node_mtime = time(0);
00466 }
00467 
00471 static void PnutNodeRelease(PNUT_BLKNUM node)
00472 {
00473     PnutNodeDataRelease(node);
00474     PnutBlockRelease(node);
00475 }
00476 
00493 static int PnutNodeGetDataPtr(PNUT_BLKNUM node, uint32_t pos, void **buffer, size_t * size, int create)
00494 {
00495     int blkidx;                 /* Number of full blocks */
00496     int rc = EINVAL;
00497 
00498     *buffer = NULL;
00499     *size = 0;
00500 
00501     /* Calculate the block index. Make sure it fits in our maximum file size. */
00502     if ((blkidx = pos / PNUT_BLOCK_SIZE) < PNUT_BLOCKS_PER_NODE) {
00503         PNUT_BLKNUM blk;
00504 
00505         /* Get the data block number. Optionally allocate a new block. */
00506         if ((blk = BankNodePointer(node)->node_blocks[blkidx]) < 0 && create) {
00507             if ((blk = PnutBlockAlloc()) < 0) {
00508                 rc = ENOSPC;
00509             } else {
00510                 BankNodePointer(node)->node_blocks[blkidx] = blk;
00511             }
00512         }
00513 
00514         /* 
00515          * If we got a valid block, then set the pointer at the specified
00516          * position and the remaining bytes within this block.
00517          */
00518         if (blk >= 0) {
00519             char *blkptr = (char *) BankNodePointer(blk);
00520             int blkpos = pos % PNUT_BLOCK_SIZE; /* Position within block */
00521 
00522             *buffer = blkptr + blkpos;
00523             *size = PNUT_BLOCK_SIZE - blkpos;
00524             rc = 0;
00525         }
00526     }
00527     return rc;
00528 }
00529 
00530 
00531 /* ------------------------ Directory routines ------------------------ */
00532 
00536 static int PnutDirIsEmpty(PNUT_BLKNUM node)
00537 {
00538     int rc = 1;
00539     uint32_t pos;
00540     size_t size;
00541     PNUT_DIRENTRY *entry;
00542 
00543     /* Loop through the data blocks of this directory node. */
00544     for (pos = 0; pos < BankNodePointer(node)->node_size; pos += PNUT_DIRENT_SIZE) {
00545 
00546         /* Get the pointer to the next directory entry. */
00547         if (PnutNodeGetDataPtr(node, pos, (void *) &entry, &size, 0) || size == 0) {
00548             /* No more entries. */
00549             break;
00550         }
00551 
00552         if (size >= PNUT_DIRENT_SIZE) {
00553             if (entry->dir_inuse && strcmp(entry->dir_name, ".") && strcmp(entry->dir_name, "..")) {
00554                 /* Entry found */
00555                 rc = 0;
00556                 break;
00557             }
00558         }
00559     }
00560     return rc;
00561 }
00562 
00576 static int PnutDirFindEntry(PNUT_BLKNUM node, CONST char *path, size_t len, PNUT_DIRENTRY ** entry)
00577 {
00578     int rc = ENOENT;
00579     uint32_t pos;
00580     size_t size;
00581 
00582     /* Loop through the data blocks of this directory node. */
00583     for (pos = 0; pos < BankNodePointer(node)->node_size; pos += PNUT_DIRENT_SIZE) {
00584 
00585         /* Get the pointer to the next directory entry. */
00586         if (PnutNodeGetDataPtr(node, pos, (void **) entry, &size, 0) || size == 0) {
00587             /* No more entries. */
00588             break;
00589         }
00590 
00591         /* Compare this entry. */
00592         if (size >= PNUT_DIRENT_SIZE) { /* Skip entries across block boundaries. */
00593             if ((*entry)->dir_inuse &&  /* Skip unused entries. */
00594                 strlen((*entry)->dir_name) == len &&    /* Skip non-matching names. */
00595                 strncmp((*entry)->dir_name, path, len) == 0) {
00596                 /* Requested entry found */
00597                 rc = 0;
00598                 break;
00599             }
00600         }
00601     }
00602     return rc;
00603 }
00604 
00605 /*
00606  * \brief Find directory entry of a specified path.
00607  *
00608  * \param node   First node block to search.
00609  * \param path   Path to find.
00610  * \param result Search result.
00611  *
00612  * \return Error code.
00613  */
00614 static int PnutDirFindPath(PNUT_BLKNUM node, CONST char *path, PNUT_FINDRESULT * result)
00615 {
00616     int rc = 0;
00617     size_t len;
00618     PNUT_DIRENTRY *entry;
00619 
00620     result->fr_pnode = node;
00621     result->fr_node = -1;
00622     result->fr_name = NULL;
00623 
00624     while (*path == '/') {
00625         path++;
00626     }
00627 
00628     if (*path == 0) {
00629         path = ".";
00630     }
00631 
00632     /* 
00633      * Loop for each path component.
00634      */
00635     while (*path) {
00636         CONST char *cp;
00637 
00638         /* Make sure that this is a directory node. */
00639         if (BankNodePointer(node)->node_type != NODETYPE_DIR) {
00640             rc = ENOTDIR;
00641             break;
00642         }
00643 
00644         /* Retrieve the length of first path component. */
00645         for (len = 0, cp = path; *cp && *cp != '/'; cp++) {
00646             len++;
00647         }
00648 
00649         /* 
00650          * If this component is last, we found all parents.
00651          * Keep the last one stored in the result.
00652          */
00653         if (*cp == 0) {
00654             result->fr_name = path;
00655         }
00656 
00657         /* Try to find this component. */
00658         if ((rc = PnutDirFindEntry(node, path, len, &entry)) != 0) {
00659             rc = ENOENT;
00660             break;
00661         }
00662 
00663         /* Component found. Return if this was the last one. */
00664         result->fr_node = entry->dir_node;
00665         if (*cp == 0) {
00666             break;
00667         }
00668 
00669         /* Not the last component. Store node as previous node. */
00670         result->fr_pnode = result->fr_node;
00671 
00672         /* Move forward to the next path component. */
00673         node = result->fr_node;
00674         path += len;
00675         while (*path == '/') {
00676             path++;
00677         }
00678     }
00679     return rc;
00680 }
00681 
00682 
00693 static int PnutDirOpen(NUTDEVICE * dev, DIR * dir)
00694 {
00695     PNUTFILE *fp;
00696     PNUT_FINDRESULT found;
00697     int rc;
00698 
00699     /* Locate the entry in our directory structure. */
00700     if ((rc = PnutDirFindPath(root, dir->dd_buf, &found)) != 0) {
00701         errno = rc;
00702         rc = -1;
00703     } else {
00704         if (BankNodePointer(found.fr_node)->node_type != NODETYPE_DIR) {
00705             errno = ENOTDIR;
00706             rc = -1;
00707         }
00708         /* Allocate a PNUT file descriptor. */
00709         else if ((fp = malloc(sizeof(PNUTFILE))) == 0) {
00710             rc = -1;
00711         }
00712         /* 
00713          * Initialize the descriptor and store it in the directory
00714          * information structure.
00715          */
00716         else {
00717             fp->f_node = found.fr_node;
00718             fp->f_pos = 0;
00719 
00720             if ((dir->dd_fd = malloc(sizeof(NUTFILE))) == 0) {
00721                 free(fp);
00722                 rc = -1;
00723             }
00724             else {
00725                 memset(dir->dd_fd, 0, sizeof(NUTFILE));
00726                 dir->dd_fd->nf_dev = dev;
00727                 dir->dd_fd->nf_fcb = fp;
00728                 /* Keep track of the number of open calls. */
00729                 BankNodePointer(fp->f_node)->node_refs++;
00730             }
00731         }
00732     }
00733     return rc;
00734 }
00735 
00739 static int PnutDirClose(DIR * dir)
00740 {
00741     if (dir && dir->dd_fd) {
00742         if (dir->dd_fd->nf_fcb) {
00743             PNUTFILE *fp = dir->dd_fd->nf_fcb;
00744 
00745             BankNodePointer(fp->f_node)->node_refs--;
00746             free(fp);
00747         }
00748         free(dir->dd_fd);
00749         return 0;
00750     }
00751     return EINVAL;
00752 }
00753 
00757 static int PnutDirRead(DIR * dir)
00758 {
00759     int rc = -1;
00760     uint32_t pos;
00761     PNUT_DIRENTRY *entry;
00762     size_t size;
00763     PNUTFILE *fp = dir->dd_fd->nf_fcb;
00764     struct dirent *ent = (struct dirent *) dir->dd_buf;
00765 
00766     ent->d_name[0] = 0;
00767     for (pos = fp->f_pos; pos < BankNodePointer(fp->f_node)->node_size; pos += PNUT_DIRENT_SIZE) {
00768         /* Retrieve the entry at the current position. */
00769         rc = PnutNodeGetDataPtr(fp->f_node, pos, (void *) &entry, &size, 0);
00770         if (rc || size == 0) {
00771             /* No more entries. */
00772             rc = -1;
00773             break;
00774         }
00775         fp->f_pos = pos + PNUT_DIRENT_SIZE;
00776 
00777         /* Skip entries across block boundaries and unused entires. */
00778         if (size >= PNUT_DIRENT_SIZE && entry->dir_inuse) {
00779             memset(ent, 0, sizeof(struct dirent));
00780             ent->d_fileno = entry->dir_node;
00781             ent->d_namlen = (uint8_t) strlen(entry->dir_name);
00782             strcpy(ent->d_name, entry->dir_name);
00783             break;
00784         }
00785     }
00786     return rc;
00787 }
00788 
00798 static int PnutDirAddEntry(PNUT_BLKNUM dnode, CONST char *name, PNUT_BLKNUM enode)
00799 {
00800     int rc = 0;
00801     uint32_t pos = 0;
00802     size_t size;
00803     PNUT_DIRENTRY *entry;
00804     PNUT_NODE *np;
00805 
00806     /*
00807      * Loop through all directory entries until an unused one
00808      * is found. If required, a new data block is allocated,
00809      * so success is guaranteed unless we run out of free
00810      * blocks.
00811      */
00812     for (;;) {
00813         /* 
00814          * Get the data pointer to the specified position. Automatically
00815          * create a new block if we reached the end of the data.
00816          */
00817         if ((rc = PnutNodeGetDataPtr(dnode, pos, (void *) &entry, &size, 1)) != 0) {
00818             break;
00819         }
00820         pos += PNUT_DIRENT_SIZE;
00821 
00822         /* Process entry if it doesn't cross block boundaries. */
00823         if (size >= PNUT_DIRENT_SIZE) {
00824             /* Did we find a previously released or a newly allocated entry? */
00825             if (entry->dir_inuse == 0) {
00826                 /* Update the entry. */
00827                 entry->dir_node = enode;
00828                 entry->dir_inuse = 1;
00829                 strcpy(entry->dir_name, name);
00830 
00831                 /* Update the directory node. */
00832                 np = BankNodePointer(dnode);
00833                 np->node_mtime = time(0);
00834                 if (pos > np->node_size) {
00835                     np->node_size = pos;
00836                 }
00837 
00838                 /* Update the entry's node. */
00839                 np = BankNodePointer(enode);
00840                 np->node_links++;
00841                 break;
00842             }
00843         }
00844     }
00845     return rc;
00846 }
00847 
00864 static int PnutDirDelEntry(PNUT_BLKNUM node, CONST char *name)
00865 {
00866     int rc;
00867     PNUT_DIRENTRY *entry;
00868     PNUT_NODE *rnp;
00869     PNUT_BLKNUM rnode;
00870 
00871     /* Make sure this entry exists. */
00872     if ((rc = PnutDirFindEntry(node, name, strlen(name), &entry)) != 0) {
00873         return rc;
00874     }
00875 
00876     /* The node to remove. */
00877     rnode = entry->dir_node;
00878     rnp = BankNodePointer(rnode);
00879 
00880     /* Make sure that this node has no pending open calls. */
00881     if (rnp->node_refs) {
00882         return EACCES;
00883     }
00884 
00885     /* We only remove empty directories. */
00886     if (rnp->node_type == NODETYPE_DIR) {
00887         if (rnp->node_links > 2 || !PnutDirIsEmpty(rnode)) {
00888             return EACCES;
00889         }
00890         rnp = BankNodePointer(node);
00891         rnp->node_links--;
00892         PnutNodeRelease(rnode);
00893     }
00894 
00895     /* Remove a file. */
00896     else {
00897         PnutNodeRelease(rnode);
00898     }
00899 
00900     /* Mark this entry unused. */
00901     PnutDirFindEntry(node, name, strlen(name), &entry);
00902     entry->dir_inuse = 0;
00903 
00904     return 0;
00905 }
00906 
00914 static int PnutDirCreate(CONST char *path)
00915 {
00916     PNUT_BLKNUM node;
00917     PNUT_FINDRESULT found;
00918     int ec;
00919 
00920     /* Return an error if this entry already exists. */
00921     if ((ec = PnutDirFindPath(root, path, &found)) == 0) {
00922         ec = EEXIST;
00923     }
00924 
00925     /* If this component does not exist, we can create it... */
00926     else if (ec == ENOENT) {
00927         /* ...provided that all parents had been found. */
00928         if (found.fr_name) {
00929             /* Allocate a new node block. */
00930             if ((node = PnutNodeAlloc(NODETYPE_DIR)) < 0) {
00931                 ec = ENOSPC;
00932             }
00933             /* Create a reference to itself. */
00934             else if ((ec = PnutDirAddEntry(node, ".", node)) != 0) {
00935                 PnutNodeRelease(node);
00936             }
00937             /* Create a reference to the parent. */
00938             else if ((ec = PnutDirAddEntry(node, "..", found.fr_pnode)) != 0) {
00939                 PnutNodeRelease(node);
00940             }
00941             /* Create a reference in the parent. */
00942             else if ((ec = PnutDirAddEntry(found.fr_pnode, found.fr_name, node)) != 0) {
00943                 PnutNodeRelease(node);
00944             }
00945         }
00946     }
00947 
00948     /* Something went wrong. */
00949     if (ec) {
00950         errno = ec;
00951         return -1;
00952     }
00953     return 0;
00954 }
00955 
00956 /* ------------------------ File routines ------------------------ */
00957 
00980 static NUTFILE *PnutFileOpen(NUTDEVICE * dev, CONST char *path, int mode, int acc)
00981 {
00982     PNUT_BLKNUM node = -1;
00983     PNUT_FINDRESULT found;
00984     int rc;
00985     PNUTFILE *file;
00986     NUTFILE *nfp = NUTFILE_EOF;
00987 
00988     /* Try to find an exisiting file. */
00989     if ((rc = PnutDirFindPath(root, path, &found)) == 0) {
00990         /* Return an error, if this is no regular file. */
00991         if (BankNodePointer(found.fr_node)->node_type != NODETYPE_REG) {
00992             errno = EISDIR;
00993         }
00994 
00995         /*
00996          * We return an error, if the file exists and _O_EXCL has been
00997          * set with _O_CREAT.
00998          */
00999         else if ((mode & (_O_CREAT | _O_EXCL)) == (_O_CREAT | _O_EXCL)) {
01000             errno = EEXIST;
01001         } else {
01002             node = found.fr_node;
01003             if (mode & _O_TRUNC) {
01004                 PnutNodeDataRelease(node);
01005             }
01006         }
01007     }
01008 
01009     else if (rc == ENOENT) {
01010         /*
01011          * If the search failed on the last path component, then
01012          * PnutDirFindPath() set fr_name. Only in this case we can
01013          * create a new file.
01014          */
01015         if (found.fr_name && (mode & _O_CREAT)) {
01016             node = PnutNodeAlloc(NODETYPE_REG);
01017 
01018             if (node < 0) {
01019                 errno = ENOSPC;
01020                 return NUTFILE_EOF;
01021             }
01022 
01023             rc = PnutDirAddEntry(found.fr_pnode, found.fr_name, node);
01024 
01025             if (rc) {
01026                 PnutBlockRelease(node);
01027             }
01028         }
01029     }
01030 
01031     if (rc == 0 && node >= 0) {
01032         if ((file = malloc(sizeof(PNUTFILE))) != 0) {
01033             file->f_flag |= mode & (_O_RDONLY | _O_WRONLY | _O_APPEND);
01034             file->f_pos = (mode & _O_APPEND) ? BankNodePointer(node)->node_size : 0;
01035             file->f_node = node;
01036 
01037             if ((nfp = malloc(sizeof(NUTFILE))) == 0) {
01038                 free(file);
01039                 nfp = NUTFILE_EOF;
01040             } else {
01041                 nfp->nf_next = 0;
01042                 nfp->nf_dev = dev;
01043                 nfp->nf_fcb = file;
01044                 /* Keep track of the number of open calls. */
01045                 BankNodePointer(node)->node_refs++;
01046             }
01047         }
01048     }
01049     return nfp;
01050 }
01051 
01055 static int PnutFileClose(NUTFILE * nfp)
01056 {
01057     if (nfp != NUTFILE_EOF) {
01058         PNUTFILE *fp = nfp->nf_fcb;
01059 
01060         if (fp) {
01061             BankNodePointer(fp->f_node)->node_refs--;
01062             free(fp);
01063         }
01064         free(nfp);
01065 
01066         return 0;
01067     }
01068     return EINVAL;
01069 }
01070 
01078 static int PnutDelete(char *path)
01079 {
01080     int ec;
01081     PNUT_FINDRESULT found;
01082 
01083     if ((ec = PnutDirFindPath(root, path, &found)) == 0) {
01084         ec = PnutDirDelEntry(found.fr_pnode, found.fr_name);
01085     }
01086     if (ec) {
01087         errno = ec;
01088         return -1;
01089     }
01090     return 0;
01091 }
01092 
01101 static int PnutStatus(CONST char *path, struct stat *status)
01102 {
01103     int rc;
01104     PNUT_FINDRESULT found;
01105 
01106     if ((rc = PnutDirFindPath(root, path, &found)) == 0) {
01107         status->st_mode = BankNodePointer(found.fr_node)->node_type;
01108         status->st_ino = found.fr_node;
01109         status->st_nlink = BankNodePointer(found.fr_node)->node_links;
01110         status->st_size = BankNodePointer(found.fr_node)->node_size;
01111         status->st_mtime = BankNodePointer(found.fr_node)->node_mtime;
01112     }
01113     return rc;
01114 }
01115 
01125 static int PnutFileStatus(PNUTFILE * fp, struct stat *status)
01126 {
01127     status->st_mode = BankNodePointer(fp->f_node)->node_type;
01128     status->st_ino = fp->f_node;
01129     status->st_nlink = BankNodePointer(fp->f_node)->node_links;
01130     status->st_size = BankNodePointer(fp->f_node)->node_size;
01131     status->st_mtime = BankNodePointer(fp->f_node)->node_mtime;
01132 
01133     return 0;
01134 }
01135 
01148 static int PnutFileWrite(NUTFILE * nfp, CONST void *buffer, int len)
01149 {
01150     PNUTFILE *fp = nfp->nf_fcb;
01151     int ec = 0;
01152     int rc = 0;
01153     PNUT_BLKNUM node = fp->f_node;
01154     uint8_t *blkptr;
01155     size_t blksiz;
01156     CONST char *buf = buffer;
01157 
01158     while (len) {
01159         if ((ec = PnutNodeGetDataPtr(node, fp->f_pos, (void *) &blkptr, &blksiz, 1)) != 0) {
01160             break;
01161         }
01162         if (blksiz >= len) {
01163             blksiz = len;
01164             len = 0;
01165         } else {
01166             len -= blksiz;
01167         }
01168         memcpy(blkptr, buf, blksiz);
01169         rc += blksiz;
01170         buf += blksiz;
01171         fp->f_pos += blksiz;
01172     }
01173     if (ec == 0 || ec == ENOSPC) {
01174         if (BankNodePointer(node)->node_size < fp->f_pos) {
01175             BankNodePointer(node)->node_size = fp->f_pos;
01176         }
01177         BankNodePointer(node)->node_mtime = time(0);
01178     }
01179     return rc;
01180 }
01181 
01182 #ifdef __HARVARD_ARCH__
01183 static int PnutFileWrite_P(NUTFILE * nfp, PGM_P buffer, int len)
01184 {
01185     return -1;
01186 }
01187 #endif
01188 
01200 static int PnutFileRead(NUTFILE * nfp, void *buffer, int len)
01201 {
01202     PNUTFILE *fp = nfp->nf_fcb;
01203     int ec = 0;
01204     int rc = 0;
01205     PNUT_BLKNUM node = fp->f_node;
01206     uint8_t *blkptr;
01207     size_t blksiz;
01208     char *buf = buffer;
01209 
01210     /* Respect end of file. */
01211     if (len > BankNodePointer(node)->node_size - fp->f_pos) {
01212         len = (size_t) (BankNodePointer(node)->node_size - fp->f_pos);
01213     }
01214     while (len) {
01215         if ((ec = PnutNodeGetDataPtr(node, fp->f_pos, (void *) &blkptr, &blksiz, 0)) != 0) {
01216             break;
01217         }
01218         if (blksiz >= len) {
01219             blksiz = len;
01220             len = 0;
01221         } else {
01222             len -= blksiz;
01223         }
01224         memcpy(buf, blkptr, blksiz);
01225         rc += blksiz;
01226         buf += blksiz;
01227         fp->f_pos += blksiz;
01228     }
01229     return rc;
01230 }
01231 
01232 static int PnutFileSeek(PNUTFILE * fp, long *pos, int whence)
01233 {
01234     int rc = 0;
01235     long npos = *pos;
01236 
01237     switch (whence) {
01238     case SEEK_CUR:
01239         npos += fp->f_pos;
01240         break;
01241     case SEEK_END:
01242         npos += BankNodePointer(fp->f_node)->node_size;
01243         break;
01244     }
01245 
01246     if (npos < 0 || npos > (long) BankNodePointer(fp->f_node)->node_size) {
01247         rc = EINVAL;
01248     } else {
01249         fp->f_pos = npos;
01250         *pos = npos;
01251     }
01252     return rc;
01253 }
01254 
01255 /* ------------------------ Special function interface ------------------------ */
01256 
01260 int PnutIOCtl(NUTDEVICE * dev, int req, void *conf)
01261 {
01262     int rc = -1;
01263 
01264     switch (req) {
01265     case FS_STATUS:
01266         {
01267             FSCP_STATUS *par = (FSCP_STATUS *) conf;
01268 
01269             rc = PnutStatus(par->par_path, par->par_stp);
01270         }
01271         break;
01272     case FS_DIR_CREATE:
01273         rc = PnutDirCreate((char *) conf);
01274         break;
01275     case FS_DIR_REMOVE:
01276         rc = PnutDelete((char *) conf);
01277         break;
01278     case FS_DIR_OPEN:
01279         rc = PnutDirOpen(dev, (DIR *) conf);
01280         break;
01281     case FS_DIR_CLOSE:
01282         rc = PnutDirClose((DIR *) conf);
01283         break;
01284     case FS_DIR_READ:
01285         rc = PnutDirRead((DIR *) conf);
01286         break;
01287     case FS_FILE_STATUS:
01288         rc = PnutFileStatus((PNUTFILE *) ((IOCTL_ARG2 *) conf)->arg1,   /* */
01289                             (struct stat *) ((IOCTL_ARG2 *) conf)->arg2);
01290         break;
01291     case FS_FILE_DELETE:
01292         rc = PnutDelete((char *) conf);
01293         break;
01294     case FS_FILE_SEEK:
01295         PnutFileSeek((PNUTFILE *) ((IOCTL_ARG3 *) conf)->arg1,  /* */
01296                      (long *) ((IOCTL_ARG3 *) conf)->arg2,      /* */
01297                      (int) ((IOCTL_ARG3 *) conf)->arg3);
01298         break;
01299     }
01300     return rc;
01301 }
01302 
01314 static long PnutFileSize(NUTFILE *nfp)
01315 {
01316     PNUTFILE *fp = nfp->nf_fcb;
01317 
01318     return BankNodePointer(fp->f_node)->node_size;
01319 }
01320 
01321 /* ------------------------ Initialization ------------------------ */
01322 
01334 static int PnutInit(NUTDEVICE * dev)
01335 {
01336     PNUT_BLKNUM i;
01337     int rc;
01338 
01339     /* Add all blocks to the free list. */
01340     for (i = 0; i < PNUT_TOTAL_BLOCKS; i++) {
01341         PnutBlockRelease(i);
01342     }
01343 
01344     /* Initialize the root directory. */
01345     if ((root = PnutNodeAlloc(NODETYPE_DIR)) == -1) {
01346         rc = ENOSPC;
01347     } else {
01348         if ((rc = PnutDirAddEntry(root, ".", root)) == 0) {
01349             rc = PnutDirAddEntry(root, "..", root);
01350         }
01351         if (rc) {
01352             PnutBlockRelease(root);
01353         }
01354     }
01355     return rc;
01356 }
01357 
01361 NUTDEVICE devPnut = {
01362     0,                          
01363     {'P', 'N', 'U', 'T', 0, 0, 0, 0, 0}
01364     ,                           
01365     IFTYP_RAM,                  
01366     0,                          
01367     0,                          
01368     0,                          
01369     0,                          
01370     PnutInit,                   
01371     PnutIOCtl,                  
01372     PnutFileRead,               
01373     PnutFileWrite,              
01374 #ifdef __HARVARD_ARCH__
01375     PnutFileWrite_P,            
01376 #endif
01377     PnutFileOpen,               
01378     PnutFileClose,              
01379     PnutFileSize                
01380 };
01381 

© 2000-2007 by egnite Software GmbH - visit http://www.ethernut.de/