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

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