Nut/OS  4.10.3
API Reference
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, uint8_t **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             uint8_t *blkptr = (uint8_t *) 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 = NULL;
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         uint8_t *ptr = (uint8_t *)entry;
00556         if (PnutNodeGetDataPtr(node, pos, &ptr, &size, 0) || size == 0) {
00557             /* No more entries. */
00558             break;
00559         }
00560         entry = (PNUT_DIRENTRY *)ptr;
00561 
00562         if (size >= PNUT_DIRENT_SIZE) {
00563             if (entry->dir_inuse && strcmp(entry->dir_name, ".") && strcmp(entry->dir_name, "..")) {
00564                 /* Entry found */
00565                 rc = 0;
00566                 break;
00567             }
00568         }
00569     }
00570     return rc;
00571 }
00572 
00586 static int PnutDirFindEntry(PNUT_BLKNUM node, CONST char *path, size_t len, PNUT_DIRENTRY ** entry)
00587 {
00588     int rc = ENOENT;
00589     uint32_t pos;
00590     size_t size;
00591 
00592     /* Loop through the data blocks of this directory node. */
00593     for (pos = 0; pos < BankNodePointer(node)->node_size; pos += PNUT_DIRENT_SIZE) {
00594 
00595         /* Get the pointer to the next directory entry. */
00596         uint8_t *ptr = (uint8_t *)(*entry);
00597         if (PnutNodeGetDataPtr(node, pos, &ptr, &size, 0) || size == 0) {
00598             /* No more entries. */
00599             break;
00600         }
00601         *entry = (PNUT_DIRENTRY *)ptr;
00602 
00603         /* Compare this entry. */
00604         if (size >= PNUT_DIRENT_SIZE) { /* Skip entries across block boundaries. */
00605             if ((*entry)->dir_inuse &&  /* Skip unused entries. */
00606                 strlen((*entry)->dir_name) == len &&    /* Skip non-matching names. */
00607                 strncmp((*entry)->dir_name, path, len) == 0) {
00608                 /* Requested entry found */
00609                 rc = 0;
00610                 break;
00611             }
00612         }
00613     }
00614     return rc;
00615 }
00616 
00617 /*
00618  * \brief Find directory entry of a specified path.
00619  *
00620  * \param node   First node block to search.
00621  * \param path   Path to find.
00622  * \param result Search result.
00623  *
00624  * \return Error code.
00625  */
00626 static int PnutDirFindPath(PNUT_BLKNUM node, CONST char *path, PNUT_FINDRESULT * result)
00627 {
00628     int rc = 0;
00629     size_t len;
00630     PNUT_DIRENTRY *entry = NULL;
00631 
00632     result->fr_pnode = node;
00633     result->fr_node = -1;
00634     result->fr_name = NULL;
00635 
00636     while (*path == '/') {
00637         path++;
00638     }
00639 
00640     if (*path == 0) {
00641         path = ".";
00642     }
00643 
00644     /* 
00645      * Loop for each path component.
00646      */
00647     while (*path) {
00648         CONST char *cp;
00649 
00650         /* Make sure that this is a directory node. */
00651         if (BankNodePointer(node)->node_type != NODETYPE_DIR) {
00652             rc = ENOTDIR;
00653             break;
00654         }
00655 
00656         /* Retrieve the length of first path component. */
00657         for (len = 0, cp = path; *cp && *cp != '/'; cp++) {
00658             len++;
00659         }
00660 
00661         /* 
00662          * If this component is last, we found all parents.
00663          * Keep the last one stored in the result.
00664          */
00665         if (*cp == 0) {
00666             result->fr_name = path;
00667         }
00668 
00669         /* Try to find this component. */
00670         if ((rc = PnutDirFindEntry(node, path, len, &entry)) != 0) {
00671             rc = ENOENT;
00672             break;
00673         }
00674 
00675         /* Component found. Return if this was the last one. */
00676         result->fr_node = entry->dir_node;
00677         if (*cp == 0) {
00678             break;
00679         }
00680 
00681         /* Not the last component. Store node as previous node. */
00682         result->fr_pnode = result->fr_node;
00683 
00684         /* Move forward to the next path component. */
00685         node = result->fr_node;
00686         path += len;
00687         while (*path == '/') {
00688             path++;
00689         }
00690     }
00691     return rc;
00692 }
00693 
00694 
00705 static int PnutDirOpen(NUTDEVICE * dev, DIR * dir)
00706 {
00707     PNUTFILE *fp;
00708     PNUT_FINDRESULT found;
00709     int rc;
00710 
00711     /* Locate the entry in our directory structure. */
00712     if ((rc = PnutDirFindPath(root, dir->dd_buf, &found)) != 0) {
00713         errno = rc;
00714         rc = -1;
00715     } else {
00716         if (BankNodePointer(found.fr_node)->node_type != NODETYPE_DIR) {
00717             errno = ENOTDIR;
00718             rc = -1;
00719         }
00720         /* Allocate a PNUT file descriptor. */
00721         else if ((fp = malloc(sizeof(PNUTFILE))) == 0) {
00722             rc = -1;
00723         }
00724         /* 
00725          * Initialize the descriptor and store it in the directory
00726          * information structure.
00727          */
00728         else {
00729             fp->f_node = found.fr_node;
00730             fp->f_pos = 0;
00731 
00732             if ((dir->dd_fd = malloc(sizeof(NUTFILE))) == 0) {
00733                 free(fp);
00734                 rc = -1;
00735             }
00736             else {
00737                 memset(dir->dd_fd, 0, sizeof(NUTFILE));
00738                 dir->dd_fd->nf_dev = dev;
00739                 dir->dd_fd->nf_fcb = fp;
00740                 /* Keep track of the number of open calls. */
00741                 BankNodePointer(fp->f_node)->node_refs++;
00742             }
00743         }
00744     }
00745     return rc;
00746 }
00747 
00751 static int PnutDirClose(DIR * dir)
00752 {
00753     if (dir && dir->dd_fd) {
00754         if (dir->dd_fd->nf_fcb) {
00755             PNUTFILE *fp = dir->dd_fd->nf_fcb;
00756 
00757             BankNodePointer(fp->f_node)->node_refs--;
00758             free(fp);
00759         }
00760         free(dir->dd_fd);
00761         return 0;
00762     }
00763     return EINVAL;
00764 }
00765 
00769 static int PnutDirRead(DIR * dir)
00770 {
00771     int rc = -1;
00772     uint32_t pos;
00773     PNUT_DIRENTRY *entry = NULL;
00774     size_t size;
00775     PNUTFILE *fp = dir->dd_fd->nf_fcb;
00776     struct dirent *ent = (struct dirent *) dir->dd_buf;
00777 
00778     ent->d_name[0] = 0;
00779     for (pos = fp->f_pos; pos < BankNodePointer(fp->f_node)->node_size; pos += PNUT_DIRENT_SIZE) {
00780         uint8_t *ptr = (uint8_t *)entry;
00781         /* Retrieve the entry at the current position. */
00782         rc = PnutNodeGetDataPtr(fp->f_node, pos, &ptr, &size, 0);
00783         if (rc || size == 0) {
00784             /* No more entries. */
00785             rc = -1;
00786             break;
00787         }
00788         entry = (PNUT_DIRENTRY *)ptr;
00789         fp->f_pos = pos + PNUT_DIRENT_SIZE;
00790 
00791         /* Skip entries across block boundaries and unused entires. */
00792         if (size >= PNUT_DIRENT_SIZE && entry->dir_inuse) {
00793             memset(ent, 0, sizeof(struct dirent));
00794             ent->d_fileno = entry->dir_node;
00795             ent->d_namlen = (uint8_t) strlen(entry->dir_name);
00796             strcpy(ent->d_name, entry->dir_name);
00797             break;
00798         }
00799     }
00800     return rc;
00801 }
00802 
00812 static int PnutDirAddEntry(PNUT_BLKNUM dnode, CONST char *name, PNUT_BLKNUM enode)
00813 {
00814     int rc = 0;
00815     uint32_t pos = 0;
00816     size_t size;
00817     PNUT_DIRENTRY *entry = NULL;
00818     PNUT_NODE *np;
00819 
00820     /*
00821      * Loop through all directory entries until an unused one
00822      * is found. If required, a new data block is allocated,
00823      * so success is guaranteed unless we run out of free
00824      * blocks.
00825      */
00826     for (;;) {
00827         /* 
00828          * Get the data pointer to the specified position. Automatically
00829          * create a new block if we reached the end of the data.
00830          */
00831         uint8_t *ptr = (uint8_t *)entry;
00832         if ((rc = PnutNodeGetDataPtr(dnode, pos, &ptr, &size, 1)) != 0) {
00833             break;
00834         }
00835         entry = (PNUT_DIRENTRY *)ptr;
00836         pos += PNUT_DIRENT_SIZE;
00837 
00838         /* Process entry if it doesn't cross block boundaries. */
00839         if (size >= PNUT_DIRENT_SIZE) {
00840             /* Did we find a previously released or a newly allocated entry? */
00841             if (entry->dir_inuse == 0) {
00842                 /* Update the entry. */
00843                 entry->dir_node = enode;
00844                 entry->dir_inuse = 1;
00845                 strcpy(entry->dir_name, name);
00846 
00847                 /* Update the directory node. */
00848                 np = BankNodePointer(dnode);
00849                 np->node_mtime = time(0);
00850                 if (pos > np->node_size) {
00851                     np->node_size = pos;
00852                 }
00853 
00854                 /* Update the entry's node. */
00855                 np = BankNodePointer(enode);
00856                 np->node_links++;
00857                 break;
00858             }
00859         }
00860     }
00861     return rc;
00862 }
00863 
00880 static int PnutDirDelEntry(PNUT_BLKNUM node, CONST char *name)
00881 {
00882     int rc;
00883     PNUT_DIRENTRY *entry = NULL;
00884     PNUT_NODE *rnp;
00885     PNUT_BLKNUM rnode;
00886 
00887     /* Make sure this entry exists. */
00888     if ((rc = PnutDirFindEntry(node, name, strlen(name), &entry)) != 0) {
00889         return rc;
00890     }
00891 
00892     /* The node to remove. */
00893     rnode = entry->dir_node;
00894     rnp = BankNodePointer(rnode);
00895 
00896     /* Make sure that this node has no pending open calls. */
00897     if (rnp->node_refs) {
00898         return EACCES;
00899     }
00900 
00901     /* We only remove empty directories. */
00902     if (rnp->node_type == NODETYPE_DIR) {
00903         if (rnp->node_links > 2 || !PnutDirIsEmpty(rnode)) {
00904             return EACCES;
00905         }
00906         rnp = BankNodePointer(node);
00907         rnp->node_links--;
00908         PnutNodeRelease(rnode);
00909     }
00910 
00911     /* Remove a file. */
00912     else {
00913         PnutNodeRelease(rnode);
00914     }
00915 
00916     /* Mark this entry unused. */
00917     PnutDirFindEntry(node, name, strlen(name), &entry);
00918     entry->dir_inuse = 0;
00919 
00920     return 0;
00921 }
00922 
00930 static int PnutDirCreate(CONST char *path)
00931 {
00932     PNUT_BLKNUM node;
00933     PNUT_FINDRESULT found;
00934     int ec;
00935 
00936     /* Return an error if this entry already exists. */
00937     if ((ec = PnutDirFindPath(root, path, &found)) == 0) {
00938         ec = EEXIST;
00939     }
00940 
00941     /* If this component does not exist, we can create it... */
00942     else if (ec == ENOENT) {
00943         /* ...provided that all parents had been found. */
00944         if (found.fr_name) {
00945             /* Allocate a new node block. */
00946             if ((node = PnutNodeAlloc(NODETYPE_DIR)) < 0) {
00947                 ec = ENOSPC;
00948             }
00949             /* Create a reference to itself. */
00950             else if ((ec = PnutDirAddEntry(node, ".", node)) != 0) {
00951                 PnutNodeRelease(node);
00952             }
00953             /* Create a reference to the parent. */
00954             else if ((ec = PnutDirAddEntry(node, "..", found.fr_pnode)) != 0) {
00955                 PnutNodeRelease(node);
00956             }
00957             /* Create a reference in the parent. */
00958             else if ((ec = PnutDirAddEntry(found.fr_pnode, found.fr_name, node)) != 0) {
00959                 PnutNodeRelease(node);
00960             }
00961         }
00962     }
00963 
00964     /* Something went wrong. */
00965     if (ec) {
00966         errno = ec;
00967         return -1;
00968     }
00969     return 0;
00970 }
00971 
00972 /* ------------------------ File routines ------------------------ */
00973 
00996 static NUTFILE *PnutFileOpen(NUTDEVICE * dev, CONST char *path, int mode, int acc)
00997 {
00998     PNUT_BLKNUM node = -1;
00999     PNUT_FINDRESULT found;
01000     int rc;
01001     PNUTFILE *file;
01002     NUTFILE *nfp = NUTFILE_EOF;
01003 
01004     /* Try to find an exisiting file. */
01005     if ((rc = PnutDirFindPath(root, path, &found)) == 0) {
01006         /* Return an error, if this is no regular file. */
01007         if (BankNodePointer(found.fr_node)->node_type != NODETYPE_REG) {
01008             errno = EISDIR;
01009         }
01010 
01011         /*
01012          * We return an error, if the file exists and _O_EXCL has been
01013          * set with _O_CREAT.
01014          */
01015         else if ((mode & (_O_CREAT | _O_EXCL)) == (_O_CREAT | _O_EXCL)) {
01016             errno = EEXIST;
01017         } else {
01018             node = found.fr_node;
01019             if (mode & _O_TRUNC) {
01020                 PnutNodeDataRelease(node);
01021             }
01022         }
01023     }
01024 
01025     else if (rc == ENOENT) {
01026         /*
01027          * If the search failed on the last path component, then
01028          * PnutDirFindPath() set fr_name. Only in this case we can
01029          * create a new file.
01030          */
01031         if (found.fr_name && (mode & _O_CREAT)) {
01032             node = PnutNodeAlloc(NODETYPE_REG);
01033 
01034             if (node < 0) {
01035                 errno = ENOSPC;
01036                 return NUTFILE_EOF;
01037             }
01038 
01039             rc = PnutDirAddEntry(found.fr_pnode, found.fr_name, node);
01040 
01041             if (rc) {
01042                 PnutBlockRelease(node);
01043             }
01044         }
01045     }
01046 
01047     if (rc == 0 && node >= 0) {
01048         if ((file = malloc(sizeof(PNUTFILE))) != 0) {
01049             file->f_flag |= mode & (_O_RDONLY | _O_WRONLY | _O_APPEND);
01050             file->f_pos = (mode & _O_APPEND) ? BankNodePointer(node)->node_size : 0;
01051             file->f_node = node;
01052 
01053             if ((nfp = malloc(sizeof(NUTFILE))) == 0) {
01054                 free(file);
01055                 nfp = NUTFILE_EOF;
01056             } else {
01057                 nfp->nf_next = 0;
01058                 nfp->nf_dev = dev;
01059                 nfp->nf_fcb = file;
01060                 /* Keep track of the number of open calls. */
01061                 BankNodePointer(node)->node_refs++;
01062             }
01063         }
01064     }
01065     return nfp;
01066 }
01067 
01071 static int PnutFileClose(NUTFILE * nfp)
01072 {
01073     if (nfp != NUTFILE_EOF) {
01074         PNUTFILE *fp = nfp->nf_fcb;
01075 
01076         if (fp) {
01077             BankNodePointer(fp->f_node)->node_refs--;
01078             free(fp);
01079         }
01080         free(nfp);
01081 
01082         return 0;
01083     }
01084     return EINVAL;
01085 }
01086 
01094 static int PnutDelete(char *path)
01095 {
01096     int ec;
01097     PNUT_FINDRESULT found;
01098 
01099     if ((ec = PnutDirFindPath(root, path, &found)) == 0) {
01100         ec = PnutDirDelEntry(found.fr_pnode, found.fr_name);
01101     }
01102     if (ec) {
01103         errno = ec;
01104         return -1;
01105     }
01106     return 0;
01107 }
01108 
01117 static int PnutStatus(CONST char *path, struct stat *status)
01118 {
01119     int rc;
01120     PNUT_FINDRESULT found;
01121 
01122     if ((rc = PnutDirFindPath(root, path, &found)) == 0) {
01123         status->st_mode = BankNodePointer(found.fr_node)->node_type;
01124         status->st_ino = found.fr_node;
01125         status->st_nlink = BankNodePointer(found.fr_node)->node_links;
01126         status->st_size = BankNodePointer(found.fr_node)->node_size;
01127         status->st_mtime = BankNodePointer(found.fr_node)->node_mtime;
01128     }
01129     return rc;
01130 }
01131 
01141 static int PnutFileStatus(PNUTFILE * fp, struct stat *status)
01142 {
01143     status->st_mode = BankNodePointer(fp->f_node)->node_type;
01144     status->st_ino = fp->f_node;
01145     status->st_nlink = BankNodePointer(fp->f_node)->node_links;
01146     status->st_size = BankNodePointer(fp->f_node)->node_size;
01147     status->st_mtime = BankNodePointer(fp->f_node)->node_mtime;
01148 
01149     return 0;
01150 }
01151 
01164 static int PnutFileWrite(NUTFILE * nfp, CONST void *buffer, int len)
01165 {
01166     PNUTFILE *fp = nfp->nf_fcb;
01167     int ec = 0;
01168     int rc = 0;
01169     PNUT_BLKNUM node = fp->f_node;
01170     uint8_t *blkptr;
01171     size_t blksiz;
01172     CONST char *buf = buffer;
01173 
01174     while (len) {
01175         if ((ec = PnutNodeGetDataPtr(node, fp->f_pos, &blkptr, &blksiz, 1)) != 0) {
01176             break;
01177         }
01178         if (blksiz >= len) {
01179             blksiz = len;
01180             len = 0;
01181         } else {
01182             len -= blksiz;
01183         }
01184         memcpy(blkptr, buf, blksiz);
01185         rc += blksiz;
01186         buf += blksiz;
01187         fp->f_pos += blksiz;
01188     }
01189     if (ec == 0 || ec == ENOSPC) {
01190         if (BankNodePointer(node)->node_size < fp->f_pos) {
01191             BankNodePointer(node)->node_size = fp->f_pos;
01192         }
01193         BankNodePointer(node)->node_mtime = time(0);
01194     }
01195     return rc;
01196 }
01197 
01198 #ifdef __HARVARD_ARCH__
01199 static int PnutFileWrite_P(NUTFILE * nfp, PGM_P buffer, int len)
01200 {
01201     return -1;
01202 }
01203 #endif
01204 
01216 static int PnutFileRead(NUTFILE * nfp, void *buffer, int len)
01217 {
01218     PNUTFILE *fp = nfp->nf_fcb;
01219     int ec = 0;
01220     int rc = 0;
01221     PNUT_BLKNUM node = fp->f_node;
01222     uint8_t *blkptr;
01223     size_t blksiz;
01224     char *buf = buffer;
01225 
01226     /* Respect end of file. */
01227     if (len > BankNodePointer(node)->node_size - fp->f_pos) {
01228         len = (size_t) (BankNodePointer(node)->node_size - fp->f_pos);
01229     }
01230     while (len) {
01231         if ((ec = PnutNodeGetDataPtr(node, fp->f_pos, &blkptr, &blksiz, 0)) != 0) {
01232             break;
01233         }
01234         if (blksiz >= len) {
01235             blksiz = len;
01236             len = 0;
01237         } else {
01238             len -= blksiz;
01239         }
01240         memcpy(buf, blkptr, blksiz);
01241         rc += blksiz;
01242         buf += blksiz;
01243         fp->f_pos += blksiz;
01244     }
01245     return rc;
01246 }
01247 
01248 static int PnutFileSeek(PNUTFILE * fp, long *pos, int whence)
01249 {
01250     int rc = 0;
01251     long npos = *pos;
01252 
01253     switch (whence) {
01254     case SEEK_CUR:
01255         npos += fp->f_pos;
01256         break;
01257     case SEEK_END:
01258         npos += BankNodePointer(fp->f_node)->node_size;
01259         break;
01260     }
01261 
01262     if (npos < 0 || npos > (long) BankNodePointer(fp->f_node)->node_size) {
01263         rc = EINVAL;
01264     } else {
01265         fp->f_pos = npos;
01266         *pos = npos;
01267     }
01268     return rc;
01269 }
01270 
01271 /* ------------------------ Special function interface ------------------------ */
01272 
01276 int PnutIOCtl(NUTDEVICE * dev, int req, void *conf)
01277 {
01278     int rc = -1;
01279 
01280     switch (req) {
01281     case FS_STATUS:
01282         {
01283             FSCP_STATUS *par = (FSCP_STATUS *) conf;
01284 
01285             rc = PnutStatus(par->par_path, par->par_stp);
01286         }
01287         break;
01288     case FS_DIR_CREATE:
01289         rc = PnutDirCreate((char *) conf);
01290         break;
01291     case FS_DIR_REMOVE:
01292         rc = PnutDelete((char *) conf);
01293         break;
01294     case FS_DIR_OPEN:
01295         rc = PnutDirOpen(dev, (DIR *) conf);
01296         break;
01297     case FS_DIR_CLOSE:
01298         rc = PnutDirClose((DIR *) conf);
01299         break;
01300     case FS_DIR_READ:
01301         rc = PnutDirRead((DIR *) conf);
01302         break;
01303     case FS_FILE_STATUS:
01304         rc = PnutFileStatus((PNUTFILE *) ((IOCTL_ARG2 *) conf)->arg1,   /* */
01305                             (struct stat *) ((IOCTL_ARG2 *) conf)->arg2);
01306         break;
01307     case FS_FILE_DELETE:
01308         rc = PnutDelete((char *) conf);
01309         break;
01310     case FS_FILE_SEEK:
01311         PnutFileSeek((PNUTFILE *) ((IOCTL_ARG3 *) conf)->arg1,  /* */
01312                      (long *) ((IOCTL_ARG3 *) conf)->arg2,      /* */
01313                      (int) ((IOCTL_ARG3 *) conf)->arg3);
01314         break;
01315     }
01316     return rc;
01317 }
01318 
01330 static long PnutFileSize(NUTFILE *nfp)
01331 {
01332     PNUTFILE *fp = nfp->nf_fcb;
01333 
01334     return BankNodePointer(fp->f_node)->node_size;
01335 }
01336 
01337 /* ------------------------ Initialization ------------------------ */
01338 
01350 static int PnutInit(NUTDEVICE * dev)
01351 {
01352     PNUT_BLKNUM i;
01353     int rc;
01354 
01355     /* Add all blocks to the free list. */
01356     for (i = 0; i < PNUT_TOTAL_BLOCKS; i++) {
01357         PnutBlockRelease(i);
01358     }
01359 
01360     /* Initialize the root directory. */
01361     if ((root = PnutNodeAlloc(NODETYPE_DIR)) == -1) {
01362         rc = ENOSPC;
01363     } else {
01364         if ((rc = PnutDirAddEntry(root, ".", root)) == 0) {
01365             rc = PnutDirAddEntry(root, "..", root);
01366         }
01367         if (rc) {
01368             PnutBlockRelease(root);
01369         }
01370     }
01371     return rc;
01372 }
01373 
01377 NUTDEVICE devPnut = {
01378     0,                          
01379     {'P', 'N', 'U', 'T', 0, 0, 0, 0, 0}
01380     ,                           
01381     IFTYP_RAM,                  
01382     0,                          
01383     0,                          
01384     0,                          
01385     0,                          
01386     PnutInit,                   
01387     PnutIOCtl,                  
01388     PnutFileRead,               
01389     PnutFileWrite,              
01390 #ifdef __HARVARD_ARCH__
01391     PnutFileWrite_P,            
01392 #endif
01393     PnutFileOpen,               
01394     PnutFileClose,              
01395     PnutFileSize                
01396 };
01397