Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/****************************************************************************
* fs/nxffs/nxffs_open.c
*
* Copyright (C) 2011 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <spudmonkey@racsa.co.cr>
*
* References: Linux/Documentation/filesystems/romfs.txt
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name NuttX nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <string.h>
#include <fcntl.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/kmalloc.h>
#include <nuttx/fs.h>
#include <nuttx/mtd.h>
#include "nxffs.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/****************************************************************************
* Private Types
****************************************************************************/
/****************************************************************************
****************************************************************************/
/* Since we are limited to a single file opened for writing, it makes sense
* to pre-allocate the write state structure.
*/
#ifdef CONFIG_NXFSS_PREALLOCATED
static struct nxffs_wrfile_s g_wrfile;
#endif
/****************************************************************************
****************************************************************************/
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Find a valid location for the inode header. A valid location will have
* these properties:
*
* 1. It will lie in the free flash region.
* 2. It will have enough contiguous memory to hold the entire header
* (excluding the file name which may lie in the next block).
* 3. The memory at this location will be fully erased.
*
* This function will only perform the checks of 1) and 2).
*
* Input Parameters:
* volume - Describes the NXFFS volume
* wrfile - Contains the current guess for the header position. On
* successful return, this field will hold the selected header
* position.
*
* Returned Value:
* Zero is returned on success. Otherwise, a negated errno value is
* returned indicating the nature of the failure. Of special interest
* the return error of -ENOSPC which means that the FLASH volume is
* full and should be repacked.
*
* On successful return the following are also valid:
*
* wrfile->ofile.entry.hoffset - FLASH offset to candidate header position
* volume->ioblock - Read/write block number of the block containing the
* header position
* volume->iooffset - The offset in the block to the candidate header
* position.
* volume->froffset - Updated offset to the first free FLASH block.
*
****************************************************************************/
static inline int nxffs_hdrpos(FAR struct nxffs_volume_s *volume,
FAR struct nxffs_wrfile_s *wrfile)
{
int ret;
/* Reserve memory for the object */
ret = nxffs_wrreserve(volume, SIZEOF_NXFFS_INODE_HDR);
if (ret == OK)
{
/* Save the offset to the FLASH region reserved for the inode header */
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
}
/****************************************************************************
* Name: nxffs_nampos
*
* Description:
* Find a valid location for the inode name. A valid location will have
* these properties:
*
* 1. It will lie in the free flash region.
* 2. It will have enough contiguous memory to hold the entire name
* 3. The memory at this location will be fully erased.
*
* This function will only perform the checks of 1) and 2).
*
* Input Parameters:
* volume - Describes the NXFFS volume
* wrfile - Contains the current guess for the name position. On
* successful return, this field will hold the selected name
* position.
* namlen - The length of the name.
*
* Returned Value:
* Zero is returned on success. Otherwise, a negated errno value is
* returned indicating the nature of the failure. Of special interest
* the return error of -ENOSPC which means that the FLASH volume is
* full and should be repacked.
*
* On successful return the following are also valid:
*
* wrfile->ofile.entry.noffset - FLASH offset to candidate name position
* volume->ioblock - Read/write block number of the block containing the
* name position
* volume->iooffset - The offset in the block to the candidate name
* position.
* volume->froffset - Updated offset to the first free FLASH block.
*
****************************************************************************/
static inline int nxffs_nampos(FAR struct nxffs_volume_s *volume,
FAR struct nxffs_wrfile_s *wrfile,
int namlen)
int ret;
/* Reserve memory for the object */
ret = nxffs_wrreserve(volume, namlen);
if (ret == OK)
{
/* Save the offset to the FLASH region reserved for the inode name */
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
/****************************************************************************
* Name: nxffs_hdrerased
*
* Description:
* Find a valid location for the inode header. A valid location will have
* these properties:
*
* 1. It will lie in the free flash region.
* 2. It will have enough contiguous memory to hold the entire header
* (excluding the file name which may lie in the next block).
* 3. The memory at this location will be fully erased.
*
* This function will only perform the check 3).
*
* On entry it assumes:
*
* volume->ioblock - Read/write block number of the block containing the
* header position
* volume->iooffset - The offset in the block to the candidate header
* position.
*
* Input Parameters:
* volume - Describes the NXFFS volume
* wrfile - Contains the current guess for the header position. On
* successful return, this field will hold the selected header
* position.
*
* Returned Value:
* Zero is returned on success. Otherwise, a negated errno value is
* returned indicating the nature of the failure. Of special interest
* the return error of -ENOSPC which means that the FLASH volume is
* full and should be repacked.
*
* On successful return the following are also valid:
*
* wrfile->ofile.entry.hoffset - FLASH offset to candidate header position
* volume->ioblock - Read/write block number of the block containing the
* header position
* volume->iooffset - The offset in the block to the candidate header
* position.
* volume->froffset - Updated offset to the first free FLASH block.
*
****************************************************************************/
static inline int nxffs_hdrerased(FAR struct nxffs_volume_s *volume,
FAR struct nxffs_wrfile_s *wrfile)
{
int ret;
/* Find a valid location to save the inode header */
ret = nxffs_wrverify(volume, SIZEOF_NXFFS_INODE_HDR);
if (ret == OK)
{
/* This is where we will put the header */
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
}
return ret;
}
/****************************************************************************
* Name: nxffs_namerased
*
* Description:
* Find a valid location for the inode name. A valid location will have
* these properties:
*
* 1. It will lie in the free flash region.
* 2. It will have enough contiguous memory to hold the entire name
* (excluding the file name which may lie in the next block).
* 3. The memory at this location will be fully erased.
*
* This function will only perform the check 3).
*
* On entry it assumes:
*
* volume->ioblock - Read/write block number of the block containing the
* name position
* volume->iooffset - The offset in the block to the candidate name
* position.
*
* Input Parameters:
* volume - Describes the NXFFS volume
* wrfile - Contains the current guess for the name position. On
* successful return, this field will hold the selected name
* position.
*
* Returned Value:
* Zero is returned on success. Otherwise, a negated errno value is
* returned indicating the nature of the failure. Of special interest
* the return error of -ENOSPC which means that the FLASH volume is
* full and should be repacked.
*
* On successful return the following are also valid:
*
* wrfile->ofile.entry.noffset - FLASH offset to candidate name position
* volume->ioblock - Read/write block number of the block containing the
* name position
* volume->iooffset - The offset in the block to the candidate name
* position.
* volume->froffset - Updated offset to the first free FLASH block.
*
****************************************************************************/
static inline int nxffs_namerased(FAR struct nxffs_volume_s *volume,
FAR struct nxffs_wrfile_s *wrfile,
int namlen)
{
int ret;
/* Find a valid location to save the inode name */
ret = nxffs_wrverify(volume, namlen);
if (ret == OK)
{
/* This is where we will put the name */
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
/****************************************************************************
* Name: nxffs_wrname
*
* Description:
* Write the inode name to cache at the position verified by
* nxffs_namerased().
*
* On entry it assumes:
*
* entry->noffset - FLASH offset to final name position
* volume->ioblock - Read/write block number of the block containing the
* name position
* volume->iooffset - The offset in the block to the candidate name
* position.
*
* Input Parameters:
* volume - Describes the NXFFS volume
* entry - Describes the entry to be written.
*
* Returned Value:
* Zero is returned on success. Otherwise, a negated errno value is
* returned indicating the nature of the failure.
*
****************************************************************************/
static inline int nxffs_wrname(FAR struct nxffs_volume_s *volume,
FAR struct nxffs_entry_s *entry,
int namlen)
{
int ret;
/* Seek to the inode name position and assure that it is in the volume
* cache.
*/
nxffs_ioseek(volume, entry->noffset);
ret = nxffs_rdcache(volume, volume->ioblock);
if (ret < 0)
{
fdbg("Failed to read inode name block %d: %d\n",
volume->ioblock, -ret);
return ret;
}
/* Copy the inode name to the volume cache and write the inode name block */
memcpy(&volume->cache[volume->iooffset], entry->name, namlen);
ret = nxffs_wrcache(volume);
if (ret < 0)
{
fdbg("Failed to write inode header block %d: %d\n",
volume->ioblock, -ret);
}
return ret;
}
/****************************************************************************
* Name: nxffs_wropen
*
* Description:
* Handle opening for writing. Only a single writer is permitted and only
* file creation is supported.
*
****************************************************************************/
static inline int nxffs_wropen(FAR struct nxffs_volume_s *volume,
FAR struct nxffs_ofile_s **ppofile)
{
FAR struct nxffs_wrfile_s *wrfile;
FAR struct nxffs_entry_s entry;
bool packed;
bool truncate = false;
int namlen;
int ret;
/* Limitation: Only a single writer is permitted. Writing may involve
* extension of the file system in FLASH. Since files are contiguous
* in FLASH, only a single file may be extending the FLASH region.
*/
ret = sem_wait(&volume->wrsem);
if (ret != OK)
fdbg("sem_wait failed: %d\n", ret);
ret = -errno;
/* Get exclusive access to the volume. Note that the volume exclsem
* protects the open file list. Note that exclsem is ALWAYS taken
* after wrsem to avoid deadlocks.
*/
ret = sem_wait(&volume->exclsem);
if (ret != OK)
{
fdbg("sem_wait failed: %d\n", ret);
ret = -errno;
goto errout_with_wrsem;
}
/* Check if the file exists */
ret = nxffs_findinode(volume, name, &entry);
if (ret == OK)
{
FAR struct nxffs_ofile_s *ofile;
/* It exists. Is the file already open for reading? */
ofile = nxffs_findofile(volume, name);
if (ofile)
{
/* The file is already open.
* Limitation: Files cannot be open both for reading and writing.
*/
fdbg("File is open for reading\n");
ret = -ENOSYS;
goto errout_with_exclsem;
}
/* It would be an error if we are asked to create the file
else if ((oflags & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL))
}
/* Were we asked to truncate the file? NOTE: Don't truncate the
* file if we were not also asked to created it. See below...
* we will not re-create the file unless O_CREAT is also specified.
*/
else if ((oflags & (O_CREAT|O_TRUNC)) == (O_CREAT|O_TRUNC))
/* Just schedule the removal the file and fall through to re-create it.
* Note that the old file of the same name will not actually be removed
* until the new file is successfully written.
*/
}
/* The file exists and we were not asked to truncate (and recreate) it.
* Limitation: Cannot write to existing files.
*/
else
{
fdbg("File %s exists and we were not asked to truncate it\n");
}
}
/* Okay, the file is not open and does not exists (maybe because we deleted
* it). Now, make sure that we were asked to created it.
*/
}
/* Make sure that the length of the file name will fit in a uint8_t */
namlen = strlen(name);
{
fdbg("Name is too long: %d\n", namlen);
ret = -EINVAL;
}
/* Yes.. Create a new structure that will describe the state of this open
* file. NOTE that a special variant of the open file structure is used
* that includes additional information to support the write operation.
*/
#ifdef CONFIG_NXFSS_PREALLOCATED
wrfile = &g_wrfile;
memset(wrfile, 0, sizeof(struct nxffs_wrfile_s));
#else
wrfile = (FAR struct nxffs_wrfile_s *)kzalloc(sizeof(struct nxffs_wrfile_s));
if (!wrfile)
{
wrfile->ofile.entry.utc = time(NULL);
wrfile->truncate = truncate;
/* Save a copy of the inode name. */
wrfile->ofile.entry.name = strdup(name);
if (!wrfile->ofile.entry.name)
{
ret = -ENOMEM;
goto errout_with_ofile;
}
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
/* Allocate FLASH memory for the file and set up for the write.
*
* Loop until the inode header is configured or until a failure occurs.
* Note that nothing is written to FLASH. The inode header is not
* written until the file is closed.
*/
packed = false;
for (;;)
{
/* File a valid location to position the inode header. Start with the
* first byte in the free FLASH region.
*/
ret = nxffs_hdrpos(volume, wrfile);
if (ret == OK)
{
/* Find a region of memory in the block that is fully erased */
ret = nxffs_hdrerased(volume, wrfile);
if (ret == OK)
{
/* Valid memory for the inode header was found. Break out of
* the loop.
*/
break;
}
}
/* If no valid memory is found searching to the end of the volume,
* then -ENOSPC will be returned. Other errors are not handled.
*/
if (ret != -ENOSPC || packed)
{
fdbg("Failed to find inode header memory: %d\n", -ret);
}
/* -ENOSPC is a special case.. It means that the volume is full.
* Try to pack the volume in order to free up some space.
*/
ret = nxffs_pack(volume);
if (ret < 0)
{
fdbg("Failed to pack the volume: %d\n", -ret);
}
/* After packing the volume, froffset will be updated to point to the
* new free flash region. Try again.
*/
packed = true;
}
/* Loop until the inode name is configured or until a failure occurs.
*/
for (;;)
{
/* File a valid location to position the inode name. Start with the
* first byte in the free FLASH region.
*/
ret = nxffs_nampos(volume, wrfile, namlen);
if (ret == OK)
{
/* Find a region of memory in the block that is fully erased */
ret = nxffs_namerased(volume, wrfile, namlen);
if (ret == OK)
{
/* Valid memory for the inode header was found. Write the
* inode name to this location.
*/
ret = nxffs_wrname(volume, &wrfile->ofile.entry, namlen);
if (ret < 0)
{
fdbg("Failed to write the inode name: %d\n", -ret);
goto errout_with_name;
}
/* Then just break out of the loop reporting success. Note
* that the alllocated inode name string is retained; it
* will be needed later to calculate the inode CRC.
*/
break;
}
}
/* If no valid memory is found searching to the end of the volume,
* then -ENOSPC will be returned. Other errors are not handled.
*/
if (ret != -ENOSPC || packed)
{
fdbg("Failed to find inode name memory: %d\n", -ret);
/* -ENOSPC is a special case.. It means that the volume is full.
* Try to pack the volume in order to free up some space.
*/
ret = nxffs_pack(volume);
if (ret < 0)
{
fdbg("Failed to pack the volume: %d\n", -ret);
}
/* After packing the volume, froffset will be updated to point to the
* new free flash region. Try again.
*/
packed = true;
}
/* Add the open file structure to the head of the list of open files */
wrfile->ofile.flink = volume->ofiles;
volume->ofiles = &wrfile->ofile;
/* Indicate that the volume is open for writing and return the open file
* instance. Releasing exclsem allows other readers while the write is
* in progress. But wrsem is still held for this open file, preventing
* any further writers until this inode is closed.s
errout_with_exclsem:
sem_post(&volume->exclsem);
errout_with_wrsem:
sem_post(&volume->wrsem);
}
/****************************************************************************
* Name: nxffs_rdopen
*
* Description:
* Open an existing file for reading.
*
****************************************************************************/
static inline int nxffs_rdopen(FAR struct nxffs_volume_s *volume,
FAR const char *name,
FAR struct nxffs_ofile_s **ppofile)
{
FAR struct nxffs_ofile_s *ofile;
int ret;
/* Get exclusive access to the volume. Note that the volume exclsem
* protects the open file list.
*/
ret = sem_wait(&volume->exclsem);
if (ret != OK)
{
fdbg("sem_wait failed: %d\n", ret);
ret = -errno;
goto errout;
}
/* Check if the file has already been opened (for reading) */
if (ofile)
{
/* The file is already open.
* Limitation: Files cannot be open both for reading and writing.
*/
}
/* Just increment the reference count on the ofile */
ofile->crefs++;
fdbg("crefs: %d\n", ofile->crefs);
}
/* The file has not yet been opened.
* Limitation: The file must exist. We do not support creation of files
* read-only.
*/
else
{
/* Not already open.. create a new open structure */
ofile = (FAR struct nxffs_ofile_s *)kzalloc(sizeof(struct nxffs_ofile_s));
if (!ofile)
{
fdbg("ofile allocation failed\n");
}
/* Initialize the open file state structure */
/* Find the file on this volume associated with this file name */
ret = nxffs_findinode(volume, name, &ofile->entry);
if (ret != OK)
{
}
/* Add the open file structure to the head of the list of open files */
ofile->flink = volume->ofiles;
volume->ofiles = ofile;
}
/* Return the open file state structure */
*ppofile = ofile;
errout_with_ofile:
kfree(ofile);
errout_with_exclsem:
sem_post(&volume->exclsem);
errout:
return ret;
}
/****************************************************************************
****************************************************************************/
static inline void nxffs_remofile(FAR struct nxffs_volume_s *volume,
FAR struct nxffs_ofile_s *ofile)
{
FAR struct nxffs_ofile_s *prev;
FAR struct nxffs_ofile_s *curr;
/* Find the open file structure to be removed */
curr && curr != ofile;
prev = curr, curr = curr->flink);
/* Was it found? */
if (curr)
{
/* Yes.. at the head of the list? */
if (prev)
{
prev->flink = ofile->flink;
}
else
{
}
else
{
fdbg("ERROR: Open inode %p not found\n", ofile);
}
}
/****************************************************************************
* Name: nxffs_freeofile
*
* Description:
* Free resources held by an open file.
*
****************************************************************************/
static inline void nxffs_freeofile(FAR struct nxffs_volume_s *volume,
FAR struct nxffs_ofile_s *ofile)
{
/* Release the open file entry */
/* Then free the open file container (unless this the pre-alloated
* write-only open file container)
*/
/****************************************************************************
* Name: nxffs_wrclose
*
* Description:
* Perform special operations when a file is closed:
* 1. Write the file block header
* 2. Remove any file with the same name that was discovered when the
* file was open for writing, and finally,
* 3. Write the new file inode.
*
* Input parameters
* volume - Describes the NXFFS volume
* wrfile - Describes the state of the open file
*
****************************************************************************/
static inline int nxffs_wrclose(FAR struct nxffs_volume_s *volume,
FAR struct nxffs_wrfile_s *wrfile)
/* Yes.. Write the final file block header */
ret = nxffs_wrblkhdr(volume, wrfile);
if (ret < 0)
{
fdbg("Failed to write the final block of the file: %d\n", -ret);
goto errout;
}
/* Truncation is implemented by writing the new file, then deleting the
* older version of the file. Note that we removed the entry from the
* open file list earlier in the close sequence; this will prevent the
* open file check from failing when we remove the old version of the
* file.
*/
{
fvdbg("Removing old file: %s\n", wrfile->ofile.entry.name);
ret = nxffs_rminode(volume, wrfile->ofile.entry.name);
if (ret < 0)
{
fdbg("nxffs_rminode failed: %d\n", -ret);
goto errout;
}
}
ret = nxffs_wrinode(volume, &wrfile->ofile.entry);
/* The volume is now available for other writers */
errout:
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: nxffs_findofile
*
* Description:
* Search the list of already opened files to see if the inode of this
* name is one of the opened files.
*
* Input Parameters:
* name - The name of the inode to check.
*
* Returned Value:
* If an inode of this name is found in the list of opened inodes, then
* a reference to the open file structure is returned. NULL is returned
* otherwise.
*
****************************************************************************/
FAR struct nxffs_ofile_s *nxffs_findofile(FAR struct nxffs_volume_s *volume,
FAR const char *name)
{
FAR struct nxffs_ofile_s *ofile;
/* Check every open file. Note that the volume exclsem protects the
* list of open files.
*/
for (ofile = volume->ofiles; ofile; ofile = ofile->flink)
{
/* Check for a name match */
if (strcmp(name, ofile->entry.name) == 0)
{
return ofile;
}
}
return NULL;
}
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
/****************************************************************************
* Name: nxffs_findwriter
*
* Description:
* Search the list of already opened files and return the open file
* instance for the write.
*
* Input Parameters:
* volume - Describes the NXFFS volume.
*
* Returned Value:
* If there is an active writer of the volume, its open file instance is
* returned. NULL is returned otherwise.
*
****************************************************************************/
FAR struct nxffs_wrfile_s *nxffs_findwriter(FAR struct nxffs_volume_s *volume)
{
/* We can tell if the write is in-use because it will have an allocated
* name attached.
*/
#ifdef CONFIG_NXFSS_PREALLOCATED
return g_wrfile.ofile.entry.name != NULL ? &g_wrfile : NULL;
#else
# error "Missing implementation"
#endif
}
/****************************************************************************
* Name: nxffs_open
*
* Description:
* This is the standard mountpoint open method.
*
****************************************************************************/