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 
00079 #include <compiler.h>
00080 
00081 #include <stdlib.h>
00082 #include <string.h>
00083 #include <time.h>
00084 #include <fcntl.h>
00085 #include <dirent.h>
00086 #include <errno.h>
00087 
00088 #include <sys/stat.h>
00089 #include <sys/file.h>
00090 #include <sys/bankmem.h>
00091 
00092 #include <fs/fs.h>
00093 #include <dev/pnut.h>
00094 
00099 
00106 
00111 #ifndef PNUT_BLOCK_SIZE
00112 #define PNUT_BLOCK_SIZE     512
00113 #endif
00114 
00121 #ifndef PNUT_DIRENT_SIZE
00122 #define PNUT_DIRENT_SIZE  32
00123 #endif
00124 
00137 #ifndef PNUT_BLOCKS_PER_NODE
00138 #define PNUT_BLOCKS_PER_NODE      250
00139 #endif
00140 
00141 #ifndef PNUTBANK_COUNT
00142 #ifdef ARTHERNET1
00143 /* Default for Arthernet 1. */
00144 #define PNUTBANK_COUNT 15
00145 #elif MMNET02
00146 /* Default for MMnte02. */
00147 #define PNUTBANK_COUNT 6
00148 #else
00149 /* Default for Ethernet 2. */
00150 #define PNUTBANK_COUNT 30
00151 #endif
00152 #endif
00153 
00157 #ifndef SEEK_SET
00158 #  define SEEK_SET        0     /* Seek from beginning of file.  */
00159 #  define SEEK_CUR        1     /* Seek from current position.  */
00160 #  define SEEK_END        2     /* Set file pointer to EOF plus "offset" */
00161 #endif
00162 
00163 #define NODETYPE_REG        0
00164 #define NODETYPE_DIR        1
00165 
00166 
00167 typedef short PNUT_BLKNUM;
00168 
00187 typedef struct {
00188     u_char node_type;           
00189     u_char node_refs;           
00200     u_short node_links;
00201     u_long node_size;           
00202     time_t node_mtime;          
00203     PNUT_BLKNUM node_blocks[PNUT_BLOCKS_PER_NODE];      
00204 } PNUT_NODE;
00205 
00212 #define PNUT_MAX_NAMELEN    (PNUT_DIRENT_SIZE - sizeof(PNUT_BLKNUM) - sizeof(u_char) - 1)
00213 
00222 typedef struct {
00228     PNUT_BLKNUM dir_node;
00234     u_char dir_inuse;
00240     char dir_name[PNUT_MAX_NAMELEN + 1];
00241 } PNUT_DIRENTRY;
00242 
00249 #define PNUT_MAX_FILESIZE    (PNUT_BLOCKS_PER_NODE * PNUT_BLOCK_SIZE)
00250 
00254 typedef struct {
00258     PNUT_BLKNUM fr_node;
00259 
00263     PNUT_BLKNUM fr_pnode;
00264 
00270     CONST char *fr_name;
00271 } PNUT_FINDRESULT;
00272 
00276 typedef struct _PNUTFILE PNUTFILE;
00277 
00281 struct _PNUTFILE {
00282     PNUT_BLKNUM f_node;         /* Node of the file or directory. */
00283     u_long f_pos;               /* Current file pointer position. */
00284     u_int f_flag;               /* File mode. */
00285 };
00286 
00288 static PNUT_BLKNUM root;
00289 
00290 /* -------------------- Banking hardware routines ------------------- */
00291 
00293 #ifndef NUTBANK_SIZE
00294 #define NUTBANK_SIZE 16384
00295 #endif
00296 
00307 #ifndef PNUT_TOTAL_BLOCKS
00308 #define PNUT_TOTAL_BLOCKS   (PNUTBANK_COUNT * (NUTBANK_SIZE / PNUT_BLOCK_SIZE))
00309 #endif
00310 
00311 #define BLOCKS_PER_BANK  (NUTBANK_SIZE / PNUT_BLOCK_SIZE)
00312 
00313 #ifndef NUTBANK_SR
00314 #define NUTBANK_SR 0xFF00
00315 #endif
00316 
00317 #ifndef NUTBANK_START
00318 #define NUTBANK_START 0x8000
00319 #endif
00320 
00321 #define NUTBANK_PTR ((char *)NUTBANK_START)
00322 
00334 void BankSelect(PNUT_BLKNUM blk)
00335 {
00336 
00337 // This is a hack to avoid compiler warning if no banking is enabled... 
00338 // But I don't like moving code to header files at all.. (Ole Reinhardt)
00339 #if NUTBANK_COUNT 
00340     int bank = blk / BLOCKS_PER_BANK;
00341 #endif
00342     // Bankswitching is now handled in bankmem.h
00343     NutSegBufEnable(bank);
00344 }
00345 
00349 PNUT_NODE *BankNodePointer(PNUT_BLKNUM blk)
00350 {
00351     if (blk < 0) {
00352         return NULL;
00353     }
00354     BankSelect(blk);
00355     return (PNUT_NODE *) & NUTBANK_PTR[(blk % BLOCKS_PER_BANK) * PNUT_BLOCK_SIZE];
00356 }
00357 
00358 
00359 /* ------------------------ Block routines ------------------------ */
00360 
00361 /*
00362  * \brief Index of the first free block.
00363  *
00364  * All free blocks are collected in a linked list. This global variable 
00365  * contains the index of the first free block. Each free block contains 
00366  * nothing but the index of the next free block, which is -1 in the last 
00367  * member of this list.
00368  *
00369  * If this variable is set to -1, then there are no more free blocks 
00370  * available. During file system initialization all available blocks are 
00371  * added to this list.
00372  */
00373 static PNUT_BLKNUM blockFreeList = -1;
00374 
00382 static PNUT_BLKNUM PnutBlockAlloc(void)
00383 {
00384     PNUT_BLKNUM rc = blockFreeList;
00385 
00386     if (rc >= 0) {
00387         PNUT_BLKNUM *bankptr = (PNUT_BLKNUM *) BankNodePointer(blockFreeList);
00388         blockFreeList = *bankptr;
00389         /* Clear the block contents. */
00390         memset(bankptr, 0, PNUT_BLOCK_SIZE);
00391     }
00392     return rc;
00393 }
00394 
00402 static void PnutBlockRelease(PNUT_BLKNUM blk)
00403 {
00404     PNUT_BLKNUM *bankptr = (PNUT_BLKNUM *) BankNodePointer(blk);
00405 
00406     *bankptr = blockFreeList;
00407     blockFreeList = blk;
00408 }
00409 
00410 /* ------------------------ Node routines ------------------------ */
00411 
00421 static PNUT_BLKNUM PnutNodeAlloc(u_char type)
00422 {
00423     PNUT_BLKNUM node = PnutBlockAlloc();
00424 
00425     if (node >= 0) {
00426         int i;
00427         PNUT_NODE *np = BankNodePointer(node);
00428 
00429         /* Clear the data block list of this node. */
00430         for (i = 0; i < PNUT_BLOCKS_PER_NODE; i++) {
00431             np->node_blocks[i] = -1;
00432         }
00433 
00434         /* Set the type and the last modification date. */
00435         np->node_type = type;
00436         np->node_mtime = time(0);
00437     }
00438     return node;
00439 }
00440 
00441 /*
00442  * \brief Free all blocks of a specified node.
00443  */
00444 static void PnutNodeDataRelease(PNUT_BLKNUM node)
00445 {
00446     int i;
00447 
00448     for (i = 0; i < PNUT_BLOCKS_PER_NODE; i++) {
00449         if (BankNodePointer(node)->node_blocks[i] >= 0) {
00450             PnutBlockRelease(BankNodePointer(node)->node_blocks[i]);
00451             BankNodePointer(node)->node_blocks[i] = -1;
00452         }
00453     }
00454     BankNodePointer(node)->node_size = 0;
00455     BankNodePointer(node)->node_mtime = time(0);
00456 }
00457 
00461 static void PnutNodeRelease(PNUT_BLKNUM node)
00462 {
00463     PnutNodeDataRelease(node);
00464     PnutBlockRelease(node);
00465 }
00466 
00483 static int PnutNodeGetDataPtr(PNUT_BLKNUM node, u_long pos, void **buffer, size_t * size, int create)
00484 {
00485     int blkidx;                 /* Number of full blocks */
00486     int rc = EINVAL;
00487 
00488     *buffer = NULL;
00489     *size = 0;
00490 
00491     /* Calculate the block index. Make sure it fits in our maximum file size. */
00492     if ((blkidx = pos / PNUT_BLOCK_SIZE) < PNUT_BLOCKS_PER_NODE) {
00493         PNUT_BLKNUM blk;
00494 
00495         /* Get the data block number. Optionally allocate a new block. */
00496         if ((blk = BankNodePointer(node)->node_blocks[blkidx]) < 0 && create) {
00497             if ((blk = PnutBlockAlloc()) < 0) {
00498                 rc = ENOSPC;
00499             } else {
00500                 BankNodePointer(node)->node_blocks[blkidx] = blk;
00501             }
00502         }
00503 
00504         /* 
00505          * If we got a valid block, then set the pointer at the specified
00506          * position and the remaining bytes within this block.
00507          */
00508         if (blk >= 0) {
00509             char *blkptr = (char *) BankNodePointer(blk);
00510             int blkpos = pos % PNUT_BLOCK_SIZE; /* Position within block */
00511 
00512             *buffer = blkptr + blkpos;
00513             *size = PNUT_BLOCK_SIZE - blkpos;
00514             rc = 0;
00515         }
00516     }
00517     return rc;
00518 }
00519 
00520 
00521 /* ------------------------ Directory routines ------------------------ */
00522 
00526 static int PnutDirIsEmpty(PNUT_BLKNUM node)
00527 {
00528     int rc = 1;
00529     u_long pos;
00530     size_t size;
00531     PNUT_DIRENTRY *entry;
00532 
00533     /* Loop through the data blocks of this directory node. */
00534     for (pos = 0; pos < BankNodePointer(node)->node_size; pos += PNUT_DIRENT_SIZE) {
00535 
00536         /* Get the pointer to the next directory entry. */
00537         if (PnutNodeGetDataPtr(node, pos, (void *) &entry, &size, 0) || size == 0) {
00538             /* No more entries. */
00539             break;
00540         }
00541 
00542         if (size >= PNUT_DIRENT_SIZE) {
00543             if (entry->dir_inuse && strcmp(entry->dir_name, ".") && strcmp(entry->dir_name, "..")) {
00544                 /* Entry found */
00545                 rc = 0;
00546                 break;
00547             }
00548         }
00549     }
00550     return rc;
00551 }
00552 
00566 static int PnutDirFindEntry(PNUT_BLKNUM node, CONST char *path, size_t len, PNUT_DIRENTRY ** entry)
00567 {
00568     int rc = ENOENT;
00569     u_long pos;
00570     size_t size;
00571 
00572     /* Loop through the data blocks of this directory node. */
00573     for (pos = 0; pos < BankNodePointer(node)->node_size; pos += PNUT_DIRENT_SIZE) {
00574 
00575         /* Get the pointer to the next directory entry. */
00576         if (PnutNodeGetDataPtr(node, pos, (void **) entry, &size, 0) || size == 0) {
00577             /* No more entries. */
00578             break;
00579         }
00580 
00581         /* Compare this entry. */
00582         if (size >= PNUT_DIRENT_SIZE) { /* Skip entries across block boundaries. */
00583             if ((*entry)->dir_inuse &&  /* Skip unused entries. */
00584                 strlen((*entry)->dir_name) == len &&    /* Skip non-matching names. */
00585                 strncmp((*entry)->dir_name, path, len) == 0) {
00586                 /* Requested entry found */
00587                 rc = 0;
00588                 break;
00589             }
00590         }
00591     }
00592     return rc;
00593 }
00594 
00595 /*
00596  * \brief Find directory entry of a specified path.
00597  *
00598  * \param node   First node block to search.
00599  * \param path   Path to find.
00600  * \param result Search result.
00601  *
00602  * \return Error code.
00603  */
00604 static int PnutDirFindPath(PNUT_BLKNUM node, CONST char *path, PNUT_FINDRESULT * result)
00605 {
00606     int rc = 0;
00607     size_t len;
00608     PNUT_DIRENTRY *entry;
00609 
00610     result->fr_pnode = node;
00611     result->fr_node = -1;
00612     result->fr_name = NULL;
00613 
00614     while (*path == '/') {
00615         path++;
00616     }
00617 
00618     if (*path == 0) {
00619         path = ".";
00620     }
00621 
00622     /* 
00623      * Loop for each path component.
00624      */
00625     while (*path) {
00626         CONST char *cp;
00627 
00628         /* Make sure that this is a directory node. */
00629         if (BankNodePointer(node)->node_type != NODETYPE_DIR) {
00630             rc = ENOTDIR;
00631             break;
00632         }
00633 
00634         /* Retrieve the length of first path component. */
00635         for (len = 0, cp = path; *cp && *cp != '/'; cp++) {
00636             len++;
00637         }
00638 
00639         /* 
00640          * If this component is last, we found all parents.
00641          * Keep the last one stored in the result.
00642          */
00643         if (*cp == 0) {
00644             result->fr_name = path;
00645         }
00646 
00647         /* Try to find this component. */
00648         if ((rc = PnutDirFindEntry(node, path, len, &entry)) != 0) {
00649             rc = ENOENT;
00650             break;
00651         }
00652 
00653         /* Component found. Return if this was the last one. */
00654         result->fr_node = entry->dir_node;
00655         if (*cp == 0) {
00656             break;
00657         }
00658 
00659         /* Not the last component. Store node as previous node. */
00660         result->fr_pnode = result->fr_node;
00661 
00662         /* Move forward to the next path component. */
00663         node = result->fr_node;
00664         path += len;
00665         while (*path == '/') {
00666             path++;
00667         }
00668     }
00669     return rc;
00670 }
00671 
00672 
00683 static int PnutDirOpen(NUTDEVICE * dev, DIR * dir)
00684 {
00685     PNUTFILE *fp;
00686     PNUT_FINDRESULT found;
00687     int rc;
00688 
00689     /* Locate the entry in our directory structure. */
00690     if ((rc = PnutDirFindPath(root, dir->dd_buf, &found)) != 0) {
00691         errno = rc;
00692         rc = -1;
00693     } else {
00694         if (BankNodePointer(found.fr_node)->node_type != NODETYPE_DIR) {
00695             errno = ENOTDIR;
00696             rc = -1;
00697         }
00698         /* Allocate a PNUT file descriptor. */
00699         else if ((fp = malloc(sizeof(PNUTFILE))) == 0) {
00700             rc = -1;
00701         }
00702         /* 
00703          * Initialize the descriptor and store it in the directory
00704          * information structure.
00705          */
00706         else {
00707             fp->f_node = found.fr_node;
00708             fp->f_pos = 0;
00709 
00710             if ((dir->dd_fd = malloc(sizeof(NUTFILE))) == 0) {
00711                 free(fp);
00712                 rc = -1;
00713             }
00714             else {
00715                 memset(dir->dd_fd, 0, sizeof(NUTFILE));
00716                 dir->dd_fd->nf_dev = dev;
00717                 dir->dd_fd->nf_fcb = fp;
00718                 /* Keep track of the number of open calls. */
00719                 BankNodePointer(fp->f_node)->node_refs++;
00720             }
00721         }
00722     }
00723     return rc;
00724 }
00725 
00729 static int PnutDirClose(DIR * dir)
00730 {
00731     if (dir && dir->dd_fd) {
00732         if (dir->dd_fd->nf_fcb) {
00733             PNUTFILE *fp = dir->dd_fd->nf_fcb;
00734 
00735             BankNodePointer(fp->f_node)->node_refs--;
00736             free(fp);
00737         }
00738         free(dir->dd_fd);
00739         return 0;
00740     }
00741     return EINVAL;
00742 }
00743 
00747 static int PnutDirRead(DIR * dir)
00748 {
00749     int rc = -1;
00750     u_long pos;
00751     PNUT_DIRENTRY *entry;
00752     size_t size;
00753     PNUTFILE *fp = dir->dd_fd->nf_fcb;
00754     struct dirent *ent = (struct dirent *) dir->dd_buf;
00755 
00756     ent->d_name[0] = 0;
00757     for (pos = fp->f_pos; pos < BankNodePointer(fp->f_node)->node_size; pos += PNUT_DIRENT_SIZE) {
00758         /* Retrieve the entry at the current position. */
00759         rc = PnutNodeGetDataPtr(fp->f_node, pos, (void *) &entry, &size, 0);
00760         if (rc || size == 0) {
00761             /* No more entries. */
00762             rc = -1;
00763             break;
00764         }
00765         fp->f_pos = pos + PNUT_DIRENT_SIZE;
00766 
00767         /* Skip entries across block boundaries and unused entires. */
00768         if (size >= PNUT_DIRENT_SIZE && entry->dir_inuse) {
00769             memset(ent, 0, sizeof(struct dirent));
00770             ent->d_fileno = entry->dir_node;
00771             ent->d_namlen = (u_char) strlen(entry->dir_name);
00772             strcpy(ent->d_name, entry->dir_name);
00773             break;
00774         }
00775     }
00776     return rc;
00777 }
00778 
00788 static int PnutDirAddEntry(PNUT_BLKNUM dnode, CONST char *name, PNUT_BLKNUM enode)
00789 {
00790     int rc = 0;
00791     u_long pos = 0;
00792     size_t size;
00793     PNUT_DIRENTRY *entry;
00794     PNUT_NODE *np;
00795 
00796     /*
00797      * Loop through all directory entries until an unused one
00798      * is found. If required, a new data block is allocated,
00799      * so success is guaranteed unless we run out of free
00800      * blocks.
00801      */
00802     for (;;) {
00803         /* 
00804          * Get the data pointer to the specified position. Automatically
00805          * create a new block if we reached the end of the data.
00806          */
00807         if ((rc = PnutNodeGetDataPtr(dnode, pos, (void *) &entry, &size, 1)) != 0) {
00808             break;
00809         }
00810         pos += PNUT_DIRENT_SIZE;
00811 
00812         /* Process entry if it doesn't cross block boundaries. */
00813         if (size >= PNUT_DIRENT_SIZE) {
00814             /* Did we find a previously released or a newly allocated entry? */
00815             if (entry->dir_inuse == 0) {
00816                 /* Update the entry. */
00817                 entry->dir_node = enode;
00818                 entry->dir_inuse = 1;
00819                 strcpy(entry->dir_name, name);
00820 
00821                 /* Update the directory node. */
00822                 np = BankNodePointer(dnode);
00823                 np->node_mtime = time(0);
00824                 if (pos > np->node_size) {
00825                     np->node_size = pos;
00826                 }
00827 
00828                 /* Update the entry's node. */
00829                 np = BankNodePointer(enode);
00830                 np->node_links++;
00831                 break;
00832             }
00833         }
00834     }
00835     return rc;
00836 }
00837 
00854 static int PnutDirDelEntry(PNUT_BLKNUM node, CONST char *name)
00855 {
00856     int rc;
00857     PNUT_DIRENTRY *entry;
00858     PNUT_NODE *rnp;
00859     PNUT_BLKNUM rnode;
00860 
00861     /* Make sure this entry exists. */
00862     if ((rc = PnutDirFindEntry(node, name, strlen(name), &entry)) != 0) {
00863         return rc;
00864     }
00865 
00866     /* The node to remove. */
00867     rnode = entry->dir_node;
00868     rnp = BankNodePointer(rnode);
00869 
00870     /* Make sure that this node has no pending open calls. */
00871     if (rnp->node_refs) {
00872         return EACCES;
00873     }
00874 
00875     /* We only remove empty directories. */
00876     if (rnp->node_type == NODETYPE_DIR) {
00877         if (rnp->node_links > 2 || !PnutDirIsEmpty(rnode)) {
00878             return EACCES;
00879         }
00880         rnp = BankNodePointer(node);
00881         rnp->node_links--;
00882         PnutNodeRelease(rnode);
00883     }
00884 
00885     /* Remove a file. */
00886     else {
00887         PnutNodeRelease(rnode);
00888     }
00889 
00890     /* Mark this entry unused. */
00891     PnutDirFindEntry(node, name, strlen(name), &entry);
00892     entry->dir_inuse = 0;
00893 
00894     return 0;
00895 }
00896 
00904 static int PnutDirCreate(CONST char *path)
00905 {
00906     PNUT_BLKNUM node;
00907     PNUT_FINDRESULT found;
00908     int ec;
00909 
00910     /* Return an error if this entry already exists. */
00911     if ((ec = PnutDirFindPath(root, path, &found)) == 0) {
00912         ec = EEXIST;
00913     }
00914 
00915     /* If this component does not exist, we can create it... */
00916     else if (ec == ENOENT) {
00917         /* ...provided that all parents had been found. */
00918         if (found.fr_name) {
00919             /* Allocate a new node block. */
00920             if ((node = PnutNodeAlloc(NODETYPE_DIR)) < 0) {
00921                 ec = ENOSPC;
00922             }
00923             /* Create a reference to itself. */
00924             else if ((ec = PnutDirAddEntry(node, ".", node)) != 0) {
00925                 PnutNodeRelease(node);
00926             }
00927             /* Create a reference to the parent. */
00928             else if ((ec = PnutDirAddEntry(node, "..", found.fr_pnode)) != 0) {
00929                 PnutNodeRelease(node);
00930             }
00931             /* Create a reference in the parent. */
00932             else if ((ec = PnutDirAddEntry(found.fr_pnode, found.fr_name, node)) != 0) {
00933                 PnutNodeRelease(node);
00934             }
00935         }
00936     }
00937 
00938     /* Something went wrong. */
00939     if (ec) {
00940         errno = ec;
00941         return -1;
00942     }
00943     return 0;
00944 }
00945 
00946 /* ------------------------ File routines ------------------------ */
00947 
00970 static NUTFILE *PnutFileOpen(NUTDEVICE * dev, CONST char *path, int mode, int acc)
00971 {
00972     PNUT_BLKNUM node = -1;
00973     PNUT_FINDRESULT found;
00974     int rc;
00975     PNUTFILE *file;
00976     NUTFILE *nfp = NUTFILE_EOF;
00977 
00978     /* Try to find an exisiting file. */
00979     if ((rc = PnutDirFindPath(root, path, &found)) == 0) {
00980         /* Return an error, if this is no regular file. */
00981         if (BankNodePointer(found.fr_node)->node_type != NODETYPE_REG) {
00982             errno = EISDIR;
00983         }
00984 
00985         /*
00986          * We return an error, if the file exists and _O_EXCL has been
00987          * set with _O_CREAT.
00988          */
00989         else if ((mode & (_O_CREAT | _O_EXCL)) == (_O_CREAT | _O_EXCL)) {
00990             errno = EEXIST;
00991         } else {
00992             node = found.fr_node;
00993             if (mode & _O_TRUNC) {
00994                 PnutNodeDataRelease(node);
00995             }
00996         }
00997     }
00998 
00999     else if (rc == ENOENT) {
01000         /*
01001          * If the search failed on the last path component, then
01002          * PnutDirFindPath() set fr_name. Only in this case we can
01003          * create a new file.
01004          */
01005         if (found.fr_name && (mode & _O_CREAT)) {
01006             node = PnutNodeAlloc(NODETYPE_REG);
01007 
01008             if (node < 0) {
01009                 errno = ENOSPC;
01010                 return NUTFILE_EOF;
01011             }
01012 
01013             rc = PnutDirAddEntry(found.fr_pnode, found.fr_name, node);
01014 
01015             if (rc) {
01016                 PnutBlockRelease(node);
01017             }
01018         }
01019     }
01020 
01021     if (rc == 0 && node >= 0) {
01022         if ((file = malloc(sizeof(PNUTFILE))) != 0) {
01023             file->f_flag |= mode & (_O_RDONLY | _O_WRONLY | _O_APPEND);
01024             file->f_pos = (mode & _O_APPEND) ? BankNodePointer(node)->node_size : 0;
01025             file->f_node = node;
01026 
01027             if ((nfp = malloc(sizeof(NUTFILE))) == 0) {
01028                 free(file);
01029                 nfp = NUTFILE_EOF;
01030             } else {
01031                 nfp->nf_next = 0;
01032                 nfp->nf_dev = dev;
01033                 nfp->nf_fcb = file;
01034                 /* Keep track of the number of open calls. */
01035                 BankNodePointer(node)->node_refs++;
01036             }
01037         }
01038     }
01039     return nfp;
01040 }
01041 
01045 static int PnutFileClose(NUTFILE * nfp)
01046 {
01047     if (nfp != NUTFILE_EOF) {
01048         PNUTFILE *fp = nfp->nf_fcb;
01049 
01050         if (fp) {
01051             BankNodePointer(fp->f_node)->node_refs--;
01052             free(fp);
01053         }
01054         free(nfp);
01055 
01056         return 0;
01057     }
01058     return EINVAL;
01059 }
01060 
01068 static int PnutDelete(char *path)
01069 {
01070     int ec;
01071     PNUT_FINDRESULT found;
01072 
01073     if ((ec = PnutDirFindPath(root, path, &found)) == 0) {
01074         ec = PnutDirDelEntry(found.fr_pnode, found.fr_name);
01075     }
01076     if (ec) {
01077         errno = ec;
01078         return -1;
01079     }
01080     return 0;
01081 }
01082 
01091 static int PnutStatus(CONST char *path, struct stat *status)
01092 {
01093     int rc;
01094     PNUT_FINDRESULT found;
01095 
01096     if ((rc = PnutDirFindPath(root, path, &found)) == 0) {
01097         status->st_mode = BankNodePointer(found.fr_node)->node_type;
01098         status->st_ino = found.fr_node;
01099         status->st_nlink = BankNodePointer(found.fr_node)->node_links;
01100         status->st_size = BankNodePointer(found.fr_node)->node_size;
01101         status->st_mtime = BankNodePointer(found.fr_node)->node_mtime;
01102     }
01103     return rc;
01104 }
01105 
01115 static int PnutFileStatus(PNUTFILE * fp, struct stat *status)
01116 {
01117     status->st_mode = BankNodePointer(fp->f_node)->node_type;
01118     status->st_ino = fp->f_node;
01119     status->st_nlink = BankNodePointer(fp->f_node)->node_links;
01120     status->st_size = BankNodePointer(fp->f_node)->node_size;
01121     status->st_mtime = BankNodePointer(fp->f_node)->node_mtime;
01122 
01123     return 0;
01124 }
01125 
01138 static int PnutFileWrite(NUTFILE * nfp, CONST void *buffer, int len)
01139 {
01140     PNUTFILE *fp = nfp->nf_fcb;
01141     int ec = 0;
01142     int rc = 0;
01143     PNUT_BLKNUM node = fp->f_node;
01144     u_char *blkptr;
01145     size_t blksiz;
01146     CONST char *buf = buffer;
01147 
01148     while (len) {
01149         if ((ec = PnutNodeGetDataPtr(node, fp->f_pos, (void *) &blkptr, &blksiz, 1)) != 0) {
01150             break;
01151         }
01152         if (blksiz >= len) {
01153             blksiz = len;
01154             len = 0;
01155         } else {
01156             len -= blksiz;
01157         }
01158         memcpy(blkptr, buf, blksiz);
01159         rc += blksiz;
01160         buf += blksiz;
01161         fp->f_pos += blksiz;
01162     }
01163     if (ec == 0 || ec == ENOSPC) {
01164         if (BankNodePointer(node)->node_size < fp->f_pos) {
01165             BankNodePointer(node)->node_size = fp->f_pos;
01166         }
01167         BankNodePointer(node)->node_mtime = time(0);
01168     }
01169     return rc;
01170 }
01171 
01172 #ifdef __HARVARD_ARCH__
01173 static int PnutFileWrite_P(NUTFILE * nfp, PGM_P buffer, int len)
01174 {
01175     return -1;
01176 }
01177 #endif
01178 
01190 static int PnutFileRead(NUTFILE * nfp, void *buffer, int len)
01191 {
01192     PNUTFILE *fp = nfp->nf_fcb;
01193     int ec = 0;
01194     int rc = 0;
01195     PNUT_BLKNUM node = fp->f_node;
01196     u_char *blkptr;
01197     size_t blksiz;
01198     char *buf = buffer;
01199 
01200     /* Respect end of file. */
01201     if (len > BankNodePointer(node)->node_size - fp->f_pos) {
01202         len = (size_t) (BankNodePointer(node)->node_size - fp->f_pos);
01203     }
01204     while (len) {
01205         if ((ec = PnutNodeGetDataPtr(node, fp->f_pos, (void *) &blkptr, &blksiz, 0)) != 0) {
01206             break;
01207         }
01208         if (blksiz >= len) {
01209             blksiz = len;
01210             len = 0;
01211         } else {
01212             len -= blksiz;
01213         }
01214         memcpy(buf, blkptr, blksiz);
01215         rc += blksiz;
01216         buf += blksiz;
01217         fp->f_pos += blksiz;
01218     }
01219     return rc;
01220 }
01221 
01222 static int PnutFileSeek(PNUTFILE * fp, long *pos, int whence)
01223 {
01224     int rc = 0;
01225     long npos = *pos;
01226 
01227     switch (whence) {
01228     case SEEK_CUR:
01229         npos += fp->f_pos;
01230         break;
01231     case SEEK_END:
01232         npos += BankNodePointer(fp->f_node)->node_size;
01233         break;
01234     }
01235 
01236     if (npos < 0 || npos > (long) BankNodePointer(fp->f_node)->node_size) {
01237         rc = EINVAL;
01238     } else {
01239         fp->f_pos = npos;
01240         *pos = npos;
01241     }
01242     return rc;
01243 }
01244 
01245 /* ------------------------ Special function interface ------------------------ */
01246 
01250 int PnutIOCtl(NUTDEVICE * dev, int req, void *conf)
01251 {
01252     int rc = -1;
01253 
01254     switch (req) {
01255     case FS_STATUS:
01256         {
01257             FSCP_STATUS *par = (FSCP_STATUS *) conf;
01258 
01259             rc = PnutStatus(par->par_path, par->par_stp);
01260         }
01261         break;
01262     case FS_DIR_CREATE:
01263         rc = PnutDirCreate((char *) conf);
01264         break;
01265     case FS_DIR_REMOVE:
01266         rc = PnutDelete((char *) conf);
01267         break;
01268     case FS_DIR_OPEN:
01269         rc = PnutDirOpen(dev, (DIR *) conf);
01270         break;
01271     case FS_DIR_CLOSE:
01272         rc = PnutDirClose((DIR *) conf);
01273         break;
01274     case FS_DIR_READ:
01275         rc = PnutDirRead((DIR *) conf);
01276         break;
01277     case FS_FILE_STATUS:
01278         rc = PnutFileStatus((PNUTFILE *) ((IOCTL_ARG2 *) conf)->arg1,   /* */
01279                             (struct stat *) ((IOCTL_ARG2 *) conf)->arg2);
01280         break;
01281     case FS_FILE_DELETE:
01282         rc = PnutDelete((char *) conf);
01283         break;
01284     case FS_FILE_SEEK:
01285         PnutFileSeek((PNUTFILE *) ((IOCTL_ARG3 *) conf)->arg1,  /* */
01286                      (long *) ((IOCTL_ARG3 *) conf)->arg2,      /* */
01287                      (int) ((IOCTL_ARG3 *) conf)->arg3);
01288         break;
01289     }
01290     return rc;
01291 }
01292 
01304 static long PnutFileSize(NUTFILE *nfp)
01305 {
01306     PNUTFILE *fp = nfp->nf_fcb;
01307 
01308     return BankNodePointer(fp->f_node)->node_size;
01309 }
01310 
01311 /* ------------------------ Initialization ------------------------ */
01312 
01324 static int PnutInit(NUTDEVICE * dev)
01325 {
01326     PNUT_BLKNUM i;
01327     int rc;
01328 
01329     /* Add all blocks to the free list. */
01330     for (i = 0; i < PNUT_TOTAL_BLOCKS; i++) {
01331         PnutBlockRelease(i);
01332     }
01333 
01334     /* Initialize the root directory. */
01335     if ((root = PnutNodeAlloc(NODETYPE_DIR)) == -1) {
01336         rc = ENOSPC;
01337     } else {
01338         if ((rc = PnutDirAddEntry(root, ".", root)) == 0) {
01339             rc = PnutDirAddEntry(root, "..", root);
01340         }
01341         if (rc) {
01342             PnutBlockRelease(root);
01343         }
01344     }
01345     return rc;
01346 }
01347 
01351 NUTDEVICE devPnut = {
01352     0,                          
01353     {'P', 'N', 'U', 'T', 0, 0, 0, 0, 0}
01354     ,                           
01355     IFTYP_RAM,                  
01356     0,                          
01357     0,                          
01358     0,                          
01359     0,                          
01360     PnutInit,                   
01361     PnutIOCtl,                  
01362     PnutFileRead,               
01363     PnutFileWrite,              
01364 #ifdef __HARVARD_ARCH__
01365     PnutFileWrite_P,            
01366 #endif
01367     PnutFileOpen,               
01368     PnutFileClose,              
01369     PnutFileSize                
01370 };
01371 

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