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 
00095 #include <compiler.h>
00096 
00097 #include <stdlib.h>
00098 #include <string.h>
00099 #include <time.h>
00100 #include <fcntl.h>
00101 #include <dirent.h>
00102 #include <errno.h>
00103 #include <memdebug.h>
00104 
00105 #include <sys/stat.h>
00106 #include <sys/file.h>
00107 #include <sys/bankmem.h>
00108 
00109 #include <fs/fs.h>
00110 #include <dev/pnut.h>
00111 
00116 
00123 
00128 #ifndef PNUT_BLOCK_SIZE
00129 #define PNUT_BLOCK_SIZE     512
00130 #endif
00131 
00138 #ifndef PNUT_DIRENT_SIZE
00139 #define PNUT_DIRENT_SIZE  32
00140 #endif
00141 
00154 #ifndef PNUT_BLOCKS_PER_NODE
00155 #define PNUT_BLOCKS_PER_NODE      250
00156 #endif
00157 
00158 #ifndef PNUTBANK_COUNT
00159 #ifdef ARTHERNET1
00160 /* Default for Arthernet 1. */
00161 #define PNUTBANK_COUNT 15
00162 #elif MMNET02  || MMNET03  || MMNET04 ||\
00163       MMNET102 || MMNET103 || MMNET104  
00164 /* Default for MMnte02. */
00165 #define PNUTBANK_COUNT 6
00166 #else
00167 /* Default for Ethernet 2. */
00168 #define PNUTBANK_COUNT 30
00169 #endif
00170 #endif
00171 
00175 #ifndef SEEK_SET
00176 #  define SEEK_SET        0     /* Seek from beginning of file.  */
00177 #  define SEEK_CUR        1     /* Seek from current position.  */
00178 #  define SEEK_END        2     /* Set file pointer to EOF plus "offset" */
00179 #endif
00180 
00181 #define NODETYPE_REG        0
00182 #define NODETYPE_DIR        1
00183 
00184 
00185 typedef short PNUT_BLKNUM;
00186 
00205 typedef struct {
00206     uint8_t node_type;           
00207     uint8_t node_refs;           
00218     uint16_t node_links;
00219     uint32_t node_size;           
00220     time_t node_mtime;          
00221     PNUT_BLKNUM node_blocks[PNUT_BLOCKS_PER_NODE];      
00222 } PNUT_NODE;
00223 
00230 #define PNUT_MAX_NAMELEN    (PNUT_DIRENT_SIZE - sizeof(PNUT_BLKNUM) - sizeof(uint8_t) - 1)
00231 
00240 typedef struct {
00246     PNUT_BLKNUM dir_node;
00252     uint8_t dir_inuse;
00258     char dir_name[PNUT_MAX_NAMELEN + 1];
00259 } PNUT_DIRENTRY;
00260 
00267 #define PNUT_MAX_FILESIZE    (PNUT_BLOCKS_PER_NODE * PNUT_BLOCK_SIZE)
00268 
00272 typedef struct {
00276     PNUT_BLKNUM fr_node;
00277 
00281     PNUT_BLKNUM fr_pnode;
00282 
00288     CONST char *fr_name;
00289 } PNUT_FINDRESULT;
00290 
00294 typedef struct _PNUTFILE PNUTFILE;
00295 
00299 struct _PNUTFILE {
00300     PNUT_BLKNUM f_node;         /* Node of the file or directory. */
00301     uint32_t f_pos;               /* Current file pointer position. */
00302     unsigned int f_flag;               /* File mode. */
00303 };
00304 
00306 static PNUT_BLKNUM root;
00307 
00308 /* -------------------- Banking hardware routines ------------------- */
00309 
00311 #ifndef NUTBANK_SIZE
00312 #define NUTBANK_SIZE 16384
00313 #endif
00314 
00325 #ifndef PNUT_TOTAL_BLOCKS
00326 #define PNUT_TOTAL_BLOCKS   (PNUTBANK_COUNT * (NUTBANK_SIZE / PNUT_BLOCK_SIZE))
00327 #endif
00328 
00329 #define BLOCKS_PER_BANK  (NUTBANK_SIZE / PNUT_BLOCK_SIZE)
00330 
00331 #ifndef NUTBANK_SR
00332 #define NUTBANK_SR 0xFF00
00333 #endif
00334 
00335 #ifndef NUTBANK_START
00336 #define NUTBANK_START 0x8000
00337 #endif
00338 
00339 #define NUTBANK_PTR ((char *)NUTBANK_START)
00340 
00352 void BankSelect(PNUT_BLKNUM blk)
00353 {
00354 
00355 // This is a hack to avoid compiler warning if no banking is enabled... 
00356 // But I don't like moving code to header files at all.. (Ole Reinhardt)
00357 #if NUTBANK_COUNT 
00358     int bank = blk / BLOCKS_PER_BANK;
00359 #endif
00360     // Bankswitching is now handled in bankmem.h
00361     NutSegBufEnable(bank);
00362 }
00363 
00367 PNUT_NODE *BankNodePointer(PNUT_BLKNUM blk)
00368 {
00369     if (blk < 0) {
00370         return NULL;
00371     }
00372     BankSelect(blk);
00373     return (PNUT_NODE *) & NUTBANK_PTR[(blk % BLOCKS_PER_BANK) * PNUT_BLOCK_SIZE];
00374 }
00375 
00376 
00377 /* ------------------------ Block routines ------------------------ */
00378 
00379 /*
00380  * \brief Index of the first free block.
00381  *
00382  * All free blocks are collected in a linked list. This global variable 
00383  * contains the index of the first free block. Each free block contains 
00384  * nothing but the index of the next free block, which is -1 in the last 
00385  * member of this list.
00386  *
00387  * If this variable is set to -1, then there are no more free blocks 
00388  * available. During file system initialization all available blocks are 
00389  * added to this list.
00390  */
00391 static PNUT_BLKNUM blockFreeList = -1;
00392 
00400 static PNUT_BLKNUM PnutBlockAlloc(void)
00401 {
00402     PNUT_BLKNUM rc = blockFreeList;
00403 
00404     if (rc >= 0) {
00405         PNUT_BLKNUM *bankptr = (PNUT_BLKNUM *) BankNodePointer(blockFreeList);
00406         blockFreeList = *bankptr;
00407         /* Clear the block contents. */
00408         memset(bankptr, 0, PNUT_BLOCK_SIZE);
00409     }
00410     return rc;
00411 }
00412 
00420 static void PnutBlockRelease(PNUT_BLKNUM blk)
00421 {
00422     PNUT_BLKNUM *bankptr = (PNUT_BLKNUM *) BankNodePointer(blk);
00423 
00424     *bankptr = blockFreeList;
00425     blockFreeList = blk;
00426 }
00427 
00428 /* ------------------------ Node routines ------------------------ */
00429 
00439 static PNUT_BLKNUM PnutNodeAlloc(uint8_t type)
00440 {
00441     PNUT_BLKNUM node = PnutBlockAlloc();
00442 
00443     if (node >= 0) {
00444         int i;
00445         PNUT_NODE *np = BankNodePointer(node);
00446 
00447         /* Clear the data block list of this node. */
00448         for (i = 0; i < PNUT_BLOCKS_PER_NODE; i++) {
00449             np->node_blocks[i] = -1;
00450         }
00451 
00452         /* Set the type and the last modification date. */
00453         np->node_type = type;
00454         np->node_mtime = time(0);
00455     }
00456     return node;
00457 }
00458 
00459 /*
00460  * \brief Free all blocks of a specified node.
00461  */
00462 static void PnutNodeDataRelease(PNUT_BLKNUM node)
00463 {
00464     int i;
00465 
00466     for (i = 0; i < PNUT_BLOCKS_PER_NODE; i++) {
00467         if (BankNodePointer(node)->node_blocks[i] >= 0) {
00468             PnutBlockRelease(BankNodePointer(node)->node_blocks[i]);
00469             BankNodePointer(node)->node_blocks[i] = -1;
00470         }
00471     }
00472     BankNodePointer(node)->node_size = 0;
00473     BankNodePointer(node)->node_mtime = time(0);
00474 }
00475 
00479 static void PnutNodeRelease(PNUT_BLKNUM node)
00480 {
00481     PnutNodeDataRelease(node);
00482     PnutBlockRelease(node);
00483 }
00484 
00501 static int PnutNodeGetDataPtr(PNUT_BLKNUM node, uint32_t pos, void **buffer, size_t * size, int create)
00502 {
00503     int blkidx;                 /* Number of full blocks */
00504     int rc = EINVAL;
00505 
00506     *buffer = NULL;
00507     *size = 0;
00508 
00509     /* Calculate the block index. Make sure it fits in our maximum file size. */
00510     if ((blkidx = pos / PNUT_BLOCK_SIZE) < PNUT_BLOCKS_PER_NODE) {
00511         PNUT_BLKNUM blk;
00512 
00513         /* Get the data block number. Optionally allocate a new block. */
00514         if ((blk = BankNodePointer(node)->node_blocks[blkidx]) < 0 && create) {
00515             if ((blk = PnutBlockAlloc()) < 0) {
00516                 rc = ENOSPC;
00517             } else {
00518                 BankNodePointer(node)->node_blocks[blkidx] = blk;
00519             }
00520         }
00521 
00522         /* 
00523          * If we got a valid block, then set the pointer at the specified
00524          * position and the remaining bytes within this block.
00525          */
00526         if (blk >= 0) {
00527             char *blkptr = (char *) BankNodePointer(blk);
00528             int blkpos = pos % PNUT_BLOCK_SIZE; /* Position within block */
00529 
00530             *buffer = blkptr + blkpos;
00531             *size = PNUT_BLOCK_SIZE - blkpos;
00532             rc = 0;
00533         }
00534     }
00535     return rc;
00536 }
00537 
00538 
00539 /* ------------------------ Directory routines ------------------------ */
00540 
00544 static int PnutDirIsEmpty(PNUT_BLKNUM node)
00545 {
00546     int rc = 1;
00547     uint32_t pos;
00548     size_t size;
00549     PNUT_DIRENTRY *entry;
00550 
00551     /* Loop through the data blocks of this directory node. */
00552     for (pos = 0; pos < BankNodePointer(node)->node_size; pos += PNUT_DIRENT_SIZE) {
00553 
00554         /* Get the pointer to the next directory entry. */
00555         if (PnutNodeGetDataPtr(node, pos, (void *) &entry, &size, 0) || size == 0) {
00556             /* No more entries. */
00557             break;
00558         }
00559 
00560         if (size >= PNUT_DIRENT_SIZE) {
00561             if (entry->dir_inuse && strcmp(entry->dir_name, ".") && strcmp(entry->dir_name, "..")) {
00562                 /* Entry found */
00563                 rc = 0;
00564                 break;
00565             }
00566         }
00567     }
00568     return rc;
00569 }
00570 
00584 static int PnutDirFindEntry(PNUT_BLKNUM node, CONST char *path, size_t len, PNUT_DIRENTRY ** entry)
00585 {
00586     int rc = ENOENT;
00587     uint32_t pos;
00588     size_t size;
00589 
00590     /* Loop through the data blocks of this directory node. */
00591     for (pos = 0; pos < BankNodePointer(node)->node_size; pos += PNUT_DIRENT_SIZE) {
00592 
00593         /* Get the pointer to the next directory entry. */
00594         if (PnutNodeGetDataPtr(node, pos, (void **) entry, &size, 0) || size == 0) {
00595             /* No more entries. */
00596             break;
00597         }
00598 
00599         /* Compare this entry. */
00600         if (size >= PNUT_DIRENT_SIZE) { /* Skip entries across block boundaries. */
00601             if ((*entry)->dir_inuse &&  /* Skip unused entries. */
00602                 strlen((*entry)->dir_name) == len &&    /* Skip non-matching names. */
00603                 strncmp((*entry)->dir_name, path, len) == 0) {
00604                 /* Requested entry found */
00605                 rc = 0;
00606                 break;
00607             }
00608         }
00609     }
00610     return rc;
00611 }
00612 
00613 /*
00614  * \brief Find directory entry of a specified path.
00615  *
00616  * \param node   First node block to search.
00617  * \param path   Path to find.
00618  * \param result Search result.
00619  *
00620  * \return Error code.
00621  */
00622 static int PnutDirFindPath(PNUT_BLKNUM node, CONST char *path, PNUT_FINDRESULT * result)
00623 {
00624     int rc = 0;
00625     size_t len;
00626     PNUT_DIRENTRY *entry;
00627 
00628     result->fr_pnode = node;
00629     result->fr_node = -1;
00630     result->fr_name = NULL;
00631 
00632     while (*path == '/') {
00633         path++;
00634     }
00635 
00636     if (*path == 0) {
00637         path = ".";
00638     }
00639 
00640     /* 
00641      * Loop for each path component.
00642      */
00643     while (*path) {
00644         CONST char *cp;
00645 
00646         /* Make sure that this is a directory node. */
00647         if (BankNodePointer(node)->node_type != NODETYPE_DIR) {
00648             rc = ENOTDIR;
00649             break;
00650         }
00651 
00652         /* Retrieve the length of first path component. */
00653         for (len = 0, cp = path; *cp && *cp != '/'; cp++) {
00654             len++;
00655         }
00656 
00657         /* 
00658          * If this component is last, we found all parents.
00659          * Keep the last one stored in the result.
00660          */
00661         if (*cp == 0) {
00662             result->fr_name = path;
00663         }
00664 
00665         /* Try to find this component. */
00666         if ((rc = PnutDirFindEntry(node, path, len, &entry)) != 0) {
00667             rc = ENOENT;
00668             break;
00669         }
00670 
00671         /* Component found. Return if this was the last one. */
00672         result->fr_node = entry->dir_node;
00673         if (*cp == 0) {
00674             break;
00675         }
00676 
00677         /* Not the last component. Store node as previous node. */
00678         result->fr_pnode = result->fr_node;
00679 
00680         /* Move forward to the next path component. */
00681         node = result->fr_node;
00682         path += len;
00683         while (*path == '/') {
00684             path++;
00685         }
00686     }
00687     return rc;
00688 }
00689 
00690 
00701 static int PnutDirOpen(NUTDEVICE * dev, DIR * dir)
00702 {
00703     PNUTFILE *fp;
00704     PNUT_FINDRESULT found;
00705     int rc;
00706 
00707     /* Locate the entry in our directory structure. */
00708     if ((rc = PnutDirFindPath(root, dir->dd_buf, &found)) != 0) {
00709         errno = rc;
00710         rc = -1;
00711     } else {
00712         if (BankNodePointer(found.fr_node)->node_type != NODETYPE_DIR) {
00713             errno = ENOTDIR;
00714             rc = -1;
00715         }
00716         /* Allocate a PNUT file descriptor. */
00717         else if ((fp = malloc(sizeof(PNUTFILE))) == 0) {
00718             rc = -1;
00719         }
00720         /* 
00721          * Initialize the descriptor and store it in the directory
00722          * information structure.
00723          */
00724         else {
00725             fp->f_node = found.fr_node;
00726             fp->f_pos = 0;
00727 
00728             if ((dir->dd_fd = malloc(sizeof(NUTFILE))) == 0) {
00729                 free(fp);
00730                 rc = -1;
00731             }
00732             else {
00733                 memset(dir->dd_fd, 0, sizeof(NUTFILE));
00734                 dir->dd_fd->nf_dev = dev;
00735                 dir->dd_fd->nf_fcb = fp;
00736                 /* Keep track of the number of open calls. */
00737                 BankNodePointer(fp->f_node)->node_refs++;
00738             }
00739         }
00740     }
00741     return rc;
00742 }
00743 
00747 static int PnutDirClose(DIR * dir)
00748 {
00749     if (dir && dir->dd_fd) {
00750         if (dir->dd_fd->nf_fcb) {
00751             PNUTFILE *fp = dir->dd_fd->nf_fcb;
00752 
00753             BankNodePointer(fp->f_node)->node_refs--;
00754             free(fp);
00755         }
00756         free(dir->dd_fd);
00757         return 0;
00758     }
00759     return EINVAL;
00760 }
00761 
00765 static int PnutDirRead(DIR * dir)
00766 {
00767     int rc = -1;
00768     uint32_t pos;
00769     PNUT_DIRENTRY *entry;
00770     size_t size;
00771     PNUTFILE *fp = dir->dd_fd->nf_fcb;
00772     struct dirent *ent = (struct dirent *) dir->dd_buf;
00773 
00774     ent->d_name[0] = 0;
00775     for (pos = fp->f_pos; pos < BankNodePointer(fp->f_node)->node_size; pos += PNUT_DIRENT_SIZE) {
00776         /* Retrieve the entry at the current position. */
00777         rc = PnutNodeGetDataPtr(fp->f_node, pos, (void *) &entry, &size, 0);
00778         if (rc || size == 0) {
00779             /* No more entries. */
00780             rc = -1;
00781             break;
00782         }
00783         fp->f_pos = pos + PNUT_DIRENT_SIZE;
00784 
00785         /* Skip entries across block boundaries and unused entires. */
00786         if (size >= PNUT_DIRENT_SIZE && entry->dir_inuse) {
00787             memset(ent, 0, sizeof(struct dirent));
00788             ent->d_fileno = entry->dir_node;
00789             ent->d_namlen = (uint8_t) strlen(entry->dir_name);
00790             strcpy(ent->d_name, entry->dir_name);
00791             break;
00792         }
00793     }
00794     return rc;
00795 }
00796 
00806 static int PnutDirAddEntry(PNUT_BLKNUM dnode, CONST char *name, PNUT_BLKNUM enode)
00807 {
00808     int rc = 0;
00809     uint32_t pos = 0;
00810     size_t size;
00811     PNUT_DIRENTRY *entry;
00812     PNUT_NODE *np;
00813 
00814     /*
00815      * Loop through all directory entries until an unused one
00816      * is found. If required, a new data block is allocated,
00817      * so success is guaranteed unless we run out of free
00818      * blocks.
00819      */
00820     for (;;) {
00821         /* 
00822          * Get the data pointer to the specified position. Automatically
00823          * create a new block if we reached the end of the data.
00824          */
00825         if ((rc = PnutNodeGetDataPtr(dnode, pos, (void *) &entry, &size, 1)) != 0) {
00826             break;
00827         }
00828         pos += PNUT_DIRENT_SIZE;
00829 
00830         /* Process entry if it doesn't cross block boundaries. */
00831         if (size >= PNUT_DIRENT_SIZE) {
00832             /* Did we find a previously released or a newly allocated entry? */
00833             if (entry->dir_inuse == 0) {
00834                 /* Update the entry. */
00835                 entry->dir_node = enode;
00836                 entry->dir_inuse = 1;
00837                 strcpy(entry->dir_name, name);
00838 
00839                 /* Update the directory node. */
00840                 np = BankNodePointer(dnode);
00841                 np->node_mtime = time(0);
00842                 if (pos > np->node_size) {
00843                     np->node_size = pos;
00844                 }
00845 
00846                 /* Update the entry's node. */
00847                 np = BankNodePointer(enode);
00848                 np->node_links++;
00849                 break;
00850             }
00851         }
00852     }
00853     return rc;
00854 }
00855 
00872 static int PnutDirDelEntry(PNUT_BLKNUM node, CONST char *name)
00873 {
00874     int rc;
00875     PNUT_DIRENTRY *entry;
00876     PNUT_NODE *rnp;
00877     PNUT_BLKNUM rnode;
00878 
00879     /* Make sure this entry exists. */
00880     if ((rc = PnutDirFindEntry(node, name, strlen(name), &entry)) != 0) {
00881         return rc;
00882     }
00883 
00884     /* The node to remove. */
00885     rnode = entry->dir_node;
00886     rnp = BankNodePointer(rnode);
00887 
00888     /* Make sure that this node has no pending open calls. */
00889     if (rnp->node_refs) {
00890         return EACCES;
00891     }
00892 
00893     /* We only remove empty directories. */
00894     if (rnp->node_type == NODETYPE_DIR) {
00895         if (rnp->node_links > 2 || !PnutDirIsEmpty(rnode)) {
00896             return EACCES;
00897         }
00898         rnp = BankNodePointer(node);
00899         rnp->node_links--;
00900         PnutNodeRelease(rnode);
00901     }
00902 
00903     /* Remove a file. */
00904     else {
00905         PnutNodeRelease(rnode);
00906     }
00907 
00908     /* Mark this entry unused. */
00909     PnutDirFindEntry(node, name, strlen(name), &entry);
00910     entry->dir_inuse = 0;
00911 
00912     return 0;
00913 }
00914 
00922 static int PnutDirCreate(CONST char *path)
00923 {
00924     PNUT_BLKNUM node;
00925     PNUT_FINDRESULT found;
00926     int ec;
00927 
00928     /* Return an error if this entry already exists. */
00929     if ((ec = PnutDirFindPath(root, path, &found)) == 0) {
00930         ec = EEXIST;
00931     }
00932 
00933     /* If this component does not exist, we can create it... */
00934     else if (ec == ENOENT) {
00935         /* ...provided that all parents had been found. */
00936         if (found.fr_name) {
00937             /* Allocate a new node block. */
00938             if ((node = PnutNodeAlloc(NODETYPE_DIR)) < 0) {
00939                 ec = ENOSPC;
00940             }
00941             /* Create a reference to itself. */
00942             else if ((ec = PnutDirAddEntry(node, ".", node)) != 0) {
00943                 PnutNodeRelease(node);
00944             }
00945             /* Create a reference to the parent. */
00946             else if ((ec = PnutDirAddEntry(node, "..", found.fr_pnode)) != 0) {
00947                 PnutNodeRelease(node);
00948             }
00949             /* Create a reference in the parent. */
00950             else if ((ec = PnutDirAddEntry(found.fr_pnode, found.fr_name, node)) != 0) {
00951                 PnutNodeRelease(node);
00952             }
00953         }
00954     }
00955 
00956     /* Something went wrong. */
00957     if (ec) {
00958         errno = ec;
00959         return -1;
00960     }
00961     return 0;
00962 }
00963 
00964 /* ------------------------ File routines ------------------------ */
00965 
00988 static NUTFILE *PnutFileOpen(NUTDEVICE * dev, CONST char *path, int mode, int acc)
00989 {
00990     PNUT_BLKNUM node = -1;
00991     PNUT_FINDRESULT found;
00992     int rc;
00993     PNUTFILE *file;
00994     NUTFILE *nfp = NUTFILE_EOF;
00995 
00996     /* Try to find an exisiting file. */
00997     if ((rc = PnutDirFindPath(root, path, &found)) == 0) {
00998         /* Return an error, if this is no regular file. */
00999         if (BankNodePointer(found.fr_node)->node_type != NODETYPE_REG) {
01000             errno = EISDIR;
01001         }
01002 
01003         /*
01004          * We return an error, if the file exists and _O_EXCL has been
01005          * set with _O_CREAT.
01006          */
01007         else if ((mode & (_O_CREAT | _O_EXCL)) == (_O_CREAT | _O_EXCL)) {
01008             errno = EEXIST;
01009         } else {
01010             node = found.fr_node;
01011             if (mode & _O_TRUNC) {
01012                 PnutNodeDataRelease(node);
01013             }
01014         }
01015     }
01016 
01017     else if (rc == ENOENT) {
01018         /*
01019          * If the search failed on the last path component, then
01020          * PnutDirFindPath() set fr_name. Only in this case we can
01021          * create a new file.
01022          */
01023         if (found.fr_name && (mode & _O_CREAT)) {
01024             node = PnutNodeAlloc(NODETYPE_REG);
01025 
01026             if (node < 0) {
01027                 errno = ENOSPC;
01028                 return NUTFILE_EOF;
01029             }
01030 
01031             rc = PnutDirAddEntry(found.fr_pnode, found.fr_name, node);
01032 
01033             if (rc) {
01034                 PnutBlockRelease(node);
01035             }
01036         }
01037     }
01038 
01039     if (rc == 0 && node >= 0) {
01040         if ((file = malloc(sizeof(PNUTFILE))) != 0) {
01041             file->f_flag |= mode & (_O_RDONLY | _O_WRONLY | _O_APPEND);
01042             file->f_pos = (mode & _O_APPEND) ? BankNodePointer(node)->node_size : 0;
01043             file->f_node = node;
01044 
01045             if ((nfp = malloc(sizeof(NUTFILE))) == 0) {
01046                 free(file);
01047                 nfp = NUTFILE_EOF;
01048             } else {
01049                 nfp->nf_next = 0;
01050                 nfp->nf_dev = dev;
01051                 nfp->nf_fcb = file;
01052                 /* Keep track of the number of open calls. */
01053                 BankNodePointer(node)->node_refs++;
01054             }
01055         }
01056     }
01057     return nfp;
01058 }
01059 
01063 static int PnutFileClose(NUTFILE * nfp)
01064 {
01065     if (nfp != NUTFILE_EOF) {
01066         PNUTFILE *fp = nfp->nf_fcb;
01067 
01068         if (fp) {
01069             BankNodePointer(fp->f_node)->node_refs--;
01070             free(fp);
01071         }
01072         free(nfp);
01073 
01074         return 0;
01075     }
01076     return EINVAL;
01077 }
01078 
01086 static int PnutDelete(char *path)
01087 {
01088     int ec;
01089     PNUT_FINDRESULT found;
01090 
01091     if ((ec = PnutDirFindPath(root, path, &found)) == 0) {
01092         ec = PnutDirDelEntry(found.fr_pnode, found.fr_name);
01093     }
01094     if (ec) {
01095         errno = ec;
01096         return -1;
01097     }
01098     return 0;
01099 }
01100 
01109 static int PnutStatus(CONST char *path, struct stat *status)
01110 {
01111     int rc;
01112     PNUT_FINDRESULT found;
01113 
01114     if ((rc = PnutDirFindPath(root, path, &found)) == 0) {
01115         status->st_mode = BankNodePointer(found.fr_node)->node_type;
01116         status->st_ino = found.fr_node;
01117         status->st_nlink = BankNodePointer(found.fr_node)->node_links;
01118         status->st_size = BankNodePointer(found.fr_node)->node_size;
01119         status->st_mtime = BankNodePointer(found.fr_node)->node_mtime;
01120     }
01121     return rc;
01122 }
01123 
01133 static int PnutFileStatus(PNUTFILE * fp, struct stat *status)
01134 {
01135     status->st_mode = BankNodePointer(fp->f_node)->node_type;
01136     status->st_ino = fp->f_node;
01137     status->st_nlink = BankNodePointer(fp->f_node)->node_links;
01138     status->st_size = BankNodePointer(fp->f_node)->node_size;
01139     status->st_mtime = BankNodePointer(fp->f_node)->node_mtime;
01140 
01141     return 0;
01142 }
01143 
01156 static int PnutFileWrite(NUTFILE * nfp, CONST void *buffer, int len)
01157 {
01158     PNUTFILE *fp = nfp->nf_fcb;
01159     int ec = 0;
01160     int rc = 0;
01161     PNUT_BLKNUM node = fp->f_node;
01162     uint8_t *blkptr;
01163     size_t blksiz;
01164     CONST char *buf = buffer;
01165 
01166     while (len) {
01167         if ((ec = PnutNodeGetDataPtr(node, fp->f_pos, (void *) &blkptr, &blksiz, 1)) != 0) {
01168             break;
01169         }
01170         if (blksiz >= len) {
01171             blksiz = len;
01172             len = 0;
01173         } else {
01174             len -= blksiz;
01175         }
01176         memcpy(blkptr, buf, blksiz);
01177         rc += blksiz;
01178         buf += blksiz;
01179         fp->f_pos += blksiz;
01180     }
01181     if (ec == 0 || ec == ENOSPC) {
01182         if (BankNodePointer(node)->node_size < fp->f_pos) {
01183             BankNodePointer(node)->node_size = fp->f_pos;
01184         }
01185         BankNodePointer(node)->node_mtime = time(0);
01186     }
01187     return rc;
01188 }
01189 
01190 #ifdef __HARVARD_ARCH__
01191 static int PnutFileWrite_P(NUTFILE * nfp, PGM_P buffer, int len)
01192 {
01193     return -1;
01194 }
01195 #endif
01196 
01208 static int PnutFileRead(NUTFILE * nfp, void *buffer, int len)
01209 {
01210     PNUTFILE *fp = nfp->nf_fcb;
01211     int ec = 0;
01212     int rc = 0;
01213     PNUT_BLKNUM node = fp->f_node;
01214     uint8_t *blkptr;
01215     size_t blksiz;
01216     char *buf = buffer;
01217 
01218     /* Respect end of file. */
01219     if (len > BankNodePointer(node)->node_size - fp->f_pos) {
01220         len = (size_t) (BankNodePointer(node)->node_size - fp->f_pos);
01221     }
01222     while (len) {
01223         if ((ec = PnutNodeGetDataPtr(node, fp->f_pos, (void *) &blkptr, &blksiz, 0)) != 0) {
01224             break;
01225         }
01226         if (blksiz >= len) {
01227             blksiz = len;
01228             len = 0;
01229         } else {
01230             len -= blksiz;
01231         }
01232         memcpy(buf, blkptr, blksiz);
01233         rc += blksiz;
01234         buf += blksiz;
01235         fp->f_pos += blksiz;
01236     }
01237     return rc;
01238 }
01239 
01240 static int PnutFileSeek(PNUTFILE * fp, long *pos, int whence)
01241 {
01242     int rc = 0;
01243     long npos = *pos;
01244 
01245     switch (whence) {
01246     case SEEK_CUR:
01247         npos += fp->f_pos;
01248         break;
01249     case SEEK_END:
01250         npos += BankNodePointer(fp->f_node)->node_size;
01251         break;
01252     }
01253 
01254     if (npos < 0 || npos > (long) BankNodePointer(fp->f_node)->node_size) {
01255         rc = EINVAL;
01256     } else {
01257         fp->f_pos = npos;
01258         *pos = npos;
01259     }
01260     return rc;
01261 }
01262 
01263 /* ------------------------ Special function interface ------------------------ */
01264 
01268 int PnutIOCtl(NUTDEVICE * dev, int req, void *conf)
01269 {
01270     int rc = -1;
01271 
01272     switch (req) {
01273     case FS_STATUS:
01274         {
01275             FSCP_STATUS *par = (FSCP_STATUS *) conf;
01276 
01277             rc = PnutStatus(par->par_path, par->par_stp);
01278         }
01279         break;
01280     case FS_DIR_CREATE:
01281         rc = PnutDirCreate((char *) conf);
01282         break;
01283     case FS_DIR_REMOVE:
01284         rc = PnutDelete((char *) conf);
01285         break;
01286     case FS_DIR_OPEN:
01287         rc = PnutDirOpen(dev, (DIR *) conf);
01288         break;
01289     case FS_DIR_CLOSE:
01290         rc = PnutDirClose((DIR *) conf);
01291         break;
01292     case FS_DIR_READ:
01293         rc = PnutDirRead((DIR *) conf);
01294         break;
01295     case FS_FILE_STATUS:
01296         rc = PnutFileStatus((PNUTFILE *) ((IOCTL_ARG2 *) conf)->arg1,   /* */
01297                             (struct stat *) ((IOCTL_ARG2 *) conf)->arg2);
01298         break;
01299     case FS_FILE_DELETE:
01300         rc = PnutDelete((char *) conf);
01301         break;
01302     case FS_FILE_SEEK:
01303         PnutFileSeek((PNUTFILE *) ((IOCTL_ARG3 *) conf)->arg1,  /* */
01304                      (long *) ((IOCTL_ARG3 *) conf)->arg2,      /* */
01305                      (int) ((IOCTL_ARG3 *) conf)->arg3);
01306         break;
01307     }
01308     return rc;
01309 }
01310 
01322 static long PnutFileSize(NUTFILE *nfp)
01323 {
01324     PNUTFILE *fp = nfp->nf_fcb;
01325 
01326     return BankNodePointer(fp->f_node)->node_size;
01327 }
01328 
01329 /* ------------------------ Initialization ------------------------ */
01330 
01342 static int PnutInit(NUTDEVICE * dev)
01343 {
01344     PNUT_BLKNUM i;
01345     int rc;
01346 
01347     /* Add all blocks to the free list. */
01348     for (i = 0; i < PNUT_TOTAL_BLOCKS; i++) {
01349         PnutBlockRelease(i);
01350     }
01351 
01352     /* Initialize the root directory. */
01353     if ((root = PnutNodeAlloc(NODETYPE_DIR)) == -1) {
01354         rc = ENOSPC;
01355     } else {
01356         if ((rc = PnutDirAddEntry(root, ".", root)) == 0) {
01357             rc = PnutDirAddEntry(root, "..", root);
01358         }
01359         if (rc) {
01360             PnutBlockRelease(root);
01361         }
01362     }
01363     return rc;
01364 }
01365 
01369 NUTDEVICE devPnut = {
01370     0,                          
01371     {'P', 'N', 'U', 'T', 0, 0, 0, 0, 0}
01372     ,                           
01373     IFTYP_RAM,                  
01374     0,                          
01375     0,                          
01376     0,                          
01377     0,                          
01378     PnutInit,                   
01379     PnutIOCtl,                  
01380     PnutFileRead,               
01381     PnutFileWrite,              
01382 #ifdef __HARVARD_ARCH__
01383     PnutFileWrite_P,            
01384 #endif
01385     PnutFileOpen,               
01386     PnutFileClose,              
01387     PnutFileSize                
01388 };
01389 

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