mirror of
https://bitbucket.org/librepilot/librepilot.git
synced 2024-11-29 07:24:13 +01:00
Merge remote-tracking branch 'origin/abeck/OP_1503v3-yaffs' into next
This commit is contained in:
commit
2dc777304e
32
flight/pios/common/libraries/yaffs2/README.txt
Normal file
32
flight/pios/common/libraries/yaffs2/README.txt
Normal file
@ -0,0 +1,32 @@
|
||||
yaffs2 library in flight/pios/common/libraries/yaffs2
|
||||
|
||||
Implementation of core Yaffs Direct:
|
||||
yaffs_allocator.c Allocates Yaffs object and tnode structures.
|
||||
yaffs_checkpointrw.c Streamer for writing checkpoint data
|
||||
yaffs_ecc.c ECC code
|
||||
yaffs_guts.c The major Yaffs algorithms.
|
||||
yaffs_nand.c Flash interfacing abstraction.
|
||||
yaffs_packedtags1.c Tags packing code
|
||||
yaffs_packedtags2.c
|
||||
yaffs_qsort.c Qsort used during Yaffs2 scanning
|
||||
yaffs_tagscompat.c Tags compatibility code to support Yaffs1 mode.
|
||||
yaffs_tagsvalidity.c Tags validity checking.
|
||||
yaffsfs.c The Yaffs direct interface
|
||||
yaffs_hweight.c Linux hweight implementation equivalent (Is this in OP TODO)
|
||||
yaffs_list.c Linked list implementation
|
||||
yaffs_tarsmarshall.c
|
||||
Interface between Yaffs and OP PiOS or Posix:
|
||||
ydirectenv.h Environment wrappers for Yaffs direct to suit the OP firmware environment
|
||||
yaffs_osglue.h Interface for Yaffs to use to access OS method
|
||||
Interface to Yaffs Direct:
|
||||
yaffsfs.h and interface structures and functions defined here
|
||||
|
||||
POSIX Implementation in flight/pios/common/libraries/posix
|
||||
PiOS Implementation in flight/pios/common/libraries/PiOS
|
||||
|
||||
CHANGE LOG:
|
||||
1. Initial import
|
||||
2. ydirectenv.h Defined Y_LOFF_T to be int32_t. Needs to be signed to return -1 for lseek invalid
|
||||
3. library.mk Compiler defines to set yaffs2 diect mode and port options
|
||||
4. simposix Added simposix implementation of lower layers
|
||||
5. arm compilation Addressed compile errors in arm toolchain. Generally marked changes with CONFIG_YAFFS_OP
|
30
flight/pios/common/libraries/yaffs2/inc/yaffs_allocator.h
Normal file
30
flight/pios/common/libraries/yaffs2/inc/yaffs_allocator.h
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
#ifndef __YAFFS_ALLOCATOR_H__
|
||||
#define __YAFFS_ALLOCATOR_H__
|
||||
|
||||
#include "yaffs_guts.h"
|
||||
|
||||
void yaffs_init_raw_tnodes_and_objs(struct yaffs_dev *dev);
|
||||
void yaffs_deinit_raw_tnodes_and_objs(struct yaffs_dev *dev);
|
||||
|
||||
struct yaffs_tnode *yaffs_alloc_raw_tnode(struct yaffs_dev *dev);
|
||||
void yaffs_free_raw_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn);
|
||||
|
||||
struct yaffs_obj *yaffs_alloc_raw_obj(struct yaffs_dev *dev);
|
||||
void yaffs_free_raw_obj(struct yaffs_dev *dev, struct yaffs_obj *obj);
|
||||
|
||||
#endif
|
28
flight/pios/common/libraries/yaffs2/inc/yaffs_attribs.h
Normal file
28
flight/pios/common/libraries/yaffs2/inc/yaffs_attribs.h
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
#ifndef __YAFFS_ATTRIBS_H__
|
||||
#define __YAFFS_ATTRIBS_H__
|
||||
|
||||
#include "yaffs_guts.h"
|
||||
|
||||
void yaffs_load_attribs(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh);
|
||||
void yaffs_load_attribs_oh(struct yaffs_obj_hdr *oh, struct yaffs_obj *obj);
|
||||
void yaffs_attribs_init(struct yaffs_obj *obj, u32 gid, u32 uid, u32 rdev);
|
||||
void yaffs_load_current_time(struct yaffs_obj *obj, int do_a, int do_c);
|
||||
int yaffs_set_attribs(struct yaffs_obj *obj, struct iattr *attr);
|
||||
int yaffs_get_attribs(struct yaffs_obj *obj, struct iattr *attr);
|
||||
|
||||
#endif
|
33
flight/pios/common/libraries/yaffs2/inc/yaffs_bitmap.h
Normal file
33
flight/pios/common/libraries/yaffs2/inc/yaffs_bitmap.h
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Chunk bitmap manipulations
|
||||
*/
|
||||
|
||||
#ifndef __YAFFS_BITMAP_H__
|
||||
#define __YAFFS_BITMAP_H__
|
||||
|
||||
#include "yaffs_guts.h"
|
||||
|
||||
void yaffs_verify_chunk_bit_id(struct yaffs_dev *dev, int blk, int chunk);
|
||||
void yaffs_clear_chunk_bits(struct yaffs_dev *dev, int blk);
|
||||
void yaffs_clear_chunk_bit(struct yaffs_dev *dev, int blk, int chunk);
|
||||
void yaffs_set_chunk_bit(struct yaffs_dev *dev, int blk, int chunk);
|
||||
int yaffs_check_chunk_bit(struct yaffs_dev *dev, int blk, int chunk);
|
||||
int yaffs_still_some_chunks(struct yaffs_dev *dev, int blk);
|
||||
int yaffs_count_chunk_bits(struct yaffs_dev *dev, int blk);
|
||||
|
||||
#endif
|
33
flight/pios/common/libraries/yaffs2/inc/yaffs_checkptrw.h
Normal file
33
flight/pios/common/libraries/yaffs2/inc/yaffs_checkptrw.h
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
#ifndef __YAFFS_CHECKPTRW_H__
|
||||
#define __YAFFS_CHECKPTRW_H__
|
||||
|
||||
#include "yaffs_guts.h"
|
||||
|
||||
int yaffs2_checkpt_open(struct yaffs_dev *dev, int writing);
|
||||
|
||||
int yaffs2_checkpt_wr(struct yaffs_dev *dev, const void *data, int n_bytes);
|
||||
|
||||
int yaffs2_checkpt_rd(struct yaffs_dev *dev, void *data, int n_bytes);
|
||||
|
||||
int yaffs2_get_checkpt_sum(struct yaffs_dev *dev, u32 * sum);
|
||||
|
||||
int yaffs_checkpt_close(struct yaffs_dev *dev);
|
||||
|
||||
int yaffs2_checkpt_invalidate_stream(struct yaffs_dev *dev);
|
||||
|
||||
#endif
|
44
flight/pios/common/libraries/yaffs2/inc/yaffs_ecc.h
Normal file
44
flight/pios/common/libraries/yaffs2/inc/yaffs_ecc.h
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code implements the ECC algorithm used in SmartMedia.
|
||||
*
|
||||
* The ECC comprises 22 bits of parity information and is stuffed into 3 bytes.
|
||||
* The two unused bit are set to 1.
|
||||
* The ECC can correct single bit errors in a 256-byte page of data.
|
||||
* Thus, two such ECC blocks are used on a 512-byte NAND page.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __YAFFS_ECC_H__
|
||||
#define __YAFFS_ECC_H__
|
||||
|
||||
struct yaffs_ecc_other {
|
||||
unsigned char col_parity;
|
||||
unsigned line_parity;
|
||||
unsigned line_parity_prime;
|
||||
};
|
||||
|
||||
void yaffs_ecc_calc(const unsigned char *data, unsigned char *ecc);
|
||||
int yaffs_ecc_correct(unsigned char *data, unsigned char *read_ecc,
|
||||
const unsigned char *test_ecc);
|
||||
|
||||
void yaffs_ecc_calc_other(const unsigned char *data, unsigned n_bytes,
|
||||
struct yaffs_ecc_other *ecc);
|
||||
int yaffs_ecc_correct_other(unsigned char *data, unsigned n_bytes,
|
||||
struct yaffs_ecc_other *read_ecc,
|
||||
const struct yaffs_ecc_other *test_ecc);
|
||||
#endif
|
35
flight/pios/common/libraries/yaffs2/inc/yaffs_flashif.h
Normal file
35
flight/pios/common/libraries/yaffs2/inc/yaffs_flashif.h
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
#ifndef __YAFFS_FLASH_H__
|
||||
#define __YAFFS_FLASH_H__
|
||||
|
||||
|
||||
#include "yaffs_guts.h"
|
||||
int yflash_EraseBlockInNAND(struct yaffs_dev *dev, int blockNumber);
|
||||
int yflash_WriteChunkToNAND(struct yaffs_dev *dev, int nand_chunk,
|
||||
const u8 *data, const struct yaffs_spare *spare);
|
||||
int yflash_WriteChunkWithTagsToNAND(struct yaffs_dev *dev, int nand_chunk,
|
||||
const u8 *data, const struct yaffs_ext_tags *tags);
|
||||
int yflash_ReadChunkFromNAND(struct yaffs_dev *dev, int nand_chunk,
|
||||
u8 *data, struct yaffs_spare *spare);
|
||||
int yflash_ReadChunkWithTagsFromNAND(struct yaffs_dev *dev, int nand_chunk,
|
||||
u8 *data, struct yaffs_ext_tags *tags);
|
||||
int yflash_InitialiseNAND(struct yaffs_dev *dev);
|
||||
int yflash_MarkNANDBlockBad(struct yaffs_dev *dev, int block_no);
|
||||
int yflash_QueryNANDBlock(struct yaffs_dev *dev, int block_no,
|
||||
enum yaffs_block_state *state, u32 *seq_number);
|
||||
|
||||
#endif
|
35
flight/pios/common/libraries/yaffs2/inc/yaffs_flashif2.h
Normal file
35
flight/pios/common/libraries/yaffs2/inc/yaffs_flashif2.h
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
#ifndef __YAFFS_FLASH2_H__
|
||||
#define __YAFFS_FLASH2_H__
|
||||
|
||||
|
||||
#include "yaffs_guts.h"
|
||||
int yflash2_EraseBlockInNAND(struct yaffs_dev *dev, int blockNumber);
|
||||
int yflash2_WriteChunkToNAND(struct yaffs_dev *dev, int nand_chunk,
|
||||
const u8 *data, const struct yaffs_spare *spare);
|
||||
int yflash2_WriteChunkWithTagsToNAND(struct yaffs_dev *dev, int nand_chunk,
|
||||
const u8 *data, const struct yaffs_ext_tags *tags);
|
||||
int yflash2_ReadChunkFromNAND(struct yaffs_dev *dev, int nand_chunk,
|
||||
u8 *data, struct yaffs_spare *spare);
|
||||
int yflash2_ReadChunkWithTagsFromNAND(struct yaffs_dev *dev, int nand_chunk,
|
||||
u8 *data, struct yaffs_ext_tags *tags);
|
||||
int yflash2_InitialiseNAND(struct yaffs_dev *dev);
|
||||
int yflash2_MarkNANDBlockBad(struct yaffs_dev *dev, int block_no);
|
||||
int yflash2_QueryNANDBlock(struct yaffs_dev *dev, int block_no,
|
||||
enum yaffs_block_state *state, u32 *seq_number);
|
||||
|
||||
#endif
|
35
flight/pios/common/libraries/yaffs2/inc/yaffs_getblockinfo.h
Normal file
35
flight/pios/common/libraries/yaffs2/inc/yaffs_getblockinfo.h
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
#ifndef __YAFFS_GETBLOCKINFO_H__
|
||||
#define __YAFFS_GETBLOCKINFO_H__
|
||||
|
||||
#include "yaffs_guts.h"
|
||||
#include "yaffs_trace.h"
|
||||
|
||||
/* Function to manipulate block info */
|
||||
static inline struct yaffs_block_info *yaffs_get_block_info(struct yaffs_dev
|
||||
*dev, int blk)
|
||||
{
|
||||
if (blk < dev->internal_start_block || blk > dev->internal_end_block) {
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"**>> yaffs: get_block_info block %d is not valid",
|
||||
blk);
|
||||
BUG();
|
||||
}
|
||||
return &dev->block_info[blk - dev->internal_start_block];
|
||||
}
|
||||
|
||||
#endif
|
1010
flight/pios/common/libraries/yaffs2/inc/yaffs_guts.h
Normal file
1010
flight/pios/common/libraries/yaffs2/inc/yaffs_guts.h
Normal file
@ -0,0 +1,1010 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
#ifndef __YAFFS_GUTS_H__
|
||||
#define __YAFFS_GUTS_H__
|
||||
|
||||
#include "yportenv.h"
|
||||
|
||||
#define YAFFS_OK 1
|
||||
#define YAFFS_FAIL 0
|
||||
|
||||
/* Give us a Y=0x59,
|
||||
* Give us an A=0x41,
|
||||
* Give us an FF=0xff
|
||||
* Give us an S=0x53
|
||||
* And what have we got...
|
||||
*/
|
||||
#define YAFFS_MAGIC 0x5941ff53
|
||||
|
||||
/*
|
||||
* Tnodes form a tree with the tnodes in "levels"
|
||||
* Levels greater than 0 hold 8 slots which point to other tnodes.
|
||||
* Those at level 0 hold 16 slots which point to chunks in NAND.
|
||||
*
|
||||
* A maximum level of 8 thust supports files of size up to:
|
||||
*
|
||||
* 2^(3*MAX_LEVEL+4)
|
||||
*
|
||||
* Thus a max level of 8 supports files with up to 2^^28 chunks which gives
|
||||
* a maximum file size of around 512Gbytees with 2k chunks.
|
||||
*/
|
||||
#define YAFFS_NTNODES_LEVEL0 16
|
||||
#define YAFFS_TNODES_LEVEL0_BITS 4
|
||||
#define YAFFS_TNODES_LEVEL0_MASK 0xf
|
||||
|
||||
#define YAFFS_NTNODES_INTERNAL (YAFFS_NTNODES_LEVEL0 / 2)
|
||||
#define YAFFS_TNODES_INTERNAL_BITS (YAFFS_TNODES_LEVEL0_BITS - 1)
|
||||
#define YAFFS_TNODES_INTERNAL_MASK 0x7
|
||||
#define YAFFS_TNODES_MAX_LEVEL 8
|
||||
#define YAFFS_TNODES_MAX_BITS (YAFFS_TNODES_LEVEL0_BITS + \
|
||||
YAFFS_TNODES_INTERNAL_BITS * \
|
||||
YAFFS_TNODES_MAX_LEVEL)
|
||||
#define YAFFS_MAX_CHUNK_ID ((1 << YAFFS_TNODES_MAX_BITS) - 1)
|
||||
|
||||
#define YAFFS_MAX_FILE_SIZE_32 0x7fffffff
|
||||
|
||||
/* Constants for YAFFS1 mode */
|
||||
#define YAFFS_BYTES_PER_SPARE 16
|
||||
#define YAFFS_BYTES_PER_CHUNK 512
|
||||
#define YAFFS_CHUNK_SIZE_SHIFT 9
|
||||
#define YAFFS_CHUNKS_PER_BLOCK 32
|
||||
#define YAFFS_BYTES_PER_BLOCK (YAFFS_CHUNKS_PER_BLOCK*YAFFS_BYTES_PER_CHUNK)
|
||||
|
||||
#define YAFFS_MIN_YAFFS2_CHUNK_SIZE 1024
|
||||
#define YAFFS_MIN_YAFFS2_SPARE_SIZE 32
|
||||
|
||||
|
||||
|
||||
#define YAFFS_ALLOCATION_NOBJECTS 100
|
||||
#define YAFFS_ALLOCATION_NTNODES 100
|
||||
#define YAFFS_ALLOCATION_NLINKS 100
|
||||
|
||||
#define YAFFS_NOBJECT_BUCKETS 256
|
||||
|
||||
#define YAFFS_OBJECT_SPACE 0x40000
|
||||
#define YAFFS_MAX_OBJECT_ID (YAFFS_OBJECT_SPACE - 1)
|
||||
|
||||
/* Binary data version stamps */
|
||||
#define YAFFS_SUMMARY_VERSION 1
|
||||
#define YAFFS_CHECKPOINT_VERSION 7
|
||||
|
||||
#ifdef CONFIG_YAFFS_UNICODE
|
||||
#define YAFFS_MAX_NAME_LENGTH 127
|
||||
#define YAFFS_MAX_ALIAS_LENGTH 79
|
||||
#else
|
||||
#define YAFFS_MAX_NAME_LENGTH 255
|
||||
#define YAFFS_MAX_ALIAS_LENGTH 159
|
||||
#endif
|
||||
|
||||
#define YAFFS_SHORT_NAME_LENGTH 15
|
||||
|
||||
/* Some special object ids for pseudo objects */
|
||||
#define YAFFS_OBJECTID_ROOT 1
|
||||
#define YAFFS_OBJECTID_LOSTNFOUND 2
|
||||
#define YAFFS_OBJECTID_UNLINKED 3
|
||||
#define YAFFS_OBJECTID_DELETED 4
|
||||
|
||||
/* Fake object Id for summary data */
|
||||
#define YAFFS_OBJECTID_SUMMARY 0x10
|
||||
|
||||
/* Pseudo object ids for checkpointing */
|
||||
#define YAFFS_OBJECTID_CHECKPOINT_DATA 0x20
|
||||
#define YAFFS_SEQUENCE_CHECKPOINT_DATA 0x21
|
||||
|
||||
#define YAFFS_MAX_SHORT_OP_CACHES 20
|
||||
|
||||
#define YAFFS_N_TEMP_BUFFERS 6
|
||||
|
||||
/* We limit the number attempts at sucessfully saving a chunk of data.
|
||||
* Small-page devices have 32 pages per block; large-page devices have 64.
|
||||
* Default to something in the order of 5 to 10 blocks worth of chunks.
|
||||
*/
|
||||
#define YAFFS_WR_ATTEMPTS (5*64)
|
||||
|
||||
/* Sequence numbers are used in YAFFS2 to determine block allocation order.
|
||||
* The range is limited slightly to help distinguish bad numbers from good.
|
||||
* This also allows us to perhaps in the future use special numbers for
|
||||
* special purposes.
|
||||
* EFFFFF00 allows the allocation of 8 blocks/second (~1Mbytes) for 15 years,
|
||||
* and is a larger number than the lifetime of a 2GB device.
|
||||
*/
|
||||
#define YAFFS_LOWEST_SEQUENCE_NUMBER 0x00001000
|
||||
#define YAFFS_HIGHEST_SEQUENCE_NUMBER 0xefffff00
|
||||
|
||||
/* Special sequence number for bad block that failed to be marked bad */
|
||||
#define YAFFS_SEQUENCE_BAD_BLOCK 0xffff0000
|
||||
|
||||
/* ChunkCache is used for short read/write operations.*/
|
||||
struct yaffs_cache {
|
||||
struct yaffs_obj *object;
|
||||
int chunk_id;
|
||||
int last_use;
|
||||
int dirty;
|
||||
int n_bytes; /* Only valid if the cache is dirty */
|
||||
int locked; /* Can't push out or flush while locked. */
|
||||
u8 *data;
|
||||
};
|
||||
|
||||
/* yaffs1 tags structures in RAM
|
||||
* NB This uses bitfield. Bitfields should not straddle a u32 boundary
|
||||
* otherwise the structure size will get blown out.
|
||||
*/
|
||||
|
||||
struct yaffs_tags {
|
||||
u32 chunk_id:20;
|
||||
u32 serial_number:2;
|
||||
u32 n_bytes_lsb:10;
|
||||
u32 obj_id:18;
|
||||
u32 ecc:12;
|
||||
u32 n_bytes_msb:2;
|
||||
};
|
||||
|
||||
union yaffs_tags_union {
|
||||
struct yaffs_tags as_tags;
|
||||
u8 as_bytes[8];
|
||||
};
|
||||
|
||||
|
||||
/* Stuff used for extended tags in YAFFS2 */
|
||||
|
||||
enum yaffs_ecc_result {
|
||||
YAFFS_ECC_RESULT_UNKNOWN,
|
||||
YAFFS_ECC_RESULT_NO_ERROR,
|
||||
YAFFS_ECC_RESULT_FIXED,
|
||||
YAFFS_ECC_RESULT_UNFIXED
|
||||
};
|
||||
|
||||
enum yaffs_obj_type {
|
||||
YAFFS_OBJECT_TYPE_UNKNOWN,
|
||||
YAFFS_OBJECT_TYPE_FILE,
|
||||
YAFFS_OBJECT_TYPE_SYMLINK,
|
||||
YAFFS_OBJECT_TYPE_DIRECTORY,
|
||||
YAFFS_OBJECT_TYPE_HARDLINK,
|
||||
YAFFS_OBJECT_TYPE_SPECIAL
|
||||
};
|
||||
|
||||
#define YAFFS_OBJECT_TYPE_MAX YAFFS_OBJECT_TYPE_SPECIAL
|
||||
|
||||
struct yaffs_ext_tags {
|
||||
unsigned chunk_used; /* Status of the chunk: used or unused */
|
||||
unsigned obj_id; /* If 0 this is not used */
|
||||
unsigned chunk_id; /* If 0 this is a header, else a data chunk */
|
||||
unsigned n_bytes; /* Only valid for data chunks */
|
||||
|
||||
/* The following stuff only has meaning when we read */
|
||||
enum yaffs_ecc_result ecc_result;
|
||||
unsigned block_bad;
|
||||
|
||||
/* YAFFS 1 stuff */
|
||||
unsigned is_deleted; /* The chunk is marked deleted */
|
||||
unsigned serial_number; /* Yaffs1 2-bit serial number */
|
||||
|
||||
/* YAFFS2 stuff */
|
||||
unsigned seq_number; /* The sequence number of this block */
|
||||
|
||||
/* Extra info if this is an object header (YAFFS2 only) */
|
||||
|
||||
unsigned extra_available; /* Extra info available if not zero */
|
||||
unsigned extra_parent_id; /* The parent object */
|
||||
unsigned extra_is_shrink; /* Is it a shrink header? */
|
||||
unsigned extra_shadows; /* Does this shadow another object? */
|
||||
|
||||
enum yaffs_obj_type extra_obj_type; /* What object type? */
|
||||
|
||||
Y_LOFF_T extra_file_size; /* Length if it is a file */
|
||||
unsigned extra_equiv_id; /* Equivalent object for a hard link */
|
||||
};
|
||||
|
||||
/* Spare structure for YAFFS1 */
|
||||
struct yaffs_spare {
|
||||
u8 tb0;
|
||||
u8 tb1;
|
||||
u8 tb2;
|
||||
u8 tb3;
|
||||
u8 page_status; /* set to 0 to delete the chunk */
|
||||
u8 block_status;
|
||||
u8 tb4;
|
||||
u8 tb5;
|
||||
u8 ecc1[3];
|
||||
u8 tb6;
|
||||
u8 tb7;
|
||||
u8 ecc2[3];
|
||||
};
|
||||
|
||||
/*Special structure for passing through to mtd */
|
||||
struct yaffs_nand_spare {
|
||||
struct yaffs_spare spare;
|
||||
int eccres1;
|
||||
int eccres2;
|
||||
};
|
||||
|
||||
/* Block data in RAM */
|
||||
|
||||
enum yaffs_block_state {
|
||||
YAFFS_BLOCK_STATE_UNKNOWN = 0,
|
||||
|
||||
YAFFS_BLOCK_STATE_SCANNING,
|
||||
/* Being scanned */
|
||||
|
||||
YAFFS_BLOCK_STATE_NEEDS_SCAN,
|
||||
/* The block might have something on it (ie it is allocating or full,
|
||||
* perhaps empty) but it needs to be scanned to determine its true
|
||||
* state.
|
||||
* This state is only valid during scanning.
|
||||
* NB We tolerate empty because the pre-scanner might be incapable of
|
||||
* deciding
|
||||
* However, if this state is returned on a YAFFS2 device,
|
||||
* then we expect a sequence number
|
||||
*/
|
||||
|
||||
YAFFS_BLOCK_STATE_EMPTY,
|
||||
/* This block is empty */
|
||||
|
||||
YAFFS_BLOCK_STATE_ALLOCATING,
|
||||
/* This block is partially allocated.
|
||||
* At least one page holds valid data.
|
||||
* This is the one currently being used for page
|
||||
* allocation. Should never be more than one of these.
|
||||
* If a block is only partially allocated at mount it is treated as
|
||||
* full.
|
||||
*/
|
||||
|
||||
YAFFS_BLOCK_STATE_FULL,
|
||||
/* All the pages in this block have been allocated.
|
||||
* If a block was only partially allocated when mounted we treat
|
||||
* it as fully allocated.
|
||||
*/
|
||||
|
||||
YAFFS_BLOCK_STATE_DIRTY,
|
||||
/* The block was full and now all chunks have been deleted.
|
||||
* Erase me, reuse me.
|
||||
*/
|
||||
|
||||
YAFFS_BLOCK_STATE_CHECKPOINT,
|
||||
/* This block is assigned to holding checkpoint data. */
|
||||
|
||||
YAFFS_BLOCK_STATE_COLLECTING,
|
||||
/* This block is being garbage collected */
|
||||
|
||||
YAFFS_BLOCK_STATE_DEAD
|
||||
/* This block has failed and is not in use */
|
||||
};
|
||||
|
||||
#define YAFFS_NUMBER_OF_BLOCK_STATES (YAFFS_BLOCK_STATE_DEAD + 1)
|
||||
|
||||
struct yaffs_block_info {
|
||||
|
||||
s32 soft_del_pages:10; /* number of soft deleted pages */
|
||||
s32 pages_in_use:10; /* number of pages in use */
|
||||
u32 block_state:4; /* One of the above block states. */
|
||||
/* NB use unsigned because enum is sometimes
|
||||
* an int */
|
||||
u32 needs_retiring:1; /* Data has failed on this block, */
|
||||
/*need to get valid data off and retire*/
|
||||
u32 skip_erased_check:1;/* Skip the erased check on this block */
|
||||
u32 gc_prioritise:1; /* An ECC check or blank check has failed.
|
||||
Block should be prioritised for GC */
|
||||
u32 chunk_error_strikes:3; /* How many times we've had ecc etc
|
||||
failures on this block and tried to reuse it */
|
||||
u32 has_summary:1; /* The block has a summary */
|
||||
|
||||
u32 has_shrink_hdr:1; /* This block has at least one shrink header */
|
||||
u32 seq_number; /* block sequence number for yaffs2 */
|
||||
|
||||
};
|
||||
|
||||
/* -------------------------- Object structure -------------------------------*/
|
||||
/* This is the object structure as stored on NAND */
|
||||
|
||||
struct yaffs_obj_hdr {
|
||||
enum yaffs_obj_type type;
|
||||
|
||||
/* Apply to everything */
|
||||
int parent_obj_id;
|
||||
u16 sum_no_longer_used; /* checksum of name. No longer used */
|
||||
YCHAR name[YAFFS_MAX_NAME_LENGTH + 1];
|
||||
|
||||
/* The following apply to all object types except for hard links */
|
||||
u32 yst_mode; /* protection */
|
||||
|
||||
u32 yst_uid;
|
||||
u32 yst_gid;
|
||||
u32 yst_atime;
|
||||
u32 yst_mtime;
|
||||
u32 yst_ctime;
|
||||
|
||||
/* File size applies to files only */
|
||||
u32 file_size_low;
|
||||
|
||||
/* Equivalent object id applies to hard links only. */
|
||||
int equiv_id;
|
||||
|
||||
/* Alias is for symlinks only. */
|
||||
YCHAR alias[YAFFS_MAX_ALIAS_LENGTH + 1];
|
||||
|
||||
u32 yst_rdev; /* stuff for block and char devices (major/min) */
|
||||
|
||||
u32 win_ctime[2];
|
||||
u32 win_atime[2];
|
||||
u32 win_mtime[2];
|
||||
|
||||
u32 inband_shadowed_obj_id;
|
||||
u32 inband_is_shrink;
|
||||
|
||||
u32 file_size_high;
|
||||
u32 reserved[1];
|
||||
int shadows_obj; /* This object header shadows the
|
||||
specified object if > 0 */
|
||||
|
||||
/* is_shrink applies to object headers written when wemake a hole. */
|
||||
u32 is_shrink;
|
||||
|
||||
};
|
||||
|
||||
/*--------------------------- Tnode -------------------------- */
|
||||
|
||||
struct yaffs_tnode {
|
||||
struct yaffs_tnode *internal[YAFFS_NTNODES_INTERNAL];
|
||||
};
|
||||
|
||||
/*------------------------ Object -----------------------------*/
|
||||
/* An object can be one of:
|
||||
* - a directory (no data, has children links
|
||||
* - a regular file (data.... not prunes :->).
|
||||
* - a symlink [symbolic link] (the alias).
|
||||
* - a hard link
|
||||
*/
|
||||
|
||||
struct yaffs_file_var {
|
||||
Y_LOFF_T file_size;
|
||||
Y_LOFF_T scanned_size;
|
||||
Y_LOFF_T shrink_size;
|
||||
int top_level;
|
||||
struct yaffs_tnode *top;
|
||||
};
|
||||
|
||||
struct yaffs_dir_var {
|
||||
struct list_head children; /* list of child links */
|
||||
struct list_head dirty; /* Entry for list of dirty directories */
|
||||
};
|
||||
|
||||
struct yaffs_symlink_var {
|
||||
YCHAR *alias;
|
||||
};
|
||||
|
||||
struct yaffs_hardlink_var {
|
||||
struct yaffs_obj *equiv_obj;
|
||||
u32 equiv_id;
|
||||
};
|
||||
|
||||
union yaffs_obj_var {
|
||||
struct yaffs_file_var file_variant;
|
||||
struct yaffs_dir_var dir_variant;
|
||||
struct yaffs_symlink_var symlink_variant;
|
||||
struct yaffs_hardlink_var hardlink_variant;
|
||||
};
|
||||
|
||||
struct yaffs_obj {
|
||||
u8 deleted:1; /* This should only apply to unlinked files. */
|
||||
u8 soft_del:1; /* it has also been soft deleted */
|
||||
u8 unlinked:1; /* An unlinked file.*/
|
||||
u8 fake:1; /* A fake object has no presence on NAND. */
|
||||
u8 rename_allowed:1; /* Some objects cannot be renamed. */
|
||||
u8 unlink_allowed:1;
|
||||
u8 dirty:1; /* the object needs to be written to flash */
|
||||
u8 valid:1; /* When the file system is being loaded up, this
|
||||
* object might be created before the data
|
||||
* is available
|
||||
* ie. file data chunks encountered before
|
||||
* the header.
|
||||
*/
|
||||
u8 lazy_loaded:1; /* This object has been lazy loaded and
|
||||
* is missing some detail */
|
||||
|
||||
u8 defered_free:1; /* Object is removed from NAND, but is
|
||||
* still in the inode cache.
|
||||
* Free of object is defered.
|
||||
* until the inode is released.
|
||||
*/
|
||||
u8 being_created:1; /* This object is still being created
|
||||
* so skip some verification checks. */
|
||||
u8 is_shadowed:1; /* This object is shadowed on the way
|
||||
* to being renamed. */
|
||||
|
||||
u8 xattr_known:1; /* We know if this has object has xattribs
|
||||
* or not. */
|
||||
u8 has_xattr:1; /* This object has xattribs.
|
||||
* Only valid if xattr_known. */
|
||||
|
||||
u8 serial; /* serial number of chunk in NAND.*/
|
||||
u16 sum; /* sum of the name to speed searching */
|
||||
|
||||
struct yaffs_dev *my_dev; /* The device I'm on */
|
||||
|
||||
struct list_head hash_link; /* list of objects in hash bucket */
|
||||
|
||||
struct list_head hard_links; /* hard linked object chain*/
|
||||
|
||||
/* directory structure stuff */
|
||||
/* also used for linking up the free list */
|
||||
struct yaffs_obj *parent;
|
||||
struct list_head siblings;
|
||||
|
||||
/* Where's my object header in NAND? */
|
||||
int hdr_chunk;
|
||||
|
||||
int n_data_chunks; /* Number of data chunks for this file. */
|
||||
|
||||
u32 obj_id; /* the object id value */
|
||||
|
||||
u32 yst_mode;
|
||||
|
||||
YCHAR short_name[YAFFS_SHORT_NAME_LENGTH + 1];
|
||||
|
||||
#ifdef CONFIG_YAFFS_WINCE
|
||||
u32 win_ctime[2];
|
||||
u32 win_mtime[2];
|
||||
u32 win_atime[2];
|
||||
#else
|
||||
u32 yst_uid;
|
||||
u32 yst_gid;
|
||||
u32 yst_atime;
|
||||
u32 yst_mtime;
|
||||
u32 yst_ctime;
|
||||
#endif
|
||||
|
||||
u32 yst_rdev;
|
||||
|
||||
void *my_inode;
|
||||
|
||||
enum yaffs_obj_type variant_type;
|
||||
|
||||
union yaffs_obj_var variant;
|
||||
|
||||
};
|
||||
|
||||
struct yaffs_obj_bucket {
|
||||
struct list_head list;
|
||||
int count;
|
||||
};
|
||||
|
||||
/* yaffs_checkpt_obj holds the definition of an object as dumped
|
||||
* by checkpointing.
|
||||
*/
|
||||
|
||||
struct yaffs_checkpt_obj {
|
||||
int struct_type;
|
||||
u32 obj_id;
|
||||
u32 parent_id;
|
||||
int hdr_chunk;
|
||||
enum yaffs_obj_type variant_type:3;
|
||||
u8 deleted:1;
|
||||
u8 soft_del:1;
|
||||
u8 unlinked:1;
|
||||
u8 fake:1;
|
||||
u8 rename_allowed:1;
|
||||
u8 unlink_allowed:1;
|
||||
u8 serial;
|
||||
int n_data_chunks;
|
||||
Y_LOFF_T size_or_equiv_obj;
|
||||
};
|
||||
|
||||
/*--------------------- Temporary buffers ----------------
|
||||
*
|
||||
* These are chunk-sized working buffers. Each device has a few.
|
||||
*/
|
||||
|
||||
struct yaffs_buffer {
|
||||
u8 *buffer;
|
||||
int in_use;
|
||||
};
|
||||
|
||||
/*----------------- Device ---------------------------------*/
|
||||
|
||||
struct yaffs_param {
|
||||
const YCHAR *name;
|
||||
|
||||
/*
|
||||
* Entry parameters set up way early. Yaffs sets up the rest.
|
||||
* The structure should be zeroed out before use so that unused
|
||||
* and default values are zero.
|
||||
*/
|
||||
|
||||
int inband_tags; /* Use unband tags */
|
||||
u32 total_bytes_per_chunk; /* Should be >= 512, does not need to
|
||||
be a power of 2 */
|
||||
int chunks_per_block; /* does not need to be a power of 2 */
|
||||
int spare_bytes_per_chunk; /* spare area size */
|
||||
int start_block; /* Start block we're allowed to use */
|
||||
int end_block; /* End block we're allowed to use */
|
||||
int n_reserved_blocks; /* Tuneable so that we can reduce
|
||||
* reserved blocks on NOR and RAM. */
|
||||
|
||||
int n_caches; /* If <= 0, then short op caching is disabled,
|
||||
* else the number of short op caches.
|
||||
*/
|
||||
int cache_bypass_aligned; /* If non-zero then bypass the cache for
|
||||
* aligned writes.
|
||||
*/
|
||||
|
||||
int use_nand_ecc; /* Flag to decide whether or not to use
|
||||
* NAND driver ECC on data (yaffs1) */
|
||||
int tags_9bytes; /* Use 9 byte tags */
|
||||
int no_tags_ecc; /* Flag to decide whether or not to do ECC
|
||||
* on packed tags (yaffs2) */
|
||||
|
||||
int is_yaffs2; /* Use yaffs2 mode on this device */
|
||||
|
||||
int empty_lost_n_found; /* Auto-empty lost+found directory on mount */
|
||||
|
||||
int refresh_period; /* How often to check for a block refresh */
|
||||
|
||||
/* Checkpoint control. Can be set before or after initialisation */
|
||||
u8 skip_checkpt_rd;
|
||||
u8 skip_checkpt_wr;
|
||||
|
||||
int enable_xattr; /* Enable xattribs */
|
||||
|
||||
int max_objects; /*
|
||||
* Set to limit the number of objects created.
|
||||
* 0 = no limit.
|
||||
*/
|
||||
|
||||
/* The remove_obj_fn function must be supplied by OS flavours that
|
||||
* need it.
|
||||
* yaffs direct uses it to implement the faster readdir.
|
||||
* Linux uses it to protect the directory during unlocking.
|
||||
*/
|
||||
void (*remove_obj_fn) (struct yaffs_obj *obj);
|
||||
|
||||
/* Callback to mark the superblock dirty */
|
||||
void (*sb_dirty_fn) (struct yaffs_dev *dev);
|
||||
|
||||
/* Callback to control garbage collection. */
|
||||
unsigned (*gc_control_fn) (struct yaffs_dev *dev);
|
||||
|
||||
/* Debug control flags. Don't use unless you know what you're doing */
|
||||
int use_header_file_size; /* Flag to determine if we should use
|
||||
* file sizes from the header */
|
||||
int disable_lazy_load; /* Disable lazy loading on this device */
|
||||
int wide_tnodes_disabled; /* Set to disable wide tnodes */
|
||||
int disable_soft_del; /* yaffs 1 only: Set to disable the use of
|
||||
* softdeletion. */
|
||||
|
||||
int defered_dir_update; /* Set to defer directory updates */
|
||||
|
||||
#ifdef CONFIG_YAFFS_AUTO_UNICODE
|
||||
int auto_unicode;
|
||||
#endif
|
||||
int always_check_erased; /* Force chunk erased check always on */
|
||||
|
||||
int disable_summary;
|
||||
int disable_bad_block_marking;
|
||||
|
||||
};
|
||||
|
||||
struct yaffs_driver {
|
||||
int (*drv_write_chunk_fn) (struct yaffs_dev *dev, int nand_chunk,
|
||||
const u8 *data, int data_len,
|
||||
const u8 *oob, int oob_len);
|
||||
int (*drv_read_chunk_fn) (struct yaffs_dev *dev, int nand_chunk,
|
||||
u8 *data, int data_len,
|
||||
u8 *oob, int oob_len,
|
||||
enum yaffs_ecc_result *ecc_result);
|
||||
int (*drv_erase_fn) (struct yaffs_dev *dev, int block_no);
|
||||
int (*drv_mark_bad_fn) (struct yaffs_dev *dev, int block_no);
|
||||
int (*drv_check_bad_fn) (struct yaffs_dev *dev, int block_no);
|
||||
int (*drv_initialise_fn) (struct yaffs_dev *dev);
|
||||
int (*drv_deinitialise_fn) (struct yaffs_dev *dev);
|
||||
};
|
||||
|
||||
struct yaffs_tags_handler {
|
||||
int (*write_chunk_tags_fn) (struct yaffs_dev *dev,
|
||||
int nand_chunk, const u8 *data,
|
||||
const struct yaffs_ext_tags *tags);
|
||||
int (*read_chunk_tags_fn) (struct yaffs_dev *dev,
|
||||
int nand_chunk, u8 *data,
|
||||
struct yaffs_ext_tags *tags);
|
||||
|
||||
int (*query_block_fn) (struct yaffs_dev *dev, int block_no,
|
||||
enum yaffs_block_state *state,
|
||||
u32 *seq_number);
|
||||
int (*mark_bad_fn) (struct yaffs_dev *dev, int block_no);
|
||||
};
|
||||
|
||||
struct yaffs_dev {
|
||||
struct yaffs_param param;
|
||||
struct yaffs_driver drv;
|
||||
struct yaffs_tags_handler tagger;
|
||||
|
||||
/* Context storage. Holds extra OS specific data for this device */
|
||||
|
||||
void *os_context;
|
||||
void *driver_context;
|
||||
|
||||
struct list_head dev_list;
|
||||
|
||||
int ll_init;
|
||||
/* Runtime parameters. Set up by YAFFS. */
|
||||
int data_bytes_per_chunk;
|
||||
|
||||
/* Non-wide tnode stuff */
|
||||
u16 chunk_grp_bits; /* Number of bits that need to be resolved if
|
||||
* the tnodes are not wide enough.
|
||||
*/
|
||||
u16 chunk_grp_size; /* == 2^^chunk_grp_bits */
|
||||
|
||||
/* Stuff to support wide tnodes */
|
||||
u32 tnode_width;
|
||||
u32 tnode_mask;
|
||||
u32 tnode_size;
|
||||
|
||||
/* Stuff for figuring out file offset to chunk conversions */
|
||||
u32 chunk_shift; /* Shift value */
|
||||
u32 chunk_div; /* Divisor after shifting: 1 for 2^n sizes */
|
||||
u32 chunk_mask; /* Mask to use for power-of-2 case */
|
||||
|
||||
int is_mounted;
|
||||
int read_only;
|
||||
int is_checkpointed;
|
||||
|
||||
/* Stuff to support block offsetting to support start block zero */
|
||||
int internal_start_block;
|
||||
int internal_end_block;
|
||||
int block_offset;
|
||||
int chunk_offset;
|
||||
|
||||
/* Runtime checkpointing stuff */
|
||||
int checkpt_page_seq; /* running sequence number of checkpt pages */
|
||||
int checkpt_byte_count;
|
||||
int checkpt_byte_offs;
|
||||
u8 *checkpt_buffer;
|
||||
int checkpt_open_write;
|
||||
int blocks_in_checkpt;
|
||||
int checkpt_cur_chunk;
|
||||
int checkpt_cur_block;
|
||||
int checkpt_next_block;
|
||||
int *checkpt_block_list;
|
||||
int checkpt_max_blocks;
|
||||
u32 checkpt_sum;
|
||||
u32 checkpt_xor;
|
||||
|
||||
int checkpoint_blocks_required; /* Number of blocks needed to store
|
||||
* current checkpoint set */
|
||||
|
||||
/* Block Info */
|
||||
struct yaffs_block_info *block_info;
|
||||
u8 *chunk_bits; /* bitmap of chunks in use */
|
||||
u8 block_info_alt:1; /* allocated using alternative alloc */
|
||||
u8 chunk_bits_alt:1; /* allocated using alternative alloc */
|
||||
int chunk_bit_stride; /* Number of bytes of chunk_bits per block.
|
||||
* Must be consistent with chunks_per_block.
|
||||
*/
|
||||
|
||||
int n_erased_blocks;
|
||||
int alloc_block; /* Current block being allocated off */
|
||||
u32 alloc_page;
|
||||
int alloc_block_finder; /* Used to search for next allocation block */
|
||||
|
||||
/* Object and Tnode memory management */
|
||||
void *allocator;
|
||||
int n_obj;
|
||||
int n_tnodes;
|
||||
|
||||
int n_hardlinks;
|
||||
|
||||
struct yaffs_obj_bucket obj_bucket[YAFFS_NOBJECT_BUCKETS];
|
||||
u32 bucket_finder;
|
||||
|
||||
int n_free_chunks;
|
||||
|
||||
/* Garbage collection control */
|
||||
u32 *gc_cleanup_list; /* objects to delete at the end of a GC. */
|
||||
u32 n_clean_ups;
|
||||
|
||||
unsigned has_pending_prioritised_gc; /* We think this device might
|
||||
have pending prioritised gcs */
|
||||
unsigned gc_disable;
|
||||
unsigned gc_block_finder;
|
||||
unsigned gc_dirtiest;
|
||||
unsigned gc_pages_in_use;
|
||||
unsigned gc_not_done;
|
||||
unsigned gc_block;
|
||||
unsigned gc_chunk;
|
||||
unsigned gc_skip;
|
||||
struct yaffs_summary_tags *gc_sum_tags;
|
||||
|
||||
/* Special directories */
|
||||
struct yaffs_obj *root_dir;
|
||||
struct yaffs_obj *lost_n_found;
|
||||
|
||||
int buffered_block; /* Which block is buffered here? */
|
||||
int doing_buffered_block_rewrite;
|
||||
|
||||
struct yaffs_cache *cache;
|
||||
int cache_last_use;
|
||||
|
||||
/* Stuff for background deletion and unlinked files. */
|
||||
struct yaffs_obj *unlinked_dir; /* Directory where unlinked and deleted
|
||||
files live. */
|
||||
struct yaffs_obj *del_dir; /* Directory where deleted objects are
|
||||
sent to disappear. */
|
||||
struct yaffs_obj *unlinked_deletion; /* Current file being
|
||||
background deleted. */
|
||||
int n_deleted_files; /* Count of files awaiting deletion; */
|
||||
int n_unlinked_files; /* Count of unlinked files. */
|
||||
int n_bg_deletions; /* Count of background deletions. */
|
||||
|
||||
/* Temporary buffer management */
|
||||
struct yaffs_buffer temp_buffer[YAFFS_N_TEMP_BUFFERS];
|
||||
int max_temp;
|
||||
int temp_in_use;
|
||||
int unmanaged_buffer_allocs;
|
||||
int unmanaged_buffer_deallocs;
|
||||
|
||||
/* yaffs2 runtime stuff */
|
||||
unsigned seq_number; /* Sequence number of currently
|
||||
allocating block */
|
||||
unsigned oldest_dirty_seq;
|
||||
unsigned oldest_dirty_block;
|
||||
|
||||
/* Block refreshing */
|
||||
int refresh_skip; /* A skip down counter.
|
||||
* Refresh happens when this gets to zero. */
|
||||
|
||||
/* Dirty directory handling */
|
||||
struct list_head dirty_dirs; /* List of dirty directories */
|
||||
|
||||
/* Summary */
|
||||
int chunks_per_summary;
|
||||
struct yaffs_summary_tags *sum_tags;
|
||||
|
||||
/* Statistics */
|
||||
u32 n_page_writes;
|
||||
u32 n_page_reads;
|
||||
u32 n_erasures;
|
||||
u32 n_bad_queries;
|
||||
u32 n_bad_markings;
|
||||
u32 n_erase_failures;
|
||||
u32 n_gc_copies;
|
||||
u32 all_gcs;
|
||||
u32 passive_gc_count;
|
||||
u32 oldest_dirty_gc_count;
|
||||
u32 n_gc_blocks;
|
||||
u32 bg_gcs;
|
||||
u32 n_retried_writes;
|
||||
u32 n_retired_blocks;
|
||||
u32 n_ecc_fixed;
|
||||
u32 n_ecc_unfixed;
|
||||
u32 n_tags_ecc_fixed;
|
||||
u32 n_tags_ecc_unfixed;
|
||||
u32 n_deletions;
|
||||
u32 n_unmarked_deletions;
|
||||
u32 refresh_count;
|
||||
u32 cache_hits;
|
||||
u32 tags_used;
|
||||
u32 summary_used;
|
||||
|
||||
};
|
||||
|
||||
/* The CheckpointDevice structure holds the device information that changes
|
||||
*at runtime and must be preserved over unmount/mount cycles.
|
||||
*/
|
||||
struct yaffs_checkpt_dev {
|
||||
int struct_type;
|
||||
int n_erased_blocks;
|
||||
int alloc_block; /* Current block being allocated off */
|
||||
u32 alloc_page;
|
||||
int n_free_chunks;
|
||||
|
||||
int n_deleted_files; /* Count of files awaiting deletion; */
|
||||
int n_unlinked_files; /* Count of unlinked files. */
|
||||
int n_bg_deletions; /* Count of background deletions. */
|
||||
|
||||
/* yaffs2 runtime stuff */
|
||||
unsigned seq_number; /* Sequence number of currently
|
||||
* allocating block */
|
||||
|
||||
};
|
||||
|
||||
struct yaffs_checkpt_validity {
|
||||
int struct_type;
|
||||
u32 magic;
|
||||
u32 version;
|
||||
u32 head;
|
||||
};
|
||||
|
||||
struct yaffs_shadow_fixer {
|
||||
int obj_id;
|
||||
int shadowed_id;
|
||||
struct yaffs_shadow_fixer *next;
|
||||
};
|
||||
|
||||
/* Structure for doing xattr modifications */
|
||||
struct yaffs_xattr_mod {
|
||||
int set; /* If 0 then this is a deletion */
|
||||
const YCHAR *name;
|
||||
const void *data;
|
||||
int size;
|
||||
int flags;
|
||||
int result;
|
||||
};
|
||||
|
||||
/*----------------------- YAFFS Functions -----------------------*/
|
||||
|
||||
int yaffs_guts_initialise(struct yaffs_dev *dev);
|
||||
void yaffs_deinitialise(struct yaffs_dev *dev);
|
||||
|
||||
int yaffs_get_n_free_chunks(struct yaffs_dev *dev);
|
||||
|
||||
int yaffs_rename_obj(struct yaffs_obj *old_dir, const YCHAR * old_name,
|
||||
struct yaffs_obj *new_dir, const YCHAR * new_name);
|
||||
|
||||
int yaffs_unlinker(struct yaffs_obj *dir, const YCHAR * name);
|
||||
int yaffs_del_obj(struct yaffs_obj *obj);
|
||||
struct yaffs_obj *yaffs_retype_obj(struct yaffs_obj *obj,
|
||||
enum yaffs_obj_type type);
|
||||
|
||||
|
||||
int yaffs_get_obj_name(struct yaffs_obj *obj, YCHAR * name, int buffer_size);
|
||||
Y_LOFF_T yaffs_get_obj_length(struct yaffs_obj *obj);
|
||||
int yaffs_get_obj_inode(struct yaffs_obj *obj);
|
||||
unsigned yaffs_get_obj_type(struct yaffs_obj *obj);
|
||||
int yaffs_get_obj_link_count(struct yaffs_obj *obj);
|
||||
|
||||
/* File operations */
|
||||
int yaffs_file_rd(struct yaffs_obj *obj, u8 * buffer, Y_LOFF_T offset,
|
||||
int n_bytes);
|
||||
int yaffs_wr_file(struct yaffs_obj *obj, const u8 * buffer, Y_LOFF_T offset,
|
||||
int n_bytes, int write_trhrough);
|
||||
int yaffs_resize_file(struct yaffs_obj *obj, Y_LOFF_T new_size);
|
||||
|
||||
struct yaffs_obj *yaffs_create_file(struct yaffs_obj *parent,
|
||||
const YCHAR *name, u32 mode, u32 uid,
|
||||
u32 gid);
|
||||
|
||||
int yaffs_flush_file(struct yaffs_obj *in,
|
||||
int update_time,
|
||||
int data_sync,
|
||||
int discard_cache);
|
||||
|
||||
/* Flushing and checkpointing */
|
||||
void yaffs_flush_whole_cache(struct yaffs_dev *dev, int discard);
|
||||
|
||||
int yaffs_checkpoint_save(struct yaffs_dev *dev);
|
||||
int yaffs_checkpoint_restore(struct yaffs_dev *dev);
|
||||
|
||||
/* Directory operations */
|
||||
struct yaffs_obj *yaffs_create_dir(struct yaffs_obj *parent, const YCHAR *name,
|
||||
u32 mode, u32 uid, u32 gid);
|
||||
struct yaffs_obj *yaffs_find_by_name(struct yaffs_obj *the_dir,
|
||||
const YCHAR *name);
|
||||
struct yaffs_obj *yaffs_find_by_number(struct yaffs_dev *dev, u32 number);
|
||||
|
||||
/* Link operations */
|
||||
struct yaffs_obj *yaffs_link_obj(struct yaffs_obj *parent, const YCHAR *name,
|
||||
struct yaffs_obj *equiv_obj);
|
||||
|
||||
struct yaffs_obj *yaffs_get_equivalent_obj(struct yaffs_obj *obj);
|
||||
|
||||
/* Symlink operations */
|
||||
struct yaffs_obj *yaffs_create_symlink(struct yaffs_obj *parent,
|
||||
const YCHAR *name, u32 mode, u32 uid,
|
||||
u32 gid, const YCHAR *alias);
|
||||
YCHAR *yaffs_get_symlink_alias(struct yaffs_obj *obj);
|
||||
|
||||
/* Special inodes (fifos, sockets and devices) */
|
||||
struct yaffs_obj *yaffs_create_special(struct yaffs_obj *parent,
|
||||
const YCHAR *name, u32 mode, u32 uid,
|
||||
u32 gid, u32 rdev);
|
||||
|
||||
int yaffs_set_xattrib(struct yaffs_obj *obj, const YCHAR *name,
|
||||
const void *value, int size, int flags);
|
||||
int yaffs_get_xattrib(struct yaffs_obj *obj, const YCHAR *name, void *value,
|
||||
int size);
|
||||
int yaffs_list_xattrib(struct yaffs_obj *obj, char *buffer, int size);
|
||||
int yaffs_remove_xattrib(struct yaffs_obj *obj, const YCHAR *name);
|
||||
|
||||
/* Special directories */
|
||||
struct yaffs_obj *yaffs_root(struct yaffs_dev *dev);
|
||||
struct yaffs_obj *yaffs_lost_n_found(struct yaffs_dev *dev);
|
||||
|
||||
void yaffs_handle_defered_free(struct yaffs_obj *obj);
|
||||
|
||||
void yaffs_update_dirty_dirs(struct yaffs_dev *dev);
|
||||
|
||||
int yaffs_bg_gc(struct yaffs_dev *dev, unsigned urgency);
|
||||
|
||||
/* Debug dump */
|
||||
int yaffs_dump_obj(struct yaffs_obj *obj);
|
||||
|
||||
void yaffs_guts_test(struct yaffs_dev *dev);
|
||||
int yaffs_guts_ll_init(struct yaffs_dev *dev);
|
||||
|
||||
|
||||
/* A few useful functions to be used within the core files*/
|
||||
void yaffs_chunk_del(struct yaffs_dev *dev, int chunk_id, int mark_flash,
|
||||
int lyn);
|
||||
int yaffs_check_ff(u8 *buffer, int n_bytes);
|
||||
void yaffs_handle_chunk_error(struct yaffs_dev *dev,
|
||||
struct yaffs_block_info *bi);
|
||||
|
||||
u8 *yaffs_get_temp_buffer(struct yaffs_dev *dev);
|
||||
void yaffs_release_temp_buffer(struct yaffs_dev *dev, u8 *buffer);
|
||||
|
||||
struct yaffs_obj *yaffs_find_or_create_by_number(struct yaffs_dev *dev,
|
||||
int number,
|
||||
enum yaffs_obj_type type);
|
||||
int yaffs_put_chunk_in_file(struct yaffs_obj *in, int inode_chunk,
|
||||
int nand_chunk, int in_scan);
|
||||
void yaffs_set_obj_name(struct yaffs_obj *obj, const YCHAR *name);
|
||||
void yaffs_set_obj_name_from_oh(struct yaffs_obj *obj,
|
||||
const struct yaffs_obj_hdr *oh);
|
||||
void yaffs_add_obj_to_dir(struct yaffs_obj *directory, struct yaffs_obj *obj);
|
||||
YCHAR *yaffs_clone_str(const YCHAR *str);
|
||||
void yaffs_link_fixup(struct yaffs_dev *dev, struct list_head *hard_list);
|
||||
void yaffs_block_became_dirty(struct yaffs_dev *dev, int block_no);
|
||||
int yaffs_update_oh(struct yaffs_obj *in, const YCHAR *name,
|
||||
int force, int is_shrink, int shadows,
|
||||
struct yaffs_xattr_mod *xop);
|
||||
void yaffs_handle_shadowed_obj(struct yaffs_dev *dev, int obj_id,
|
||||
int backward_scanning);
|
||||
int yaffs_check_alloc_available(struct yaffs_dev *dev, int n_chunks);
|
||||
struct yaffs_tnode *yaffs_get_tnode(struct yaffs_dev *dev);
|
||||
struct yaffs_tnode *yaffs_add_find_tnode_0(struct yaffs_dev *dev,
|
||||
struct yaffs_file_var *file_struct,
|
||||
u32 chunk_id,
|
||||
struct yaffs_tnode *passed_tn);
|
||||
|
||||
int yaffs_do_file_wr(struct yaffs_obj *in, const u8 *buffer, Y_LOFF_T offset,
|
||||
int n_bytes, int write_trhrough);
|
||||
void yaffs_resize_file_down(struct yaffs_obj *obj, Y_LOFF_T new_size);
|
||||
void yaffs_skip_rest_of_block(struct yaffs_dev *dev);
|
||||
|
||||
int yaffs_count_free_chunks(struct yaffs_dev *dev);
|
||||
|
||||
struct yaffs_tnode *yaffs_find_tnode_0(struct yaffs_dev *dev,
|
||||
struct yaffs_file_var *file_struct,
|
||||
u32 chunk_id);
|
||||
|
||||
u32 yaffs_get_group_base(struct yaffs_dev *dev, struct yaffs_tnode *tn,
|
||||
unsigned pos);
|
||||
|
||||
int yaffs_is_non_empty_dir(struct yaffs_obj *obj);
|
||||
|
||||
int yaffs_guts_format_dev(struct yaffs_dev *dev);
|
||||
|
||||
void yaffs_addr_to_chunk(struct yaffs_dev *dev, Y_LOFF_T addr,
|
||||
int *chunk_out, u32 *offset_out);
|
||||
/*
|
||||
* Marshalling functions to get Y_LOFF_T file sizes into aand out of
|
||||
* object headers.
|
||||
*/
|
||||
void yaffs_oh_size_load(struct yaffs_obj_hdr *oh, Y_LOFF_T fsize);
|
||||
Y_LOFF_T yaffs_oh_to_size(struct yaffs_obj_hdr *oh);
|
||||
Y_LOFF_T yaffs_max_file_size(struct yaffs_dev *dev);
|
||||
|
||||
/*
|
||||
* Debug function to count number of blocks in each state
|
||||
* NB Needs to be called with correct number of integers
|
||||
*/
|
||||
|
||||
void yaffs_count_blocks_by_state(struct yaffs_dev *dev, int bs[10]);
|
||||
|
||||
int yaffs_find_chunk_in_file(struct yaffs_obj *in, int inode_chunk,
|
||||
struct yaffs_ext_tags *tags);
|
||||
|
||||
#endif
|
24
flight/pios/common/libraries/yaffs2/inc/yaffs_hweight.h
Normal file
24
flight/pios/common/libraries/yaffs2/inc/yaffs_hweight.h
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
#ifndef __YAFFS_HWEIGHT_H__
|
||||
#define __YAFFS_HWEIGHT_H__
|
||||
|
||||
#include "yportenv.h"
|
||||
|
||||
int yaffs_hweight8(u8 x);
|
||||
int yaffs_hweight32(u32 x);
|
||||
|
||||
#endif
|
126
flight/pios/common/libraries/yaffs2/inc/yaffs_list.h
Normal file
126
flight/pios/common/libraries/yaffs2/inc/yaffs_list.h
Normal file
@ -0,0 +1,126 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is just holds extra declarations of macros that would normally
|
||||
* be providesd in the Linux kernel. These macros have been written from
|
||||
* scratch but are functionally equivalent to the Linux ones.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __YAFFS_LIST_H__
|
||||
#define __YAFFS_LIST_H__
|
||||
|
||||
|
||||
/*
|
||||
* This is a simple doubly linked list implementation that matches the
|
||||
* way the Linux kernel doubly linked list implementation works.
|
||||
*/
|
||||
|
||||
struct list_head {
|
||||
struct list_head *next; /* next in chain */
|
||||
struct list_head *prev; /* previous in chain */
|
||||
};
|
||||
|
||||
|
||||
/* Initialise a static list */
|
||||
#define LIST_HEAD(name) \
|
||||
struct list_head name = { &(name), &(name)}
|
||||
|
||||
|
||||
|
||||
/* Initialise a list head to an empty list */
|
||||
#define INIT_LIST_HEAD(p) \
|
||||
do { \
|
||||
(p)->next = (p);\
|
||||
(p)->prev = (p); \
|
||||
} while (0)
|
||||
|
||||
|
||||
/* Add an element to a list */
|
||||
static inline void list_add(struct list_head *new_entry,
|
||||
struct list_head *list)
|
||||
{
|
||||
struct list_head *list_next = list->next;
|
||||
|
||||
list->next = new_entry;
|
||||
new_entry->prev = list;
|
||||
new_entry->next = list_next;
|
||||
list_next->prev = new_entry;
|
||||
|
||||
}
|
||||
|
||||
static inline void list_add_tail(struct list_head *new_entry,
|
||||
struct list_head *list)
|
||||
{
|
||||
struct list_head *list_prev = list->prev;
|
||||
|
||||
list->prev = new_entry;
|
||||
new_entry->next = list;
|
||||
new_entry->prev = list_prev;
|
||||
list_prev->next = new_entry;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Take an element out of its current list, with or without
|
||||
* reinitialising the links.of the entry*/
|
||||
static inline void list_del(struct list_head *entry)
|
||||
{
|
||||
struct list_head *list_next = entry->next;
|
||||
struct list_head *list_prev = entry->prev;
|
||||
|
||||
list_next->prev = list_prev;
|
||||
list_prev->next = list_next;
|
||||
|
||||
}
|
||||
|
||||
static inline void list_del_init(struct list_head *entry)
|
||||
{
|
||||
list_del(entry);
|
||||
entry->next = entry->prev = entry;
|
||||
}
|
||||
|
||||
|
||||
/* Test if the list is empty */
|
||||
static inline int list_empty(struct list_head *entry)
|
||||
{
|
||||
return (entry->next == entry);
|
||||
}
|
||||
|
||||
|
||||
/* list_entry takes a pointer to a list entry and offsets it to that
|
||||
* we can find a pointer to the object it is embedded in.
|
||||
*/
|
||||
|
||||
|
||||
#define list_entry(entry, type, member) \
|
||||
((type *)((char *)(entry)-(unsigned long)(&((type *)NULL)->member)))
|
||||
|
||||
|
||||
/* list_for_each and list_for_each_safe iterate over lists.
|
||||
* list_for_each_safe uses temporary storage to make the list delete safe
|
||||
*/
|
||||
|
||||
#define list_for_each(itervar, list) \
|
||||
for (itervar = (list)->next; itervar != (list); itervar = itervar->next)
|
||||
|
||||
#define list_for_each_safe(itervar, save_var, list) \
|
||||
for (itervar = (list)->next, save_var = (list)->next->next; \
|
||||
itervar != (list); \
|
||||
itervar = save_var, save_var = save_var->next)
|
||||
|
||||
|
||||
#endif
|
28
flight/pios/common/libraries/yaffs2/inc/yaffs_nameval.h
Normal file
28
flight/pios/common/libraries/yaffs2/inc/yaffs_nameval.h
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
#ifndef __NAMEVAL_H__
|
||||
#define __NAMEVAL_H__
|
||||
|
||||
#include "yportenv.h"
|
||||
|
||||
int nval_del(char *xb, int xb_size, const YCHAR * name);
|
||||
int nval_set(char *xb, int xb_size, const YCHAR * name, const char *buf,
|
||||
int bsize, int flags);
|
||||
int nval_get(const char *xb, int xb_size, const YCHAR * name, char *buf,
|
||||
int bsize);
|
||||
int nval_list(const char *xb, int xb_size, char *buf, int bsize);
|
||||
int nval_hasvalues(const char *xb, int xb_size);
|
||||
#endif
|
39
flight/pios/common/libraries/yaffs2/inc/yaffs_nand.h
Normal file
39
flight/pios/common/libraries/yaffs2/inc/yaffs_nand.h
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
#ifndef __YAFFS_NAND_H__
|
||||
#define __YAFFS_NAND_H__
|
||||
#include "yaffs_guts.h"
|
||||
|
||||
int yaffs_rd_chunk_tags_nand(struct yaffs_dev *dev, int nand_chunk,
|
||||
u8 *buffer, struct yaffs_ext_tags *tags);
|
||||
|
||||
int yaffs_wr_chunk_tags_nand(struct yaffs_dev *dev,
|
||||
int nand_chunk,
|
||||
const u8 *buffer, struct yaffs_ext_tags *tags);
|
||||
|
||||
int yaffs_mark_bad(struct yaffs_dev *dev, int block_no);
|
||||
|
||||
int yaffs_query_init_block_state(struct yaffs_dev *dev,
|
||||
int block_no,
|
||||
enum yaffs_block_state *state,
|
||||
unsigned *seq_number);
|
||||
|
||||
int yaffs_erase_block(struct yaffs_dev *dev, int flash_block);
|
||||
|
||||
int yaffs_init_nand(struct yaffs_dev *dev);
|
||||
int yaffs_deinit_nand(struct yaffs_dev *dev);
|
||||
|
||||
#endif
|
44
flight/pios/common/libraries/yaffs2/inc/yaffs_osglue.h
Normal file
44
flight/pios/common/libraries/yaffs2/inc/yaffs_osglue.h
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Header file for using yaffs in an application via
|
||||
* a direct interface.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __YAFFS_OSGLUE_H__
|
||||
#define __YAFFS_OSGLUE_H__
|
||||
|
||||
|
||||
#include "yportenv.h"
|
||||
|
||||
void yaffsfs_Lock(void);
|
||||
void yaffsfs_Unlock(void);
|
||||
|
||||
u32 yaffsfs_CurrentTime(void);
|
||||
|
||||
void yaffsfs_SetError(int err);
|
||||
|
||||
void *yaffsfs_malloc(size_t size);
|
||||
void yaffsfs_free(void *ptr);
|
||||
|
||||
int yaffsfs_CheckMemRegion(const void *addr, size_t size, int write_request);
|
||||
|
||||
void yaffsfs_OSInitialisation(void);
|
||||
|
||||
|
||||
#endif
|
||||
|
47
flight/pios/common/libraries/yaffs2/inc/yaffs_packedtags2.h
Normal file
47
flight/pios/common/libraries/yaffs2/inc/yaffs_packedtags2.h
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
/* This is used to pack YAFFS2 tags, not YAFFS1tags. */
|
||||
|
||||
#ifndef __YAFFS_PACKEDTAGS2_H__
|
||||
#define __YAFFS_PACKEDTAGS2_H__
|
||||
|
||||
#include "yaffs_guts.h"
|
||||
#include "yaffs_ecc.h"
|
||||
|
||||
struct yaffs_packed_tags2_tags_only {
|
||||
unsigned seq_number;
|
||||
unsigned obj_id;
|
||||
unsigned chunk_id;
|
||||
unsigned n_bytes;
|
||||
};
|
||||
|
||||
struct yaffs_packed_tags2 {
|
||||
struct yaffs_packed_tags2_tags_only t;
|
||||
struct yaffs_ecc_other ecc;
|
||||
};
|
||||
|
||||
/* Full packed tags with ECC, used for oob tags */
|
||||
void yaffs_pack_tags2(struct yaffs_packed_tags2 *pt,
|
||||
const struct yaffs_ext_tags *t, int tags_ecc);
|
||||
void yaffs_unpack_tags2(struct yaffs_ext_tags *t, struct yaffs_packed_tags2 *pt,
|
||||
int tags_ecc);
|
||||
|
||||
/* Only the tags part (no ECC for use with inband tags */
|
||||
void yaffs_pack_tags2_tags_only(struct yaffs_packed_tags2_tags_only *pt,
|
||||
const struct yaffs_ext_tags *t);
|
||||
void yaffs_unpack_tags2_tags_only(struct yaffs_ext_tags *t,
|
||||
struct yaffs_packed_tags2_tags_only *pt);
|
||||
#endif
|
37
flight/pios/common/libraries/yaffs2/inc/yaffs_summary.h
Normal file
37
flight/pios/common/libraries/yaffs2/inc/yaffs_summary.h
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
#ifndef __YAFFS_SUMMARY_H__
|
||||
#define __YAFFS_SUMMARY_H__
|
||||
|
||||
#include "yaffs_packedtags2.h"
|
||||
|
||||
|
||||
int yaffs_summary_init(struct yaffs_dev *dev);
|
||||
void yaffs_summary_deinit(struct yaffs_dev *dev);
|
||||
|
||||
int yaffs_summary_add(struct yaffs_dev *dev,
|
||||
struct yaffs_ext_tags *tags,
|
||||
int chunk_in_block);
|
||||
int yaffs_summary_fetch(struct yaffs_dev *dev,
|
||||
struct yaffs_ext_tags *tags,
|
||||
int chunk_in_block);
|
||||
int yaffs_summary_read(struct yaffs_dev *dev,
|
||||
struct yaffs_summary_tags *st,
|
||||
int blk);
|
||||
void yaffs_summary_gc(struct yaffs_dev *dev, int blk);
|
||||
|
||||
|
||||
#endif
|
44
flight/pios/common/libraries/yaffs2/inc/yaffs_tagscompat.h
Normal file
44
flight/pios/common/libraries/yaffs2/inc/yaffs_tagscompat.h
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
#ifndef __YAFFS_TAGSCOMPAT_H__
|
||||
#define __YAFFS_TAGSCOMPAT_H__
|
||||
|
||||
|
||||
#include "yaffs_guts.h"
|
||||
|
||||
#if 0
|
||||
|
||||
|
||||
int yaffs_tags_compat_wr(struct yaffs_dev *dev,
|
||||
int nand_chunk,
|
||||
const u8 *data, const struct yaffs_ext_tags *tags);
|
||||
int yaffs_tags_compat_rd(struct yaffs_dev *dev,
|
||||
int nand_chunk,
|
||||
u8 *data, struct yaffs_ext_tags *tags);
|
||||
int yaffs_tags_compat_mark_bad(struct yaffs_dev *dev, int block_no);
|
||||
int yaffs_tags_compat_query_block(struct yaffs_dev *dev,
|
||||
int block_no,
|
||||
enum yaffs_block_state *state,
|
||||
u32 *seq_number);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
void yaffs_tags_compat_install(struct yaffs_dev *dev);
|
||||
void yaffs_calc_tags_ecc(struct yaffs_tags *tags);
|
||||
int yaffs_check_tags_ecc(struct yaffs_tags *tags);
|
||||
|
||||
#endif
|
22
flight/pios/common/libraries/yaffs2/inc/yaffs_tagsmarshall.h
Normal file
22
flight/pios/common/libraries/yaffs2/inc/yaffs_tagsmarshall.h
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
#ifndef __YAFFS_TAGSMARSHALL_H__
|
||||
#define __YAFFS_TAGSMARSHALL_H__
|
||||
|
||||
#include "yaffs_guts.h"
|
||||
void yaffs_tags_marshall_install(struct yaffs_dev *dev);
|
||||
|
||||
#endif
|
57
flight/pios/common/libraries/yaffs2/inc/yaffs_trace.h
Normal file
57
flight/pios/common/libraries/yaffs2/inc/yaffs_trace.h
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
#ifndef __YTRACE_H__
|
||||
#define __YTRACE_H__
|
||||
|
||||
extern unsigned int yaffs_trace_mask;
|
||||
extern unsigned int yaffs_wr_attempts;
|
||||
|
||||
/*
|
||||
* Tracing flags.
|
||||
* The flags masked in YAFFS_TRACE_ALWAYS are always traced.
|
||||
*/
|
||||
|
||||
#define YAFFS_TRACE_OS 0x00000002
|
||||
#define YAFFS_TRACE_ALLOCATE 0x00000004
|
||||
#define YAFFS_TRACE_SCAN 0x00000008
|
||||
#define YAFFS_TRACE_BAD_BLOCKS 0x00000010
|
||||
#define YAFFS_TRACE_ERASE 0x00000020
|
||||
#define YAFFS_TRACE_GC 0x00000040
|
||||
#define YAFFS_TRACE_WRITE 0x00000080
|
||||
#define YAFFS_TRACE_TRACING 0x00000100
|
||||
#define YAFFS_TRACE_DELETION 0x00000200
|
||||
#define YAFFS_TRACE_BUFFERS 0x00000400
|
||||
#define YAFFS_TRACE_NANDACCESS 0x00000800
|
||||
#define YAFFS_TRACE_GC_DETAIL 0x00001000
|
||||
#define YAFFS_TRACE_SCAN_DEBUG 0x00002000
|
||||
#define YAFFS_TRACE_MTD 0x00004000
|
||||
#define YAFFS_TRACE_CHECKPOINT 0x00008000
|
||||
|
||||
#define YAFFS_TRACE_VERIFY 0x00010000
|
||||
#define YAFFS_TRACE_VERIFY_NAND 0x00020000
|
||||
#define YAFFS_TRACE_VERIFY_FULL 0x00040000
|
||||
#define YAFFS_TRACE_VERIFY_ALL 0x000f0000
|
||||
|
||||
#define YAFFS_TRACE_SYNC 0x00100000
|
||||
#define YAFFS_TRACE_BACKGROUND 0x00200000
|
||||
#define YAFFS_TRACE_LOCK 0x00400000
|
||||
#define YAFFS_TRACE_MOUNT 0x00800000
|
||||
|
||||
#define YAFFS_TRACE_ERROR 0x40000000
|
||||
#define YAFFS_TRACE_BUG 0x80000000
|
||||
#define YAFFS_TRACE_ALWAYS 0xf0000000
|
||||
|
||||
#endif
|
43
flight/pios/common/libraries/yaffs2/inc/yaffs_verify.h
Normal file
43
flight/pios/common/libraries/yaffs2/inc/yaffs_verify.h
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
#ifndef __YAFFS_VERIFY_H__
|
||||
#define __YAFFS_VERIFY_H__
|
||||
|
||||
#include "yaffs_guts.h"
|
||||
|
||||
void yaffs_verify_blk(struct yaffs_dev *dev, struct yaffs_block_info *bi,
|
||||
int n);
|
||||
void yaffs_verify_collected_blk(struct yaffs_dev *dev,
|
||||
struct yaffs_block_info *bi, int n);
|
||||
void yaffs_verify_blocks(struct yaffs_dev *dev);
|
||||
|
||||
void yaffs_verify_oh(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh,
|
||||
struct yaffs_ext_tags *tags, int parent_check);
|
||||
void yaffs_verify_file(struct yaffs_obj *obj);
|
||||
void yaffs_verify_link(struct yaffs_obj *obj);
|
||||
void yaffs_verify_symlink(struct yaffs_obj *obj);
|
||||
void yaffs_verify_special(struct yaffs_obj *obj);
|
||||
void yaffs_verify_obj(struct yaffs_obj *obj);
|
||||
void yaffs_verify_objects(struct yaffs_dev *dev);
|
||||
void yaffs_verify_obj_in_dir(struct yaffs_obj *obj);
|
||||
void yaffs_verify_dir(struct yaffs_obj *directory);
|
||||
void yaffs_verify_free_chunks(struct yaffs_dev *dev);
|
||||
|
||||
int yaffs_verify_file_sane(struct yaffs_obj *obj);
|
||||
|
||||
int yaffs_skip_verification(struct yaffs_dev *dev);
|
||||
|
||||
#endif
|
22
flight/pios/common/libraries/yaffs2/inc/yaffs_yaffs1.h
Normal file
22
flight/pios/common/libraries/yaffs2/inc/yaffs_yaffs1.h
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
#ifndef __YAFFS_YAFFS1_H__
|
||||
#define __YAFFS_YAFFS1_H__
|
||||
|
||||
#include "yaffs_guts.h"
|
||||
int yaffs1_scan(struct yaffs_dev *dev);
|
||||
|
||||
#endif
|
39
flight/pios/common/libraries/yaffs2/inc/yaffs_yaffs2.h
Normal file
39
flight/pios/common/libraries/yaffs2/inc/yaffs_yaffs2.h
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
#ifndef __YAFFS_YAFFS2_H__
|
||||
#define __YAFFS_YAFFS2_H__
|
||||
|
||||
#include "yaffs_guts.h"
|
||||
|
||||
void yaffs_calc_oldest_dirty_seq(struct yaffs_dev *dev);
|
||||
void yaffs2_find_oldest_dirty_seq(struct yaffs_dev *dev);
|
||||
void yaffs2_clear_oldest_dirty_seq(struct yaffs_dev *dev,
|
||||
struct yaffs_block_info *bi);
|
||||
void yaffs2_update_oldest_dirty_seq(struct yaffs_dev *dev, unsigned block_no,
|
||||
struct yaffs_block_info *bi);
|
||||
int yaffs_block_ok_for_gc(struct yaffs_dev *dev, struct yaffs_block_info *bi);
|
||||
u32 yaffs2_find_refresh_block(struct yaffs_dev *dev);
|
||||
int yaffs2_checkpt_required(struct yaffs_dev *dev);
|
||||
int yaffs_calc_checkpt_blocks_required(struct yaffs_dev *dev);
|
||||
|
||||
void yaffs2_checkpt_invalidate(struct yaffs_dev *dev);
|
||||
int yaffs2_checkpt_save(struct yaffs_dev *dev);
|
||||
int yaffs2_checkpt_restore(struct yaffs_dev *dev);
|
||||
|
||||
int yaffs2_handle_hole(struct yaffs_obj *obj, Y_LOFF_T new_size);
|
||||
int yaffs2_scan_backwards(struct yaffs_dev *dev);
|
||||
|
||||
#endif
|
40
flight/pios/common/libraries/yaffs2/inc/yaffscfg.h
Normal file
40
flight/pios/common/libraries/yaffs2/inc/yaffscfg.h
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Header file for using yaffs in an application via
|
||||
* a direct interface.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __YAFFSCFG_H__
|
||||
#define __YAFFSCFG_H__
|
||||
|
||||
|
||||
#include "yportenv.h"
|
||||
|
||||
#define YAFFSFS_N_HANDLES 100
|
||||
|
||||
#define YAFFSFS_N_DSC 20
|
||||
|
||||
|
||||
struct yaffsfs_DeviceConfiguration {
|
||||
const YCHAR *prefix;
|
||||
struct yaffs_dev *dev;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
334
flight/pios/common/libraries/yaffs2/inc/yaffsfs.h
Normal file
334
flight/pios/common/libraries/yaffs2/inc/yaffsfs.h
Normal file
@ -0,0 +1,334 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Header file for using yaffs in an application via
|
||||
* a direct interface.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __YAFFSFS_H__
|
||||
#define __YAFFSFS_H__
|
||||
|
||||
#include "yaffscfg.h"
|
||||
#include "yportenv.h"
|
||||
|
||||
|
||||
#ifndef NAME_MAX
|
||||
#define NAME_MAX 256
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_YAFFS_OP
|
||||
#define YAFFS_MAX_FILE_SIZE (Y_LOFF_T)16000000
|
||||
#else
|
||||
#define YAFFS_MAX_FILE_SIZE \
|
||||
( (sizeof(Y_LOFF_T) < 8) ? YAFFS_MAX_FILE_SIZE_32 : (0x800000000LL - 1) )
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
|
||||
struct yaffs_dirent {
|
||||
long d_ino; /* inode number */
|
||||
off_t d_off; /* offset to this dirent */
|
||||
unsigned short d_reclen; /* length of this dirent */
|
||||
YUCHAR d_type; /* type of this record */
|
||||
YCHAR d_name[NAME_MAX+1]; /* file name (null-terminated) */
|
||||
unsigned d_dont_use; /* debug: not for public consumption */
|
||||
};
|
||||
|
||||
typedef struct opaque_structure yaffs_DIR;
|
||||
|
||||
|
||||
|
||||
struct yaffs_stat {
|
||||
int st_dev; /* device */
|
||||
int st_ino; /* inode */
|
||||
unsigned st_mode; /* protection */
|
||||
int st_nlink; /* number of hard links */
|
||||
int st_uid; /* user ID of owner */
|
||||
int st_gid; /* group ID of owner */
|
||||
unsigned st_rdev; /* device type (if inode device) */
|
||||
Y_LOFF_T st_size; /* total size, in bytes */
|
||||
unsigned long st_blksize; /* blocksize for filesystem I/O */
|
||||
unsigned long st_blocks; /* number of blocks allocated */
|
||||
#ifdef CONFIG_YAFFS_WINCE
|
||||
/* Special 64-bit times for WinCE */
|
||||
unsigned long yst_wince_atime[2];
|
||||
unsigned long yst_wince_mtime[2];
|
||||
unsigned long yst_wince_ctime[2];
|
||||
#else
|
||||
unsigned long yst_atime; /* time of last access */
|
||||
unsigned long yst_mtime; /* time of last modification */
|
||||
unsigned long yst_ctime; /* time of last change */
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
struct yaffs_utimbuf {
|
||||
unsigned long actime;
|
||||
unsigned long modtime;
|
||||
};
|
||||
|
||||
/* Normal POSIX-style API functions */
|
||||
|
||||
int yaffs_open(const YCHAR *path, int oflag, int mode) ;
|
||||
|
||||
int yaffs_close(int fd) ;
|
||||
int yaffs_fsync(int fd) ;
|
||||
int yaffs_fdatasync(int fd) ;
|
||||
int yaffs_flush(int fd) ; /* same as yaffs_fsync() */
|
||||
|
||||
int yaffs_access(const YCHAR *path, int amode);
|
||||
|
||||
int yaffs_dup(int fd);
|
||||
|
||||
int yaffs_read(int fd, void *buf, unsigned int nbyte) ;
|
||||
int yaffs_write(int fd, const void *buf, unsigned int nbyte) ;
|
||||
|
||||
int yaffs_pread(int fd, void *buf, unsigned int nbyte, Y_LOFF_T offset);
|
||||
int yaffs_pwrite(int fd, const void *buf, unsigned int nbyte, Y_LOFF_T offset);
|
||||
|
||||
Y_LOFF_T yaffs_lseek(int fd, Y_LOFF_T offset, int whence) ;
|
||||
|
||||
int yaffs_truncate(const YCHAR *path, Y_LOFF_T new_size);
|
||||
int yaffs_ftruncate(int fd, Y_LOFF_T new_size);
|
||||
|
||||
int yaffs_unlink(const YCHAR *path) ;
|
||||
int yaffs_rename(const YCHAR *oldPath, const YCHAR *newPath) ;
|
||||
|
||||
int yaffs_stat(const YCHAR *path, struct yaffs_stat *buf) ;
|
||||
int yaffs_lstat(const YCHAR *path, struct yaffs_stat *buf) ;
|
||||
int yaffs_fstat(int fd, struct yaffs_stat *buf) ;
|
||||
|
||||
int yaffs_utime(const YCHAR *path, const struct yaffs_utimbuf *buf);
|
||||
int yaffs_futime(int fd, const struct yaffs_utimbuf *buf);
|
||||
|
||||
|
||||
int yaffs_setxattr(const char *path, const char *name,
|
||||
const void *data, int size, int flags);
|
||||
int yaffs_lsetxattr(const char *path, const char *name,
|
||||
const void *data, int size, int flags);
|
||||
int yaffs_fsetxattr(int fd, const char *name,
|
||||
const void *data, int size, int flags);
|
||||
|
||||
int yaffs_getxattr(const char *path, const char *name,
|
||||
void *data, int size);
|
||||
int yaffs_lgetxattr(const char *path, const char *name,
|
||||
void *data, int size);
|
||||
int yaffs_fgetxattr(int fd, const char *name,
|
||||
void *data, int size);
|
||||
|
||||
int yaffs_removexattr(const char *path, const char *name);
|
||||
int yaffs_lremovexattr(const char *path, const char *name);
|
||||
int yaffs_fremovexattr(int fd, const char *name);
|
||||
|
||||
int yaffs_listxattr(const char *path, char *list, int size);
|
||||
int yaffs_llistxattr(const char *path, char *list, int size);
|
||||
int yaffs_flistxattr(int fd, char *list, int size);
|
||||
|
||||
int yaffs_chmod(const YCHAR *path, mode_t mode);
|
||||
int yaffs_fchmod(int fd, mode_t mode);
|
||||
|
||||
int yaffs_mkdir(const YCHAR *path, mode_t mode) ;
|
||||
int yaffs_rmdir(const YCHAR *path) ;
|
||||
|
||||
yaffs_DIR *yaffs_opendir(const YCHAR *dirname) ;
|
||||
struct yaffs_dirent *yaffs_readdir(yaffs_DIR *dirp) ;
|
||||
void yaffs_rewinddir(yaffs_DIR *dirp) ;
|
||||
int yaffs_closedir(yaffs_DIR *dirp) ;
|
||||
|
||||
int yaffs_mount(const YCHAR *path) ;
|
||||
int yaffs_mount2(const YCHAR *path, int read_only);
|
||||
int yaffs_mount3(const YCHAR *path, int read_only, int skip_checkpt);
|
||||
|
||||
int yaffs_unmount(const YCHAR *path) ;
|
||||
int yaffs_unmount2(const YCHAR *path, int force);
|
||||
int yaffs_remount(const YCHAR *path, int force, int read_only);
|
||||
|
||||
int yaffs_format(const YCHAR *path,
|
||||
int unmount_flag,
|
||||
int force_unmount_flag,
|
||||
int remount_flag);
|
||||
|
||||
int yaffs_sync(const YCHAR *path) ;
|
||||
|
||||
int yaffs_symlink(const YCHAR *oldpath, const YCHAR *newpath);
|
||||
int yaffs_readlink(const YCHAR *path, YCHAR *buf, int bufsiz);
|
||||
|
||||
int yaffs_link(const YCHAR *oldpath, const YCHAR *newpath);
|
||||
int yaffs_mknod(const YCHAR *pathname, mode_t mode, dev_t dev);
|
||||
|
||||
Y_LOFF_T yaffs_freespace(const YCHAR *path);
|
||||
Y_LOFF_T yaffs_totalspace(const YCHAR *path);
|
||||
|
||||
/* Function variants that use a relative directory */
|
||||
struct yaffs_obj;
|
||||
int yaffs_open_sharing_reldir(struct yaffs_obj *reldir, const YCHAR *path, int oflag, int mode, int sharing);
|
||||
int yaffs_open_reldir(struct yaffs_obj *reldir,const YCHAR *path, int oflag, int mode);
|
||||
int yaffs_truncate_reldir(struct yaffs_obj *reldir, const YCHAR *path, Y_LOFF_T new_size);
|
||||
int yaffs_unlink_reldir(struct yaffs_obj *reldir, const YCHAR *path);
|
||||
int yaffs_rename_reldir(struct yaffs_obj *reldir,
|
||||
const YCHAR *oldPath, const YCHAR *newPath);
|
||||
int yaffs_stat_reldir(struct yaffs_obj *reldir, const YCHAR *path, struct yaffs_stat *buf);
|
||||
int yaffs_lstat_reldir(struct yaffs_obj *reldir, const YCHAR *path, struct yaffs_stat *buf);
|
||||
int yaffs_utime_reldir(struct yaffs_obj *reldir, const YCHAR *path, const struct yaffs_utimbuf *buf);
|
||||
int yaffs_setxattr_reldir(struct yaffs_obj *reldir, const YCHAR *path,
|
||||
const char *name, const void *data, int size, int flags);
|
||||
int yaffs_lsetxattr_reldir(struct yaffs_obj *reldir, const YCHAR *path,
|
||||
const char *name, const void *data, int size, int flags);
|
||||
int yaffs_getxattr_reldir(struct yaffs_obj *reldir, const YCHAR *path,
|
||||
const char *name, void *data, int size);
|
||||
int yaffs_lgetxattr_reldir(struct yaffs_obj *reldir, const YCHAR *path,
|
||||
const char *name, void *data, int size);
|
||||
int yaffs_listxattr_reldir(struct yaffs_obj *reldir, const YCHAR *path,
|
||||
char *data, int size);
|
||||
int yaffs_llistxattr_reldir(struct yaffs_obj *reldir, const YCHAR *path,
|
||||
char *data, int size);
|
||||
int yaffs_removexattr_reldir(struct yaffs_obj *reldir, const YCHAR *path,
|
||||
const char *name);
|
||||
int yaffs_lremovexattr_reldir(struct yaffs_obj *reldir, const YCHAR *path,
|
||||
const char *name);
|
||||
int yaffs_access_reldir(struct yaffs_obj *reldir, const YCHAR *path, int amode);
|
||||
int yaffs_chmod_reldir(struct yaffs_obj *reldir, const YCHAR *path, mode_t mode);
|
||||
int yaffs_mkdir_reldir(struct yaffs_obj *reldir, const YCHAR *path, mode_t mode);
|
||||
int yaffs_rmdir_reldir(struct yaffs_obj *reldir, const YCHAR *path);
|
||||
yaffs_DIR *yaffs_opendir_reldir(struct yaffs_obj *reldir, const YCHAR *dirname);
|
||||
int yaffs_symlink_reldir(struct yaffs_obj *reldir,
|
||||
const YCHAR *oldpath, const YCHAR *newpath);
|
||||
int yaffs_readlink_reldir(struct yaffs_obj *reldir,const YCHAR *path,
|
||||
YCHAR *buf, int bufsiz);
|
||||
int yaffs_link_reldir(struct yaffs_obj *reldir,
|
||||
const YCHAR *oldpath, const YCHAR *linkpath);
|
||||
int yaffs_mknod_reldir(struct yaffs_obj *reldir, const YCHAR *pathname,
|
||||
mode_t mode, dev_t dev);
|
||||
|
||||
/* Function variants that use a relative device */
|
||||
struct yaffs_dev;
|
||||
int yaffs_open_sharing_reldev(struct yaffs_dev *dev, const YCHAR *path, int oflag, int mode, int sharing);
|
||||
int yaffs_open_reldev(struct yaffs_dev *dev,const YCHAR *path, int oflag, int mode);
|
||||
int yaffs_truncate_reldev(struct yaffs_dev *dev, const YCHAR *path, Y_LOFF_T new_size);
|
||||
int yaffs_unlink_reldev(struct yaffs_dev *dev, const YCHAR *path);
|
||||
int yaffs_rename_reldev(struct yaffs_dev *dev,
|
||||
const YCHAR *oldPath, const YCHAR *newPath);
|
||||
int yaffs_stat_reldev(struct yaffs_dev *dev, const YCHAR *path, struct yaffs_stat *buf);
|
||||
int yaffs_lstat_reldev(struct yaffs_dev *dev, const YCHAR *path, struct yaffs_stat *buf);
|
||||
int yaffs_utime_reldev(struct yaffs_dev *dev, const YCHAR *path, const struct yaffs_utimbuf *buf);
|
||||
int yaffs_setxattr_reldev(struct yaffs_dev *dev, const YCHAR *path,
|
||||
const char *name, const void *data, int size, int flags);
|
||||
int yaffs_lsetxattr_reldev(struct yaffs_dev *dev, const YCHAR *path,
|
||||
const char *name, const void *data, int size, int flags);
|
||||
int yaffs_getxattr_reldev(struct yaffs_dev *dev, const YCHAR *path,
|
||||
const char *name, void *data, int size);
|
||||
int yaffs_lgetxattr_reldev(struct yaffs_dev *dev, const YCHAR *path,
|
||||
const char *name, void *data, int size);
|
||||
int yaffs_listxattr_reldev(struct yaffs_dev *dev, const YCHAR *path,
|
||||
char *data, int size);
|
||||
int yaffs_llistxattr_reldev(struct yaffs_dev *dev, const YCHAR *path,
|
||||
char *data, int size);
|
||||
int yaffs_removexattr_reldev(struct yaffs_dev *dev, const YCHAR *path,
|
||||
const char *name);
|
||||
int yaffs_lremovexattr_reldev(struct yaffs_dev *dev, const YCHAR *path,
|
||||
const char *name);
|
||||
int yaffs_access_reldev(struct yaffs_dev *dev, const YCHAR *path, int amode);
|
||||
int yaffs_chmod_reldev(struct yaffs_dev *dev, const YCHAR *path, mode_t mode);
|
||||
int yaffs_mkdir_reldev(struct yaffs_dev *dev, const YCHAR *path, mode_t mode);
|
||||
int yaffs_rmdir_reldev(struct yaffs_dev *dev, const YCHAR *path);
|
||||
yaffs_DIR *yaffs_opendir_reldev(struct yaffs_dev *dev, const YCHAR *dirname);
|
||||
int yaffs_symlink_reldev(struct yaffs_dev *dev,
|
||||
const YCHAR *oldpath, const YCHAR *newpath);
|
||||
int yaffs_readlink_reldev(struct yaffs_dev *dev, const YCHAR *path,
|
||||
YCHAR *buf, int bufsiz);
|
||||
int yaffs_link_reldev(struct yaffs_dev *dev,
|
||||
const YCHAR *oldpath, const YCHAR *linkpath);
|
||||
int yaffs_mknod_reldev(struct yaffs_dev *dev, const YCHAR *pathname,
|
||||
mode_t mode, dev_t dev_val);
|
||||
Y_LOFF_T yaffs_freespace_reldev(struct yaffs_dev *dev);
|
||||
Y_LOFF_T yaffs_totalspace_reldev(struct yaffs_dev *dev);
|
||||
|
||||
int yaffs_sync_reldev(struct yaffs_dev *dev);
|
||||
int yaffs_unmount_reldev(struct yaffs_dev *dev);
|
||||
int yaffs_unmount2_reldev(struct yaffs_dev *dev, int force);
|
||||
int yaffs_remount_reldev(struct yaffs_dev *dev, int force, int read_only);
|
||||
|
||||
|
||||
/* Some non-standard functions to use fds to access directories */
|
||||
struct yaffs_dirent *yaffs_readdir_fd(int fd);
|
||||
void yaffs_rewinddir_fd(int fd);
|
||||
|
||||
/* Non-standard functions to pump garbage collection. */
|
||||
int yaffs_do_background_gc(const YCHAR *path, int urgency);
|
||||
int yaffs_do_background_gc_reldev(struct yaffs_dev *dev, int urgency);
|
||||
|
||||
/* Non-standard functions to get usage info */
|
||||
int yaffs_inodecount(const YCHAR *path);
|
||||
|
||||
int yaffs_n_handles(const YCHAR *path);
|
||||
|
||||
int yaffs_n_handles_reldir(struct yaffs_obj *reldir, const YCHAR *path);
|
||||
int yaffs_dump_dev_reldir(struct yaffs_obj *reldir, const YCHAR *path);
|
||||
int yaffs_n_handles_reldev(struct yaffs_dev *dev, const YCHAR *path);
|
||||
int yaffs_dump_dev_reldev(struct yaffs_dev *dev, const YCHAR *path);
|
||||
|
||||
#ifdef CONFIG_YAFFS_WINCE
|
||||
int yaffs_set_wince_times(int fd,
|
||||
const unsigned *wctime,
|
||||
const unsigned *watime,
|
||||
const unsigned *wmtime);
|
||||
int yaffs_get_wince_times(int fd,
|
||||
unsigned *wctime,
|
||||
unsigned *watime,
|
||||
unsigned *wmtime);
|
||||
#endif
|
||||
|
||||
|
||||
#define YAFFS_SHARE_READ 1
|
||||
#define YAFFS_SHARE_WRITE 2
|
||||
int yaffs_open_sharing(const YCHAR *path, int oflag, int mode, int shareMode);
|
||||
|
||||
struct yaffs_dev;
|
||||
void yaffs_add_device(struct yaffs_dev *dev);
|
||||
|
||||
int yaffs_start_up(void);
|
||||
int yaffsfs_GetLastError(void);
|
||||
|
||||
/* Functions to iterate through devices. NB Use with extreme care! */
|
||||
void yaffs_dev_rewind(void);
|
||||
struct yaffs_dev *yaffs_next_dev(void);
|
||||
|
||||
/* Function to get the last error */
|
||||
int yaffs_get_error(void);
|
||||
const char *yaffs_error_to_str(int err);
|
||||
|
||||
/* Function only for debugging */
|
||||
void *yaffs_getdev(const YCHAR *path);
|
||||
int yaffs_dump_dev(const YCHAR *path);
|
||||
int yaffs_set_error(int error);
|
||||
|
||||
/* Trace control functions */
|
||||
unsigned yaffs_set_trace(unsigned tm);
|
||||
unsigned yaffs_get_trace(void);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
101
flight/pios/common/libraries/yaffs2/inc/ydirectenv.h
Normal file
101
flight/pios/common/libraries/yaffs2/inc/ydirectenv.h
Normal file
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
/*
|
||||
* ydirectenv.h: Environment wrappers for YAFFS direct.
|
||||
*/
|
||||
|
||||
#ifndef __YDIRECTENV_H__
|
||||
#define __YDIRECTENV_H__
|
||||
|
||||
#include "stdint.h"
|
||||
#include "stdlib.h"
|
||||
#include "stdio.h"
|
||||
#include "string.h"
|
||||
#include "yaffs_osglue.h"
|
||||
#include "yaffs_hweight.h"
|
||||
|
||||
void yaffs_bug_fn(const char *file_name, int line_no);
|
||||
|
||||
#define BUG() do { yaffs_bug_fn(__FILE__, __LINE__); } while (0)
|
||||
|
||||
|
||||
#define YCHAR char
|
||||
#define YUCHAR unsigned char
|
||||
#define _Y(x) x
|
||||
|
||||
#ifndef Y_LOFF_T
|
||||
#define Y_LOFF_T int32_t // note CONFIG_YAFFS_OP changes dependent on type for max file size
|
||||
#endif
|
||||
|
||||
#define yaffs_strcat(a, b) strcat(a, b)
|
||||
#define yaffs_strcpy(a, b) strcpy(a, b)
|
||||
#define yaffs_strncpy(a, b, c) strncpy(a, b, c)
|
||||
#define yaffs_strnlen(s, m) strnlen(s, m)
|
||||
#ifdef CONFIG_YAFFS_CASE_INSENSITIVE
|
||||
#define yaffs_strcmp(a, b) strcasecmp(a, b)
|
||||
#define yaffs_strncmp(a, b, c) strncasecmp(a, b, c)
|
||||
#else
|
||||
#define yaffs_strcmp(a, b) strcmp(a, b)
|
||||
#define yaffs_strncmp(a, b, c) strncmp(a, b, c)
|
||||
#endif
|
||||
|
||||
#define hweight8(x) yaffs_hweight8(x)
|
||||
#define hweight32(x) yaffs_hweight32(x)
|
||||
|
||||
void yaffs_qsort(void *aa, size_t n, size_t es,
|
||||
int (*cmp)(const void *, const void *));
|
||||
|
||||
#define sort(base, n, sz, cmp_fn, swp) yaffs_qsort(base, n, sz, cmp_fn)
|
||||
|
||||
#define YAFFS_PATH_DIVIDERS "/"
|
||||
|
||||
#ifdef NO_inline
|
||||
#define inline
|
||||
#else
|
||||
#define inline __inline__
|
||||
#endif
|
||||
|
||||
#define kmalloc(x, flags) yaffsfs_malloc(x)
|
||||
#define kfree(x) yaffsfs_free(x)
|
||||
#define vmalloc(x) yaffsfs_malloc(x)
|
||||
#define vfree(x) yaffsfs_free(x)
|
||||
|
||||
#define cond_resched() do {} while (0)
|
||||
|
||||
#define yaffs_trace(msk, fmt, ...) do { \
|
||||
if (yaffs_trace_mask & (msk)) \
|
||||
printf("yaffs: " fmt "\n", ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
|
||||
#define YAFFS_LOSTNFOUND_NAME "lost+found"
|
||||
#define YAFFS_LOSTNFOUND_PREFIX "obj"
|
||||
|
||||
#include "yaffscfg.h"
|
||||
|
||||
#define Y_CURRENT_TIME yaffsfs_CurrentTime()
|
||||
#define Y_TIME_CONVERT(x) x
|
||||
|
||||
#define YAFFS_ROOT_MODE 0666
|
||||
#define YAFFS_LOSTNFOUND_MODE 0666
|
||||
|
||||
#include "yaffs_list.h"
|
||||
|
||||
#include "yaffsfs.h"
|
||||
|
||||
#endif
|
||||
|
||||
|
324
flight/pios/common/libraries/yaffs2/inc/yportenv.h
Normal file
324
flight/pios/common/libraries/yaffs2/inc/yportenv.h
Normal file
@ -0,0 +1,324 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __YPORTENV_H__
|
||||
#define __YPORTENV_H__
|
||||
|
||||
|
||||
/* Definition of types */
|
||||
#ifdef CONFIG_YAFFS_DEFINES_TYPES
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
typedef signed int s32;
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef CONFIG_YAFFS_PROVIDE_DEFS
|
||||
/* File types */
|
||||
|
||||
|
||||
#define DT_UNKNOWN 0
|
||||
#define DT_FIFO 1
|
||||
#define DT_CHR 2
|
||||
#define DT_DIR 4
|
||||
#define DT_BLK 6
|
||||
#define DT_REG 8
|
||||
#define DT_LNK 10
|
||||
#define DT_SOCK 12
|
||||
#define DT_WHT 14
|
||||
|
||||
|
||||
/*
|
||||
* Attribute flags.
|
||||
* These are or-ed together to select what has been changed.
|
||||
*/
|
||||
#define ATTR_MODE 1
|
||||
#define ATTR_UID 2
|
||||
#define ATTR_GID 4
|
||||
#define ATTR_SIZE 8
|
||||
#define ATTR_ATIME 16
|
||||
#define ATTR_MTIME 32
|
||||
#define ATTR_CTIME 64
|
||||
|
||||
struct iattr {
|
||||
unsigned int ia_valid;
|
||||
unsigned ia_mode;
|
||||
unsigned ia_uid;
|
||||
unsigned ia_gid;
|
||||
unsigned ia_size;
|
||||
unsigned ia_atime;
|
||||
unsigned ia_mtime;
|
||||
unsigned ia_ctime;
|
||||
unsigned int ia_attr_flags;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#if defined CONFIG_YAFFS_WINCE
|
||||
|
||||
#include "ywinceenv.h"
|
||||
|
||||
|
||||
#elif defined CONFIG_YAFFS_DIRECT
|
||||
|
||||
/* Direct interface */
|
||||
#include "ydirectenv.h"
|
||||
|
||||
#elif defined CONFIG_YAFFS_UTIL
|
||||
|
||||
#include "yutilsenv.h"
|
||||
|
||||
#else
|
||||
/* Should have specified a configuration type */
|
||||
#error Unknown configuration
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_YAFFS_DIRECT) || defined(CONFIG_YAFFS_WINCE)
|
||||
|
||||
#ifdef CONFIG_YAFFSFS_PROVIDE_VALUES
|
||||
|
||||
#ifndef O_RDONLY
|
||||
#define O_RDONLY 00
|
||||
#endif
|
||||
|
||||
#ifndef O_WRONLY
|
||||
#define O_WRONLY 01
|
||||
#endif
|
||||
|
||||
#ifndef O_RDWR
|
||||
#define O_RDWR 02
|
||||
#endif
|
||||
|
||||
#ifndef O_CREAT
|
||||
#define O_CREAT 0100
|
||||
#endif
|
||||
|
||||
#ifndef O_EXCL
|
||||
#define O_EXCL 0200
|
||||
#endif
|
||||
|
||||
#ifndef O_TRUNC
|
||||
#define O_TRUNC 01000
|
||||
#endif
|
||||
|
||||
#ifndef O_APPEND
|
||||
#define O_APPEND 02000
|
||||
#endif
|
||||
|
||||
#ifndef SEEK_SET
|
||||
#define SEEK_SET 0
|
||||
#endif
|
||||
|
||||
#ifndef SEEK_CUR
|
||||
#define SEEK_CUR 1
|
||||
#endif
|
||||
|
||||
#ifndef SEEK_END
|
||||
#define SEEK_END 2
|
||||
#endif
|
||||
|
||||
#ifndef EBUSY
|
||||
#define EBUSY 16
|
||||
#endif
|
||||
|
||||
#ifndef ENODEV
|
||||
#define ENODEV 19
|
||||
#endif
|
||||
|
||||
#ifndef EINVAL
|
||||
#define EINVAL 22
|
||||
#endif
|
||||
|
||||
#ifndef ENFILE
|
||||
#define ENFILE 23
|
||||
#endif
|
||||
|
||||
#ifndef EBADF
|
||||
#define EBADF 9
|
||||
#endif
|
||||
|
||||
#ifndef EACCES
|
||||
#define EACCES 13
|
||||
#endif
|
||||
|
||||
#ifndef EXDEV
|
||||
#define EXDEV 18
|
||||
#endif
|
||||
|
||||
#ifndef ENOENT
|
||||
#define ENOENT 2
|
||||
#endif
|
||||
|
||||
#ifndef ENOSPC
|
||||
#define ENOSPC 28
|
||||
#endif
|
||||
|
||||
#ifndef EROFS
|
||||
#define EROFS 30
|
||||
#endif
|
||||
|
||||
#ifndef ERANGE
|
||||
#define ERANGE 34
|
||||
#endif
|
||||
|
||||
#ifndef ENODATA
|
||||
#define ENODATA 61
|
||||
#endif
|
||||
|
||||
#ifndef ENOTEMPTY
|
||||
#define ENOTEMPTY 39
|
||||
#endif
|
||||
|
||||
#ifndef ENAMETOOLONG
|
||||
#define ENAMETOOLONG 36
|
||||
#endif
|
||||
|
||||
#ifndef ENOMEM
|
||||
#define ENOMEM 12
|
||||
#endif
|
||||
|
||||
#ifndef EFAULT
|
||||
#define EFAULT 14
|
||||
#endif
|
||||
|
||||
#ifndef EEXIST
|
||||
#define EEXIST 17
|
||||
#endif
|
||||
|
||||
#ifndef ENOTDIR
|
||||
#define ENOTDIR 20
|
||||
#endif
|
||||
|
||||
#ifndef EISDIR
|
||||
#define EISDIR 21
|
||||
#endif
|
||||
|
||||
#ifndef ELOOP
|
||||
#define ELOOP 40
|
||||
#endif
|
||||
|
||||
|
||||
/* Mode flags */
|
||||
|
||||
#ifndef S_IFMT
|
||||
#define S_IFMT 0170000
|
||||
#endif
|
||||
|
||||
#ifndef S_IFSOCK
|
||||
#define S_IFSOCK 0140000
|
||||
#endif
|
||||
|
||||
#ifndef S_IFIFO
|
||||
#define S_IFIFO 0010000
|
||||
#endif
|
||||
|
||||
#ifndef S_IFCHR
|
||||
#define S_IFCHR 0020000
|
||||
#endif
|
||||
|
||||
#ifndef S_IFBLK
|
||||
#define S_IFBLK 0060000
|
||||
#endif
|
||||
|
||||
#ifndef S_IFLNK
|
||||
#define S_IFLNK 0120000
|
||||
#endif
|
||||
|
||||
#ifndef S_IFDIR
|
||||
#define S_IFDIR 0040000
|
||||
#endif
|
||||
|
||||
#ifndef S_IFREG
|
||||
#define S_IFREG 0100000
|
||||
#endif
|
||||
|
||||
#ifndef S_ISSOCK
|
||||
#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
|
||||
#endif
|
||||
#ifndef S_ISLNK
|
||||
#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
|
||||
#endif
|
||||
#ifndef S_ISDIR
|
||||
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
|
||||
#endif
|
||||
#ifndef S_ISREG
|
||||
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
|
||||
#endif
|
||||
#ifndef S_ISBLK
|
||||
#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
|
||||
#endif
|
||||
#ifndef S_ISCHR
|
||||
#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
|
||||
#endif
|
||||
#ifndef S_ISFIFO
|
||||
#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifndef S_IREAD
|
||||
#define S_IREAD 0000400
|
||||
#endif
|
||||
|
||||
#ifndef S_IWRITE
|
||||
#define S_IWRITE 0000200
|
||||
#endif
|
||||
|
||||
#ifndef S_IEXEC
|
||||
#define S_IEXEC 0000100
|
||||
#endif
|
||||
|
||||
#ifndef XATTR_CREATE
|
||||
#define XATTR_CREATE 1
|
||||
#endif
|
||||
|
||||
#ifndef XATTR_REPLACE
|
||||
#define XATTR_REPLACE 2
|
||||
#endif
|
||||
|
||||
#ifndef R_OK
|
||||
#define R_OK 4
|
||||
#define W_OK 2
|
||||
#define X_OK 1
|
||||
#define F_OK 0
|
||||
#endif
|
||||
|
||||
#else
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef Y_DUMP_STACK
|
||||
#define Y_DUMP_STACK() do { } while (0)
|
||||
#endif
|
||||
|
||||
#ifndef BUG
|
||||
#define BUG() do {\
|
||||
yaffs_trace(YAFFS_TRACE_BUG,\
|
||||
"==>> yaffs bug: " __FILE__ " %d",\
|
||||
__LINE__);\
|
||||
Y_DUMP_STACK();\
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#endif
|
29
flight/pios/common/libraries/yaffs2/library.mk
Normal file
29
flight/pios/common/libraries/yaffs2/library.mk
Normal file
@ -0,0 +1,29 @@
|
||||
#
|
||||
# Rules to add yaffs2 to the PiOS target
|
||||
#
|
||||
#
|
||||
# Note that the PIOS target-specific makefile will detect that YAFFS2_DIR
|
||||
# has been defined and add in the target-specific pieces separately.
|
||||
#
|
||||
|
||||
|
||||
#
|
||||
# Directory containing this makefile
|
||||
#
|
||||
YAFFS2_DIR := $(dir $(lastword $(MAKEFILE_LIST)))
|
||||
|
||||
# Compiler options
|
||||
#
|
||||
CDEFS += -DCONFIG_YAFFS_DIRECT
|
||||
CDEFS += -DCONFIG_YAFFS_YAFFS2
|
||||
CDEFS += -DCONFIG_YAFFS_DEFINES_TYPES
|
||||
CDEFS += -DCONFIG_YAFFS_PROVIDE_DEFS
|
||||
CDEFS += -DCONFIG_YAFFSFS_PROVIDE_VALUES
|
||||
CDEFS += -DCONFIG_YAFFS_OP
|
||||
#ARCHFLAGS += -DARCH_POSIX
|
||||
|
||||
#
|
||||
# Yaffs2 device library source and includes
|
||||
#
|
||||
SRC += $(sort $(wildcard $(YAFFS2_DIR)*.c))
|
||||
EXTRAINCDIRS += $(YAFFS2_DIR)/inc
|
357
flight/pios/common/libraries/yaffs2/yaffs_allocator.c
Normal file
357
flight/pios/common/libraries/yaffs2/yaffs_allocator.c
Normal file
@ -0,0 +1,357 @@
|
||||
/*
|
||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include "yaffs_allocator.h"
|
||||
#include "yaffs_guts.h"
|
||||
#include "yaffs_trace.h"
|
||||
#include "yportenv.h"
|
||||
|
||||
/*
|
||||
* Each entry in yaffs_tnode_list and yaffs_obj_list hold blocks
|
||||
* of approx 100 objects that are themn allocated singly.
|
||||
* This is basically a simplified slab allocator.
|
||||
*
|
||||
* We don't use the Linux slab allocator because slab does not allow
|
||||
* us to dump all the objects in one hit when we do a umount and tear
|
||||
* down all the tnodes and objects. slab requires that we first free
|
||||
* the individual objects.
|
||||
*
|
||||
* Once yaffs has been mainlined I shall try to motivate for a change
|
||||
* to slab to provide the extra features we need here.
|
||||
*/
|
||||
|
||||
struct yaffs_tnode_list {
|
||||
struct yaffs_tnode_list *next;
|
||||
struct yaffs_tnode *tnodes;
|
||||
};
|
||||
|
||||
struct yaffs_obj_list {
|
||||
struct yaffs_obj_list *next;
|
||||
struct yaffs_obj *objects;
|
||||
};
|
||||
|
||||
struct yaffs_allocator {
|
||||
int n_tnodes_created;
|
||||
struct yaffs_tnode *free_tnodes;
|
||||
int n_free_tnodes;
|
||||
struct yaffs_tnode_list *alloc_tnode_list;
|
||||
|
||||
int n_obj_created;
|
||||
struct list_head free_objs;
|
||||
int n_free_objects;
|
||||
|
||||
struct yaffs_obj_list *allocated_obj_list;
|
||||
};
|
||||
|
||||
static void yaffs_deinit_raw_tnodes(struct yaffs_dev *dev)
|
||||
{
|
||||
struct yaffs_allocator *allocator =
|
||||
(struct yaffs_allocator *)dev->allocator;
|
||||
struct yaffs_tnode_list *tmp;
|
||||
|
||||
if (!allocator) {
|
||||
BUG();
|
||||
return;
|
||||
}
|
||||
|
||||
while (allocator->alloc_tnode_list) {
|
||||
tmp = allocator->alloc_tnode_list->next;
|
||||
|
||||
kfree(allocator->alloc_tnode_list->tnodes);
|
||||
kfree(allocator->alloc_tnode_list);
|
||||
allocator->alloc_tnode_list = tmp;
|
||||
}
|
||||
|
||||
allocator->free_tnodes = NULL;
|
||||
allocator->n_free_tnodes = 0;
|
||||
allocator->n_tnodes_created = 0;
|
||||
}
|
||||
|
||||
static void yaffs_init_raw_tnodes(struct yaffs_dev *dev)
|
||||
{
|
||||
struct yaffs_allocator *allocator = dev->allocator;
|
||||
|
||||
if (!allocator) {
|
||||
BUG();
|
||||
return;
|
||||
}
|
||||
|
||||
allocator->alloc_tnode_list = NULL;
|
||||
allocator->free_tnodes = NULL;
|
||||
allocator->n_free_tnodes = 0;
|
||||
allocator->n_tnodes_created = 0;
|
||||
}
|
||||
|
||||
static int yaffs_create_tnodes(struct yaffs_dev *dev, int n_tnodes)
|
||||
{
|
||||
struct yaffs_allocator *allocator =
|
||||
(struct yaffs_allocator *)dev->allocator;
|
||||
int i;
|
||||
struct yaffs_tnode *new_tnodes;
|
||||
u8 *mem;
|
||||
struct yaffs_tnode *curr;
|
||||
struct yaffs_tnode *next;
|
||||
struct yaffs_tnode_list *tnl;
|
||||
|
||||
if (!allocator) {
|
||||
BUG();
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
|
||||
if (n_tnodes < 1)
|
||||
return YAFFS_OK;
|
||||
|
||||
/* make these things */
|
||||
new_tnodes = kmalloc(n_tnodes * dev->tnode_size, GFP_NOFS);
|
||||
mem = (u8 *) new_tnodes;
|
||||
|
||||
if (!new_tnodes) {
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"yaffs: Could not allocate Tnodes");
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
|
||||
/* New hookup for wide tnodes */
|
||||
for (i = 0; i < n_tnodes - 1; i++) {
|
||||
curr = (struct yaffs_tnode *)&mem[i * dev->tnode_size];
|
||||
next = (struct yaffs_tnode *)&mem[(i + 1) * dev->tnode_size];
|
||||
curr->internal[0] = next;
|
||||
}
|
||||
|
||||
curr = (struct yaffs_tnode *)&mem[(n_tnodes - 1) * dev->tnode_size];
|
||||
curr->internal[0] = allocator->free_tnodes;
|
||||
allocator->free_tnodes = (struct yaffs_tnode *)mem;
|
||||
|
||||
allocator->n_free_tnodes += n_tnodes;
|
||||
allocator->n_tnodes_created += n_tnodes;
|
||||
|
||||
/* Now add this bunch of tnodes to a list for freeing up.
|
||||
* NB If we can't add this to the management list it isn't fatal
|
||||
* but it just means we can't free this bunch of tnodes later.
|
||||
*/
|
||||
tnl = kmalloc(sizeof(struct yaffs_tnode_list), GFP_NOFS);
|
||||
if (!tnl) {
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"Could not add tnodes to management list");
|
||||
return YAFFS_FAIL;
|
||||
} else {
|
||||
tnl->tnodes = new_tnodes;
|
||||
tnl->next = allocator->alloc_tnode_list;
|
||||
allocator->alloc_tnode_list = tnl;
|
||||
}
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_ALLOCATE, "Tnodes added");
|
||||
|
||||
return YAFFS_OK;
|
||||
}
|
||||
|
||||
struct yaffs_tnode *yaffs_alloc_raw_tnode(struct yaffs_dev *dev)
|
||||
{
|
||||
struct yaffs_allocator *allocator =
|
||||
(struct yaffs_allocator *)dev->allocator;
|
||||
struct yaffs_tnode *tn = NULL;
|
||||
|
||||
if (!allocator) {
|
||||
BUG();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* If there are none left make more */
|
||||
if (!allocator->free_tnodes)
|
||||
yaffs_create_tnodes(dev, YAFFS_ALLOCATION_NTNODES);
|
||||
|
||||
if (allocator->free_tnodes) {
|
||||
tn = allocator->free_tnodes;
|
||||
allocator->free_tnodes = allocator->free_tnodes->internal[0];
|
||||
allocator->n_free_tnodes--;
|
||||
}
|
||||
|
||||
return tn;
|
||||
}
|
||||
|
||||
/* FreeTnode frees up a tnode and puts it back on the free list */
|
||||
void yaffs_free_raw_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn)
|
||||
{
|
||||
struct yaffs_allocator *allocator = dev->allocator;
|
||||
|
||||
if (!allocator) {
|
||||
BUG();
|
||||
return;
|
||||
}
|
||||
|
||||
if (tn) {
|
||||
tn->internal[0] = allocator->free_tnodes;
|
||||
allocator->free_tnodes = tn;
|
||||
allocator->n_free_tnodes++;
|
||||
}
|
||||
dev->checkpoint_blocks_required = 0; /* force recalculation */
|
||||
}
|
||||
|
||||
/*--------------- yaffs_obj alloaction ------------------------
|
||||
*
|
||||
* Free yaffs_objs are stored in a list using obj->siblings.
|
||||
* The blocks of allocated objects are stored in a linked list.
|
||||
*/
|
||||
|
||||
static void yaffs_init_raw_objs(struct yaffs_dev *dev)
|
||||
{
|
||||
struct yaffs_allocator *allocator = dev->allocator;
|
||||
|
||||
if (!allocator) {
|
||||
BUG();
|
||||
return;
|
||||
}
|
||||
|
||||
allocator->allocated_obj_list = NULL;
|
||||
INIT_LIST_HEAD(&allocator->free_objs);
|
||||
allocator->n_free_objects = 0;
|
||||
}
|
||||
|
||||
static void yaffs_deinit_raw_objs(struct yaffs_dev *dev)
|
||||
{
|
||||
struct yaffs_allocator *allocator = dev->allocator;
|
||||
struct yaffs_obj_list *tmp;
|
||||
|
||||
if (!allocator) {
|
||||
BUG();
|
||||
return;
|
||||
}
|
||||
|
||||
while (allocator->allocated_obj_list) {
|
||||
tmp = allocator->allocated_obj_list->next;
|
||||
kfree(allocator->allocated_obj_list->objects);
|
||||
kfree(allocator->allocated_obj_list);
|
||||
allocator->allocated_obj_list = tmp;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&allocator->free_objs);
|
||||
allocator->n_free_objects = 0;
|
||||
allocator->n_obj_created = 0;
|
||||
}
|
||||
|
||||
static int yaffs_create_free_objs(struct yaffs_dev *dev, int n_obj)
|
||||
{
|
||||
struct yaffs_allocator *allocator = dev->allocator;
|
||||
int i;
|
||||
struct yaffs_obj *new_objs;
|
||||
struct yaffs_obj_list *list;
|
||||
|
||||
if (!allocator) {
|
||||
BUG();
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
|
||||
if (n_obj < 1)
|
||||
return YAFFS_OK;
|
||||
|
||||
/* make these things */
|
||||
new_objs = kmalloc(n_obj * sizeof(struct yaffs_obj), GFP_NOFS);
|
||||
list = kmalloc(sizeof(struct yaffs_obj_list), GFP_NOFS);
|
||||
|
||||
if (!new_objs || !list) {
|
||||
kfree(new_objs);
|
||||
new_objs = NULL;
|
||||
kfree(list);
|
||||
list = NULL;
|
||||
yaffs_trace(YAFFS_TRACE_ALLOCATE,
|
||||
"Could not allocate more objects");
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
|
||||
/* Hook them into the free list */
|
||||
for (i = 0; i < n_obj; i++)
|
||||
list_add(&new_objs[i].siblings, &allocator->free_objs);
|
||||
|
||||
allocator->n_free_objects += n_obj;
|
||||
allocator->n_obj_created += n_obj;
|
||||
|
||||
/* Now add this bunch of Objects to a list for freeing up. */
|
||||
|
||||
list->objects = new_objs;
|
||||
list->next = allocator->allocated_obj_list;
|
||||
allocator->allocated_obj_list = list;
|
||||
|
||||
return YAFFS_OK;
|
||||
}
|
||||
|
||||
struct yaffs_obj *yaffs_alloc_raw_obj(struct yaffs_dev *dev)
|
||||
{
|
||||
struct yaffs_obj *obj = NULL;
|
||||
struct list_head *lh;
|
||||
struct yaffs_allocator *allocator = dev->allocator;
|
||||
|
||||
if (!allocator) {
|
||||
BUG();
|
||||
return obj;
|
||||
}
|
||||
|
||||
/* If there are none left make more */
|
||||
if (list_empty(&allocator->free_objs))
|
||||
yaffs_create_free_objs(dev, YAFFS_ALLOCATION_NOBJECTS);
|
||||
|
||||
if (!list_empty(&allocator->free_objs)) {
|
||||
lh = allocator->free_objs.next;
|
||||
obj = list_entry(lh, struct yaffs_obj, siblings);
|
||||
list_del_init(lh);
|
||||
allocator->n_free_objects--;
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
void yaffs_free_raw_obj(struct yaffs_dev *dev, struct yaffs_obj *obj)
|
||||
{
|
||||
|
||||
struct yaffs_allocator *allocator = dev->allocator;
|
||||
|
||||
if (!allocator) {
|
||||
BUG();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Link into the free list. */
|
||||
list_add(&obj->siblings, &allocator->free_objs);
|
||||
allocator->n_free_objects++;
|
||||
}
|
||||
|
||||
void yaffs_deinit_raw_tnodes_and_objs(struct yaffs_dev *dev)
|
||||
{
|
||||
|
||||
if (!dev->allocator) {
|
||||
BUG();
|
||||
return;
|
||||
}
|
||||
|
||||
yaffs_deinit_raw_tnodes(dev);
|
||||
yaffs_deinit_raw_objs(dev);
|
||||
kfree(dev->allocator);
|
||||
dev->allocator = NULL;
|
||||
}
|
||||
|
||||
void yaffs_init_raw_tnodes_and_objs(struct yaffs_dev *dev)
|
||||
{
|
||||
struct yaffs_allocator *allocator;
|
||||
|
||||
if (dev->allocator) {
|
||||
BUG();
|
||||
return;
|
||||
}
|
||||
|
||||
allocator = kmalloc(sizeof(struct yaffs_allocator), GFP_NOFS);
|
||||
if (allocator) {
|
||||
dev->allocator = allocator;
|
||||
yaffs_init_raw_tnodes(dev);
|
||||
yaffs_init_raw_objs(dev);
|
||||
}
|
||||
}
|
||||
|
110
flight/pios/common/libraries/yaffs2/yaffs_attribs.c
Normal file
110
flight/pios/common/libraries/yaffs2/yaffs_attribs.c
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include "yaffs_attribs.h"
|
||||
|
||||
|
||||
void yaffs_load_attribs(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh)
|
||||
{
|
||||
#ifdef CONFIG_YAFFS_WINCE
|
||||
obj->win_atime[0] = oh->win_atime[0];
|
||||
obj->win_ctime[0] = oh->win_ctime[0];
|
||||
obj->win_mtime[0] = oh->win_mtime[0];
|
||||
obj->win_atime[1] = oh->win_atime[1];
|
||||
obj->win_ctime[1] = oh->win_ctime[1];
|
||||
obj->win_mtime[1] = oh->win_mtime[1];
|
||||
#else
|
||||
obj->yst_uid = oh->yst_uid;
|
||||
obj->yst_gid = oh->yst_gid;
|
||||
obj->yst_atime = oh->yst_atime;
|
||||
obj->yst_mtime = oh->yst_mtime;
|
||||
obj->yst_ctime = oh->yst_ctime;
|
||||
obj->yst_rdev = oh->yst_rdev;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void yaffs_load_attribs_oh(struct yaffs_obj_hdr *oh, struct yaffs_obj *obj)
|
||||
{
|
||||
#ifdef CONFIG_YAFFS_WINCE
|
||||
oh->win_atime[0] = obj->win_atime[0];
|
||||
oh->win_ctime[0] = obj->win_ctime[0];
|
||||
oh->win_mtime[0] = obj->win_mtime[0];
|
||||
oh->win_atime[1] = obj->win_atime[1];
|
||||
oh->win_ctime[1] = obj->win_ctime[1];
|
||||
oh->win_mtime[1] = obj->win_mtime[1];
|
||||
#else
|
||||
oh->yst_uid = obj->yst_uid;
|
||||
oh->yst_gid = obj->yst_gid;
|
||||
oh->yst_atime = obj->yst_atime;
|
||||
oh->yst_mtime = obj->yst_mtime;
|
||||
oh->yst_ctime = obj->yst_ctime;
|
||||
oh->yst_rdev = obj->yst_rdev;
|
||||
#endif
|
||||
}
|
||||
|
||||
void yaffs_attribs_init(struct yaffs_obj *obj, u32 gid, u32 uid, u32 rdev)
|
||||
{
|
||||
|
||||
#ifdef CONFIG_YAFFS_WINCE
|
||||
yfsd_win_file_time_now(obj->win_atime);
|
||||
obj->win_ctime[0] = obj->win_mtime[0] = obj->win_atime[0];
|
||||
obj->win_ctime[1] = obj->win_mtime[1] = obj->win_atime[1];
|
||||
|
||||
#else
|
||||
yaffs_load_current_time(obj, 1, 1);
|
||||
obj->yst_rdev = rdev;
|
||||
obj->yst_uid = uid;
|
||||
obj->yst_gid = gid;
|
||||
#endif
|
||||
}
|
||||
|
||||
void yaffs_load_current_time(struct yaffs_obj *obj, int do_a, int do_c)
|
||||
{
|
||||
#ifdef CONFIG_YAFFS_WINCE
|
||||
yfsd_win_file_time_now(obj->win_atime);
|
||||
obj->win_ctime[0] = obj->win_mtime[0] =
|
||||
obj->win_atime[0];
|
||||
obj->win_ctime[1] = obj->win_mtime[1] =
|
||||
obj->win_atime[1];
|
||||
|
||||
#else
|
||||
|
||||
obj->yst_mtime = Y_CURRENT_TIME;
|
||||
if (do_a)
|
||||
obj->yst_atime = obj->yst_atime;
|
||||
if (do_c)
|
||||
obj->yst_ctime = obj->yst_atime;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef CONFIG_YAFFS_OP // unused yaffs code
|
||||
static Y_LOFF_T yaffs_get_file_size(struct yaffs_obj *obj)
|
||||
{
|
||||
YCHAR *alias = NULL;
|
||||
obj = yaffs_get_equivalent_obj(obj);
|
||||
|
||||
switch (obj->variant_type) {
|
||||
case YAFFS_OBJECT_TYPE_FILE:
|
||||
return obj->variant.file_variant.file_size;
|
||||
case YAFFS_OBJECT_TYPE_SYMLINK:
|
||||
alias = obj->variant.symlink_variant.alias;
|
||||
if (!alias)
|
||||
return 0;
|
||||
return yaffs_strnlen(alias, YAFFS_MAX_ALIAS_LENGTH);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
97
flight/pios/common/libraries/yaffs2/yaffs_bitmap.c
Normal file
97
flight/pios/common/libraries/yaffs2/yaffs_bitmap.c
Normal file
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include "yaffs_bitmap.h"
|
||||
#include "yaffs_trace.h"
|
||||
/*
|
||||
* Chunk bitmap manipulations
|
||||
*/
|
||||
|
||||
static inline u8 *yaffs_block_bits(struct yaffs_dev *dev, int blk)
|
||||
{
|
||||
if (blk < dev->internal_start_block || blk > dev->internal_end_block) {
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"BlockBits block %d is not valid",
|
||||
blk);
|
||||
BUG();
|
||||
}
|
||||
return dev->chunk_bits +
|
||||
(dev->chunk_bit_stride * (blk - dev->internal_start_block));
|
||||
}
|
||||
|
||||
void yaffs_verify_chunk_bit_id(struct yaffs_dev *dev, int blk, int chunk)
|
||||
{
|
||||
if (blk < dev->internal_start_block || blk > dev->internal_end_block ||
|
||||
chunk < 0 || chunk >= dev->param.chunks_per_block) {
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"Chunk Id (%d:%d) invalid",
|
||||
blk, chunk);
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
|
||||
void yaffs_clear_chunk_bits(struct yaffs_dev *dev, int blk)
|
||||
{
|
||||
u8 *blk_bits = yaffs_block_bits(dev, blk);
|
||||
|
||||
memset(blk_bits, 0, dev->chunk_bit_stride);
|
||||
}
|
||||
|
||||
void yaffs_clear_chunk_bit(struct yaffs_dev *dev, int blk, int chunk)
|
||||
{
|
||||
u8 *blk_bits = yaffs_block_bits(dev, blk);
|
||||
|
||||
yaffs_verify_chunk_bit_id(dev, blk, chunk);
|
||||
blk_bits[chunk / 8] &= ~(1 << (chunk & 7));
|
||||
}
|
||||
|
||||
void yaffs_set_chunk_bit(struct yaffs_dev *dev, int blk, int chunk)
|
||||
{
|
||||
u8 *blk_bits = yaffs_block_bits(dev, blk);
|
||||
|
||||
yaffs_verify_chunk_bit_id(dev, blk, chunk);
|
||||
blk_bits[chunk / 8] |= (1 << (chunk & 7));
|
||||
}
|
||||
|
||||
int yaffs_check_chunk_bit(struct yaffs_dev *dev, int blk, int chunk)
|
||||
{
|
||||
u8 *blk_bits = yaffs_block_bits(dev, blk);
|
||||
|
||||
yaffs_verify_chunk_bit_id(dev, blk, chunk);
|
||||
return (blk_bits[chunk / 8] & (1 << (chunk & 7))) ? 1 : 0;
|
||||
}
|
||||
|
||||
int yaffs_still_some_chunks(struct yaffs_dev *dev, int blk)
|
||||
{
|
||||
u8 *blk_bits = yaffs_block_bits(dev, blk);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < dev->chunk_bit_stride; i++) {
|
||||
if (*blk_bits)
|
||||
return 1;
|
||||
blk_bits++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int yaffs_count_chunk_bits(struct yaffs_dev *dev, int blk)
|
||||
{
|
||||
u8 *blk_bits = yaffs_block_bits(dev, blk);
|
||||
int i;
|
||||
int n = 0;
|
||||
|
||||
for (i = 0; i < dev->chunk_bit_stride; i++, blk_bits++)
|
||||
n += hweight8(*blk_bits);
|
||||
|
||||
return n;
|
||||
}
|
477
flight/pios/common/libraries/yaffs2/yaffs_checkptrw.c
Normal file
477
flight/pios/common/libraries/yaffs2/yaffs_checkptrw.c
Normal file
@ -0,0 +1,477 @@
|
||||
/*
|
||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include "yaffs_checkptrw.h"
|
||||
#include "yaffs_getblockinfo.h"
|
||||
|
||||
struct yaffs_checkpt_chunk_hdr {
|
||||
int version;
|
||||
int seq;
|
||||
u32 sum;
|
||||
u32 xor;
|
||||
} ;
|
||||
|
||||
|
||||
static int apply_chunk_offset(struct yaffs_dev *dev, int chunk)
|
||||
{
|
||||
return chunk - dev->chunk_offset;
|
||||
}
|
||||
|
||||
static int apply_block_offset(struct yaffs_dev *dev, int block)
|
||||
{
|
||||
return block - dev->block_offset;
|
||||
}
|
||||
|
||||
static void yaffs2_checkpt_init_chunk_hdr(struct yaffs_dev *dev)
|
||||
{
|
||||
struct yaffs_checkpt_chunk_hdr hdr;
|
||||
|
||||
hdr.version = YAFFS_CHECKPOINT_VERSION;
|
||||
hdr.seq = dev->checkpt_page_seq;
|
||||
hdr.sum = dev->checkpt_sum;
|
||||
hdr.xor = dev->checkpt_xor;
|
||||
|
||||
dev->checkpt_byte_offs = sizeof(hdr);
|
||||
|
||||
memcpy(dev->checkpt_buffer, &hdr, sizeof(hdr));
|
||||
}
|
||||
|
||||
static int yaffs2_checkpt_check_chunk_hdr(struct yaffs_dev *dev)
|
||||
{
|
||||
struct yaffs_checkpt_chunk_hdr hdr;
|
||||
|
||||
memcpy(&hdr, dev->checkpt_buffer, sizeof(hdr));
|
||||
|
||||
dev->checkpt_byte_offs = sizeof(hdr);
|
||||
|
||||
return hdr.version == YAFFS_CHECKPOINT_VERSION &&
|
||||
hdr.seq == dev->checkpt_page_seq &&
|
||||
hdr.sum == dev->checkpt_sum &&
|
||||
hdr.xor == dev->checkpt_xor;
|
||||
}
|
||||
|
||||
static int yaffs2_checkpt_space_ok(struct yaffs_dev *dev)
|
||||
{
|
||||
int blocks_avail = dev->n_erased_blocks - dev->param.n_reserved_blocks;
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"checkpt blocks_avail = %d", blocks_avail);
|
||||
|
||||
return (blocks_avail <= 0) ? 0 : 1;
|
||||
}
|
||||
|
||||
static int yaffs_checkpt_erase(struct yaffs_dev *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!dev->drv.drv_erase_fn)
|
||||
return 0;
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"checking blocks %d to %d",
|
||||
dev->internal_start_block, dev->internal_end_block);
|
||||
|
||||
for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) {
|
||||
struct yaffs_block_info *bi = yaffs_get_block_info(dev, i);
|
||||
int offset_i = apply_block_offset(dev, i);
|
||||
int result;
|
||||
|
||||
if (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT) {
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"erasing checkpt block %d", i);
|
||||
|
||||
dev->n_erasures++;
|
||||
|
||||
result = dev->drv.drv_erase_fn(dev, offset_i);
|
||||
if(result) {
|
||||
bi->block_state = YAFFS_BLOCK_STATE_EMPTY;
|
||||
dev->n_erased_blocks++;
|
||||
dev->n_free_chunks +=
|
||||
dev->param.chunks_per_block;
|
||||
} else {
|
||||
dev->drv.drv_mark_bad_fn(dev, offset_i);
|
||||
bi->block_state = YAFFS_BLOCK_STATE_DEAD;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dev->blocks_in_checkpt = 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void yaffs2_checkpt_find_erased_block(struct yaffs_dev *dev)
|
||||
{
|
||||
int i;
|
||||
int blocks_avail = dev->n_erased_blocks - dev->param.n_reserved_blocks;
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"allocating checkpt block: erased %d reserved %d avail %d next %d ",
|
||||
dev->n_erased_blocks, dev->param.n_reserved_blocks,
|
||||
blocks_avail, dev->checkpt_next_block);
|
||||
|
||||
if (dev->checkpt_next_block >= 0 &&
|
||||
dev->checkpt_next_block <= dev->internal_end_block &&
|
||||
blocks_avail > 0) {
|
||||
|
||||
for (i = dev->checkpt_next_block; i <= dev->internal_end_block;
|
||||
i++) {
|
||||
struct yaffs_block_info *bi;
|
||||
|
||||
bi = yaffs_get_block_info(dev, i);
|
||||
if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) {
|
||||
dev->checkpt_next_block = i + 1;
|
||||
dev->checkpt_cur_block = i;
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"allocating checkpt block %d", i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT, "out of checkpt blocks");
|
||||
|
||||
dev->checkpt_next_block = -1;
|
||||
dev->checkpt_cur_block = -1;
|
||||
}
|
||||
|
||||
static void yaffs2_checkpt_find_block(struct yaffs_dev *dev)
|
||||
{
|
||||
int i;
|
||||
struct yaffs_ext_tags tags;
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"find next checkpt block: start: blocks %d next %d",
|
||||
dev->blocks_in_checkpt, dev->checkpt_next_block);
|
||||
|
||||
if (dev->blocks_in_checkpt < dev->checkpt_max_blocks)
|
||||
for (i = dev->checkpt_next_block; i <= dev->internal_end_block;
|
||||
i++) {
|
||||
int chunk = i * dev->param.chunks_per_block;
|
||||
enum yaffs_block_state state;
|
||||
u32 seq;
|
||||
|
||||
dev->tagger.read_chunk_tags_fn(dev,
|
||||
apply_chunk_offset(dev, chunk),
|
||||
NULL, &tags);
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"find next checkpt block: search: block %d state %d oid %d seq %d eccr %d",
|
||||
i, (int) state,
|
||||
tags.obj_id, tags.seq_number,
|
||||
tags.ecc_result);
|
||||
|
||||
if (tags.seq_number != YAFFS_SEQUENCE_CHECKPOINT_DATA)
|
||||
continue;
|
||||
|
||||
dev->tagger.query_block_fn(dev,
|
||||
apply_block_offset(dev, i),
|
||||
&state, &seq);
|
||||
if (state == YAFFS_BLOCK_STATE_DEAD)
|
||||
continue;
|
||||
|
||||
/* Right kind of block */
|
||||
dev->checkpt_next_block = tags.obj_id;
|
||||
dev->checkpt_cur_block = i;
|
||||
dev->checkpt_block_list[dev->blocks_in_checkpt] = i;
|
||||
dev->blocks_in_checkpt++;
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"found checkpt block %d", i);
|
||||
return;
|
||||
}
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT, "found no more checkpt blocks");
|
||||
|
||||
dev->checkpt_next_block = -1;
|
||||
dev->checkpt_cur_block = -1;
|
||||
}
|
||||
|
||||
int yaffs2_checkpt_open(struct yaffs_dev *dev, int writing)
|
||||
{
|
||||
int i;
|
||||
|
||||
dev->checkpt_open_write = writing;
|
||||
|
||||
/* Got the functions we need? */
|
||||
if (!dev->tagger.write_chunk_tags_fn ||
|
||||
!dev->tagger.read_chunk_tags_fn ||
|
||||
!dev->drv.drv_erase_fn ||
|
||||
!dev->drv.drv_mark_bad_fn)
|
||||
return 0;
|
||||
|
||||
if (writing && !yaffs2_checkpt_space_ok(dev))
|
||||
return 0;
|
||||
|
||||
if (!dev->checkpt_buffer)
|
||||
dev->checkpt_buffer =
|
||||
kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS);
|
||||
if (!dev->checkpt_buffer)
|
||||
return 0;
|
||||
|
||||
dev->checkpt_page_seq = 0;
|
||||
dev->checkpt_byte_count = 0;
|
||||
dev->checkpt_sum = 0;
|
||||
dev->checkpt_xor = 0;
|
||||
dev->checkpt_cur_block = -1;
|
||||
dev->checkpt_cur_chunk = -1;
|
||||
dev->checkpt_next_block = dev->internal_start_block;
|
||||
|
||||
if (writing) {
|
||||
memset(dev->checkpt_buffer, 0, dev->data_bytes_per_chunk);
|
||||
yaffs2_checkpt_init_chunk_hdr(dev);
|
||||
return yaffs_checkpt_erase(dev);
|
||||
}
|
||||
|
||||
/* Opening for a read */
|
||||
/* Set to a value that will kick off a read */
|
||||
dev->checkpt_byte_offs = dev->data_bytes_per_chunk;
|
||||
/* A checkpoint block list of 1 checkpoint block per 16 block is
|
||||
* (hopefully) going to be way more than we need */
|
||||
dev->blocks_in_checkpt = 0;
|
||||
dev->checkpt_max_blocks =
|
||||
(dev->internal_end_block - dev->internal_start_block) / 16 + 2;
|
||||
dev->checkpt_block_list =
|
||||
kmalloc(sizeof(int) * dev->checkpt_max_blocks, GFP_NOFS);
|
||||
|
||||
if (!dev->checkpt_block_list)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < dev->checkpt_max_blocks; i++)
|
||||
dev->checkpt_block_list[i] = -1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int yaffs2_get_checkpt_sum(struct yaffs_dev *dev, u32 * sum)
|
||||
{
|
||||
u32 composite_sum;
|
||||
|
||||
composite_sum = (dev->checkpt_sum << 8) | (dev->checkpt_xor & 0xff);
|
||||
*sum = composite_sum;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int yaffs2_checkpt_flush_buffer(struct yaffs_dev *dev)
|
||||
{
|
||||
int chunk;
|
||||
int offset_chunk;
|
||||
struct yaffs_ext_tags tags;
|
||||
|
||||
if (dev->checkpt_cur_block < 0) {
|
||||
yaffs2_checkpt_find_erased_block(dev);
|
||||
dev->checkpt_cur_chunk = 0;
|
||||
}
|
||||
|
||||
if (dev->checkpt_cur_block < 0)
|
||||
return 0;
|
||||
|
||||
tags.is_deleted = 0;
|
||||
tags.obj_id = dev->checkpt_next_block; /* Hint to next place to look */
|
||||
tags.chunk_id = dev->checkpt_page_seq + 1;
|
||||
tags.seq_number = YAFFS_SEQUENCE_CHECKPOINT_DATA;
|
||||
tags.n_bytes = dev->data_bytes_per_chunk;
|
||||
if (dev->checkpt_cur_chunk == 0) {
|
||||
/* First chunk we write for the block? Set block state to
|
||||
checkpoint */
|
||||
struct yaffs_block_info *bi =
|
||||
yaffs_get_block_info(dev, dev->checkpt_cur_block);
|
||||
bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT;
|
||||
dev->blocks_in_checkpt++;
|
||||
}
|
||||
|
||||
chunk =
|
||||
dev->checkpt_cur_block * dev->param.chunks_per_block +
|
||||
dev->checkpt_cur_chunk;
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"checkpoint wite buffer nand %d(%d:%d) objid %d chId %d",
|
||||
chunk, dev->checkpt_cur_block, dev->checkpt_cur_chunk,
|
||||
tags.obj_id, tags.chunk_id);
|
||||
|
||||
offset_chunk = apply_chunk_offset(dev, chunk);
|
||||
|
||||
dev->n_page_writes++;
|
||||
|
||||
dev->tagger.write_chunk_tags_fn(dev, offset_chunk,
|
||||
dev->checkpt_buffer, &tags);
|
||||
dev->checkpt_page_seq++;
|
||||
dev->checkpt_cur_chunk++;
|
||||
if (dev->checkpt_cur_chunk >= dev->param.chunks_per_block) {
|
||||
dev->checkpt_cur_chunk = 0;
|
||||
dev->checkpt_cur_block = -1;
|
||||
}
|
||||
memset(dev->checkpt_buffer, 0, dev->data_bytes_per_chunk);
|
||||
|
||||
yaffs2_checkpt_init_chunk_hdr(dev);
|
||||
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int yaffs2_checkpt_wr(struct yaffs_dev *dev, const void *data, int n_bytes)
|
||||
{
|
||||
int i = 0;
|
||||
int ok = 1;
|
||||
u8 *data_bytes = (u8 *) data;
|
||||
|
||||
if (!dev->checkpt_buffer)
|
||||
return 0;
|
||||
|
||||
if (!dev->checkpt_open_write)
|
||||
return -1;
|
||||
|
||||
while (i < n_bytes && ok) {
|
||||
dev->checkpt_buffer[dev->checkpt_byte_offs] = *data_bytes;
|
||||
dev->checkpt_sum += *data_bytes;
|
||||
dev->checkpt_xor ^= *data_bytes;
|
||||
|
||||
dev->checkpt_byte_offs++;
|
||||
i++;
|
||||
data_bytes++;
|
||||
dev->checkpt_byte_count++;
|
||||
|
||||
if (dev->checkpt_byte_offs < 0 ||
|
||||
dev->checkpt_byte_offs >= dev->data_bytes_per_chunk)
|
||||
ok = yaffs2_checkpt_flush_buffer(dev);
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
int yaffs2_checkpt_rd(struct yaffs_dev *dev, void *data, int n_bytes)
|
||||
{
|
||||
int i = 0;
|
||||
int ok = 1;
|
||||
struct yaffs_ext_tags tags;
|
||||
int chunk;
|
||||
int offset_chunk;
|
||||
u8 *data_bytes = (u8 *) data;
|
||||
|
||||
if (!dev->checkpt_buffer)
|
||||
return 0;
|
||||
|
||||
if (dev->checkpt_open_write)
|
||||
return -1;
|
||||
|
||||
while (i < n_bytes && ok) {
|
||||
|
||||
if (dev->checkpt_byte_offs < 0 ||
|
||||
dev->checkpt_byte_offs >= dev->data_bytes_per_chunk) {
|
||||
|
||||
if (dev->checkpt_cur_block < 0) {
|
||||
yaffs2_checkpt_find_block(dev);
|
||||
dev->checkpt_cur_chunk = 0;
|
||||
}
|
||||
|
||||
if (dev->checkpt_cur_block < 0) {
|
||||
ok = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
chunk = dev->checkpt_cur_block *
|
||||
dev->param.chunks_per_block +
|
||||
dev->checkpt_cur_chunk;
|
||||
|
||||
offset_chunk = apply_chunk_offset(dev, chunk);
|
||||
dev->n_page_reads++;
|
||||
|
||||
/* read in the next chunk */
|
||||
dev->tagger.read_chunk_tags_fn(dev,
|
||||
offset_chunk,
|
||||
dev->checkpt_buffer,
|
||||
&tags);
|
||||
#ifdef CONFIG_YAFFS_OP
|
||||
if (tags.chunk_id != (u32)(dev->checkpt_page_seq + 1) ||
|
||||
#else
|
||||
if (tags.chunk_id != (dev->checkpt_page_seq + 1) ||
|
||||
#endif
|
||||
tags.ecc_result > YAFFS_ECC_RESULT_FIXED ||
|
||||
tags.seq_number != YAFFS_SEQUENCE_CHECKPOINT_DATA) {
|
||||
ok = 0;
|
||||
break;
|
||||
}
|
||||
if(!yaffs2_checkpt_check_chunk_hdr(dev)) {
|
||||
ok = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
dev->checkpt_page_seq++;
|
||||
dev->checkpt_cur_chunk++;
|
||||
|
||||
if (dev->checkpt_cur_chunk >=
|
||||
dev->param.chunks_per_block)
|
||||
dev->checkpt_cur_block = -1;
|
||||
|
||||
}
|
||||
|
||||
*data_bytes = dev->checkpt_buffer[dev->checkpt_byte_offs];
|
||||
dev->checkpt_sum += *data_bytes;
|
||||
dev->checkpt_xor ^= *data_bytes;
|
||||
dev->checkpt_byte_offs++;
|
||||
i++;
|
||||
data_bytes++;
|
||||
dev->checkpt_byte_count++;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
int yaffs_checkpt_close(struct yaffs_dev *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (dev->checkpt_open_write) {
|
||||
if (dev->checkpt_byte_offs !=
|
||||
sizeof(sizeof(struct yaffs_checkpt_chunk_hdr)))
|
||||
yaffs2_checkpt_flush_buffer(dev);
|
||||
} else if (dev->checkpt_block_list) {
|
||||
for (i = 0;
|
||||
i < dev->blocks_in_checkpt &&
|
||||
dev->checkpt_block_list[i] >= 0; i++) {
|
||||
int blk = dev->checkpt_block_list[i];
|
||||
struct yaffs_block_info *bi = NULL;
|
||||
|
||||
if (dev->internal_start_block <= blk &&
|
||||
blk <= dev->internal_end_block)
|
||||
bi = yaffs_get_block_info(dev, blk);
|
||||
if (bi && bi->block_state == YAFFS_BLOCK_STATE_EMPTY)
|
||||
bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT;
|
||||
}
|
||||
kfree(dev->checkpt_block_list);
|
||||
dev->checkpt_block_list = NULL;
|
||||
}
|
||||
|
||||
dev->n_free_chunks -=
|
||||
dev->blocks_in_checkpt * dev->param.chunks_per_block;
|
||||
dev->n_erased_blocks -= dev->blocks_in_checkpt;
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT, "checkpoint byte count %d",
|
||||
dev->checkpt_byte_count);
|
||||
|
||||
if (dev->checkpt_buffer) {
|
||||
/* free the buffer */
|
||||
kfree(dev->checkpt_buffer);
|
||||
dev->checkpt_buffer = NULL;
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int yaffs2_checkpt_invalidate_stream(struct yaffs_dev *dev)
|
||||
{
|
||||
/* Erase the checkpoint data */
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"checkpoint invalidate of %d blocks",
|
||||
dev->blocks_in_checkpt);
|
||||
|
||||
return yaffs_checkpt_erase(dev);
|
||||
}
|
281
flight/pios/common/libraries/yaffs2/yaffs_ecc.c
Normal file
281
flight/pios/common/libraries/yaffs2/yaffs_ecc.c
Normal file
@ -0,0 +1,281 @@
|
||||
/*
|
||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code implements the ECC algorithm used in SmartMedia.
|
||||
*
|
||||
* The ECC comprises 22 bits of parity information and is stuffed into 3 bytes.
|
||||
* The two unused bit are set to 1.
|
||||
* The ECC can correct single bit errors in a 256-byte page of data. Thus, two
|
||||
* such ECC blocks are used on a 512-byte NAND page.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "yportenv.h"
|
||||
|
||||
#include "yaffs_ecc.h"
|
||||
|
||||
/* Table generated by gen-ecc.c
|
||||
* Using a table means we do not have to calculate p1..p4 and p1'..p4'
|
||||
* for each byte of data. These are instead provided in a table in bits7..2.
|
||||
* Bit 0 of each entry indicates whether the entry has an odd or even parity,
|
||||
* and therefore this bytes influence on the line parity.
|
||||
*/
|
||||
|
||||
static const unsigned char column_parity_table[] = {
|
||||
0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69,
|
||||
0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00,
|
||||
0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc,
|
||||
0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95,
|
||||
0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0,
|
||||
0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99,
|
||||
0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65,
|
||||
0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c,
|
||||
0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc,
|
||||
0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5,
|
||||
0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59,
|
||||
0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30,
|
||||
0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55,
|
||||
0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c,
|
||||
0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0,
|
||||
0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9,
|
||||
0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0,
|
||||
0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9,
|
||||
0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55,
|
||||
0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c,
|
||||
0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59,
|
||||
0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30,
|
||||
0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc,
|
||||
0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5,
|
||||
0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65,
|
||||
0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c,
|
||||
0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0,
|
||||
0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99,
|
||||
0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc,
|
||||
0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95,
|
||||
0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69,
|
||||
0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00,
|
||||
};
|
||||
|
||||
|
||||
/* Calculate the ECC for a 256-byte block of data */
|
||||
void yaffs_ecc_calc(const unsigned char *data, unsigned char *ecc)
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned char col_parity = 0;
|
||||
unsigned char line_parity = 0;
|
||||
unsigned char line_parity_prime = 0;
|
||||
unsigned char t;
|
||||
unsigned char b;
|
||||
|
||||
for (i = 0; i < 256; i++) {
|
||||
b = column_parity_table[*data++];
|
||||
col_parity ^= b;
|
||||
|
||||
if (b & 0x01) { /* odd number of bits in the byte */
|
||||
line_parity ^= i;
|
||||
line_parity_prime ^= ~i;
|
||||
}
|
||||
}
|
||||
|
||||
ecc[2] = (~col_parity) | 0x03;
|
||||
|
||||
t = 0;
|
||||
if (line_parity & 0x80)
|
||||
t |= 0x80;
|
||||
if (line_parity_prime & 0x80)
|
||||
t |= 0x40;
|
||||
if (line_parity & 0x40)
|
||||
t |= 0x20;
|
||||
if (line_parity_prime & 0x40)
|
||||
t |= 0x10;
|
||||
if (line_parity & 0x20)
|
||||
t |= 0x08;
|
||||
if (line_parity_prime & 0x20)
|
||||
t |= 0x04;
|
||||
if (line_parity & 0x10)
|
||||
t |= 0x02;
|
||||
if (line_parity_prime & 0x10)
|
||||
t |= 0x01;
|
||||
ecc[1] = ~t;
|
||||
|
||||
t = 0;
|
||||
if (line_parity & 0x08)
|
||||
t |= 0x80;
|
||||
if (line_parity_prime & 0x08)
|
||||
t |= 0x40;
|
||||
if (line_parity & 0x04)
|
||||
t |= 0x20;
|
||||
if (line_parity_prime & 0x04)
|
||||
t |= 0x10;
|
||||
if (line_parity & 0x02)
|
||||
t |= 0x08;
|
||||
if (line_parity_prime & 0x02)
|
||||
t |= 0x04;
|
||||
if (line_parity & 0x01)
|
||||
t |= 0x02;
|
||||
if (line_parity_prime & 0x01)
|
||||
t |= 0x01;
|
||||
ecc[0] = ~t;
|
||||
|
||||
}
|
||||
|
||||
/* Correct the ECC on a 256 byte block of data */
|
||||
|
||||
int yaffs_ecc_correct(unsigned char *data, unsigned char *read_ecc,
|
||||
const unsigned char *test_ecc)
|
||||
{
|
||||
unsigned char d0, d1, d2; /* deltas */
|
||||
|
||||
d0 = read_ecc[0] ^ test_ecc[0];
|
||||
d1 = read_ecc[1] ^ test_ecc[1];
|
||||
d2 = read_ecc[2] ^ test_ecc[2];
|
||||
|
||||
if ((d0 | d1 | d2) == 0)
|
||||
return 0; /* no error */
|
||||
|
||||
if (((d0 ^ (d0 >> 1)) & 0x55) == 0x55 &&
|
||||
((d1 ^ (d1 >> 1)) & 0x55) == 0x55 &&
|
||||
((d2 ^ (d2 >> 1)) & 0x54) == 0x54) {
|
||||
/* Single bit (recoverable) error in data */
|
||||
|
||||
unsigned byte;
|
||||
unsigned bit;
|
||||
|
||||
bit = byte = 0;
|
||||
|
||||
if (d1 & 0x80)
|
||||
byte |= 0x80;
|
||||
if (d1 & 0x20)
|
||||
byte |= 0x40;
|
||||
if (d1 & 0x08)
|
||||
byte |= 0x20;
|
||||
if (d1 & 0x02)
|
||||
byte |= 0x10;
|
||||
if (d0 & 0x80)
|
||||
byte |= 0x08;
|
||||
if (d0 & 0x20)
|
||||
byte |= 0x04;
|
||||
if (d0 & 0x08)
|
||||
byte |= 0x02;
|
||||
if (d0 & 0x02)
|
||||
byte |= 0x01;
|
||||
|
||||
if (d2 & 0x80)
|
||||
bit |= 0x04;
|
||||
if (d2 & 0x20)
|
||||
bit |= 0x02;
|
||||
if (d2 & 0x08)
|
||||
bit |= 0x01;
|
||||
|
||||
data[byte] ^= (1 << bit);
|
||||
|
||||
return 1; /* Corrected the error */
|
||||
}
|
||||
|
||||
if ((hweight8(d0) + hweight8(d1) + hweight8(d2)) == 1) {
|
||||
/* Reccoverable error in ecc */
|
||||
|
||||
read_ecc[0] = test_ecc[0];
|
||||
read_ecc[1] = test_ecc[1];
|
||||
read_ecc[2] = test_ecc[2];
|
||||
|
||||
return 1; /* Corrected the error */
|
||||
}
|
||||
|
||||
/* Unrecoverable error */
|
||||
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* ECCxxxOther does ECC calcs on arbitrary n bytes of data
|
||||
*/
|
||||
void yaffs_ecc_calc_other(const unsigned char *data, unsigned n_bytes,
|
||||
struct yaffs_ecc_other *ecc_other)
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned char col_parity = 0;
|
||||
unsigned line_parity = 0;
|
||||
unsigned line_parity_prime = 0;
|
||||
unsigned char b;
|
||||
|
||||
for (i = 0; i < n_bytes; i++) {
|
||||
b = column_parity_table[*data++];
|
||||
col_parity ^= b;
|
||||
|
||||
if (b & 0x01) {
|
||||
/* odd number of bits in the byte */
|
||||
line_parity ^= i;
|
||||
line_parity_prime ^= ~i;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ecc_other->col_parity = (col_parity >> 2) & 0x3f;
|
||||
ecc_other->line_parity = line_parity;
|
||||
ecc_other->line_parity_prime = line_parity_prime;
|
||||
}
|
||||
|
||||
int yaffs_ecc_correct_other(unsigned char *data, unsigned n_bytes,
|
||||
struct yaffs_ecc_other *read_ecc,
|
||||
const struct yaffs_ecc_other *test_ecc)
|
||||
{
|
||||
unsigned char delta_col; /* column parity delta */
|
||||
unsigned delta_line; /* line parity delta */
|
||||
unsigned delta_line_prime; /* line parity delta */
|
||||
unsigned bit;
|
||||
|
||||
delta_col = read_ecc->col_parity ^ test_ecc->col_parity;
|
||||
delta_line = read_ecc->line_parity ^ test_ecc->line_parity;
|
||||
delta_line_prime =
|
||||
read_ecc->line_parity_prime ^ test_ecc->line_parity_prime;
|
||||
|
||||
if ((delta_col | delta_line | delta_line_prime) == 0)
|
||||
return 0; /* no error */
|
||||
|
||||
if (delta_line == ~delta_line_prime &&
|
||||
(((delta_col ^ (delta_col >> 1)) & 0x15) == 0x15)) {
|
||||
/* Single bit (recoverable) error in data */
|
||||
|
||||
bit = 0;
|
||||
|
||||
if (delta_col & 0x20)
|
||||
bit |= 0x04;
|
||||
if (delta_col & 0x08)
|
||||
bit |= 0x02;
|
||||
if (delta_col & 0x02)
|
||||
bit |= 0x01;
|
||||
|
||||
if (delta_line >= n_bytes)
|
||||
return -1;
|
||||
|
||||
data[delta_line] ^= (1 << bit);
|
||||
|
||||
return 1; /* corrected */
|
||||
}
|
||||
|
||||
if ((hweight32(delta_line) +
|
||||
hweight32(delta_line_prime) +
|
||||
hweight8(delta_col)) == 1) {
|
||||
/* Reccoverable error in ecc */
|
||||
|
||||
*read_ecc = *test_ecc;
|
||||
return 1; /* corrected */
|
||||
}
|
||||
|
||||
/* Unrecoverable error */
|
||||
|
||||
return -1;
|
||||
}
|
58
flight/pios/common/libraries/yaffs2/yaffs_error.c
Normal file
58
flight/pios/common/libraries/yaffs2/yaffs_error.c
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* YAFFS: Yet another FFS. A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Timothy Manning <timothy@yaffs.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include "yaffsfs.h"
|
||||
|
||||
struct error_entry {
|
||||
int code;
|
||||
const char *text;
|
||||
};
|
||||
|
||||
static const struct error_entry error_list[] = {
|
||||
{ ENOMEM , "ENOMEM" },
|
||||
{ EBUSY , "EBUSY"},
|
||||
{ ENODEV , "ENODEV"},
|
||||
{ EINVAL , "EINVAL"},
|
||||
{ EBADF , "EBADF"},
|
||||
{ EACCES , "EACCES"},
|
||||
{ EXDEV , "EXDEV" },
|
||||
{ ENOENT , "ENOENT"},
|
||||
{ ENOSPC , "ENOSPC"},
|
||||
{ ERANGE , "ERANGE"},
|
||||
{ ENODATA, "ENODATA"},
|
||||
{ ENOTEMPTY, "ENOTEMPTY"},
|
||||
{ ENAMETOOLONG, "ENAMETOOLONG"},
|
||||
{ ENOMEM , "ENOMEM"},
|
||||
{ EEXIST , "EEXIST"},
|
||||
{ ENOTDIR , "ENOTDIR"},
|
||||
{ EISDIR , "EISDIR"},
|
||||
{ ENFILE, "ENFILE"},
|
||||
{ EROFS, "EROFS"},
|
||||
{ EFAULT, "EFAULT"},
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const char *yaffs_error_to_str(int err)
|
||||
{
|
||||
const struct error_entry *e = error_list;
|
||||
|
||||
if (err < 0)
|
||||
err = -err;
|
||||
|
||||
while (e->code && e->text) {
|
||||
if (err == e->code)
|
||||
return e->text;
|
||||
e++;
|
||||
}
|
||||
return "Unknown error code";
|
||||
}
|
5175
flight/pios/common/libraries/yaffs2/yaffs_guts.c
Normal file
5175
flight/pios/common/libraries/yaffs2/yaffs_guts.c
Normal file
@ -0,0 +1,5175 @@
|
||||
/*
|
||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include "yportenv.h"
|
||||
#include "yaffs_trace.h"
|
||||
|
||||
#include "yaffs_guts.h"
|
||||
#include "yaffs_getblockinfo.h"
|
||||
#include "yaffs_tagscompat.h"
|
||||
#include "yaffs_tagsmarshall.h"
|
||||
#include "yaffs_nand.h"
|
||||
#include "yaffs_yaffs1.h"
|
||||
#include "yaffs_yaffs2.h"
|
||||
#include "yaffs_bitmap.h"
|
||||
#include "yaffs_verify.h"
|
||||
#include "yaffs_nand.h"
|
||||
#include "yaffs_packedtags2.h"
|
||||
#include "yaffs_nameval.h"
|
||||
#include "yaffs_allocator.h"
|
||||
#include "yaffs_attribs.h"
|
||||
#include "yaffs_summary.h"
|
||||
|
||||
/* Note YAFFS_GC_GOOD_ENOUGH must be <= YAFFS_GC_PASSIVE_THRESHOLD */
|
||||
#define YAFFS_GC_GOOD_ENOUGH 2
|
||||
#define YAFFS_GC_PASSIVE_THRESHOLD 4
|
||||
|
||||
#include "yaffs_ecc.h"
|
||||
|
||||
/* Forward declarations */
|
||||
|
||||
static int yaffs_wr_data_obj(struct yaffs_obj *in, int inode_chunk,
|
||||
const u8 *buffer, int n_bytes, int use_reserve);
|
||||
|
||||
static void yaffs_fix_null_name(struct yaffs_obj *obj, YCHAR *name,
|
||||
int buffer_size);
|
||||
|
||||
/* Function to calculate chunk and offset */
|
||||
|
||||
void yaffs_addr_to_chunk(struct yaffs_dev *dev, Y_LOFF_T addr,
|
||||
int *chunk_out, u32 *offset_out)
|
||||
{
|
||||
int chunk;
|
||||
u32 offset;
|
||||
|
||||
chunk = (u32) (addr >> dev->chunk_shift);
|
||||
|
||||
if (dev->chunk_div == 1) {
|
||||
/* easy power of 2 case */
|
||||
offset = (u32) (addr & dev->chunk_mask);
|
||||
} else {
|
||||
/* Non power-of-2 case */
|
||||
|
||||
Y_LOFF_T chunk_base;
|
||||
|
||||
chunk /= dev->chunk_div;
|
||||
|
||||
chunk_base = ((Y_LOFF_T) chunk) * dev->data_bytes_per_chunk;
|
||||
offset = (u32) (addr - chunk_base);
|
||||
}
|
||||
|
||||
*chunk_out = chunk;
|
||||
*offset_out = offset;
|
||||
}
|
||||
|
||||
/* Function to return the number of shifts for a power of 2 greater than or
|
||||
* equal to the given number
|
||||
* Note we don't try to cater for all possible numbers and this does not have to
|
||||
* be hellishly efficient.
|
||||
*/
|
||||
|
||||
static inline u32 calc_shifts_ceiling(u32 x)
|
||||
{
|
||||
int extra_bits;
|
||||
int shifts;
|
||||
|
||||
shifts = extra_bits = 0;
|
||||
|
||||
while (x > 1) {
|
||||
if (x & 1)
|
||||
extra_bits++;
|
||||
x >>= 1;
|
||||
shifts++;
|
||||
}
|
||||
|
||||
if (extra_bits)
|
||||
shifts++;
|
||||
|
||||
return shifts;
|
||||
}
|
||||
|
||||
/* Function to return the number of shifts to get a 1 in bit 0
|
||||
*/
|
||||
|
||||
static inline u32 calc_shifts(u32 x)
|
||||
{
|
||||
u32 shifts;
|
||||
|
||||
shifts = 0;
|
||||
|
||||
if (!x)
|
||||
return 0;
|
||||
|
||||
while (!(x & 1)) {
|
||||
x >>= 1;
|
||||
shifts++;
|
||||
}
|
||||
|
||||
return shifts;
|
||||
}
|
||||
|
||||
/*
|
||||
* Temporary buffer manipulations.
|
||||
*/
|
||||
|
||||
static int yaffs_init_tmp_buffers(struct yaffs_dev *dev)
|
||||
{
|
||||
int i;
|
||||
u8 *buf = (u8 *) 1;
|
||||
|
||||
memset(dev->temp_buffer, 0, sizeof(dev->temp_buffer));
|
||||
|
||||
for (i = 0; buf && i < YAFFS_N_TEMP_BUFFERS; i++) {
|
||||
dev->temp_buffer[i].in_use = 0;
|
||||
buf = kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS);
|
||||
dev->temp_buffer[i].buffer = buf;
|
||||
}
|
||||
|
||||
return buf ? YAFFS_OK : YAFFS_FAIL;
|
||||
}
|
||||
|
||||
u8 *yaffs_get_temp_buffer(struct yaffs_dev * dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
dev->temp_in_use++;
|
||||
if (dev->temp_in_use > dev->max_temp)
|
||||
dev->max_temp = dev->temp_in_use;
|
||||
|
||||
for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) {
|
||||
if (dev->temp_buffer[i].in_use == 0) {
|
||||
dev->temp_buffer[i].in_use = 1;
|
||||
return dev->temp_buffer[i].buffer;
|
||||
}
|
||||
}
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_BUFFERS, "Out of temp buffers");
|
||||
/*
|
||||
* If we got here then we have to allocate an unmanaged one
|
||||
* This is not good.
|
||||
*/
|
||||
|
||||
dev->unmanaged_buffer_allocs++;
|
||||
return kmalloc(dev->data_bytes_per_chunk, GFP_NOFS);
|
||||
|
||||
}
|
||||
|
||||
void yaffs_release_temp_buffer(struct yaffs_dev *dev, u8 *buffer)
|
||||
{
|
||||
int i;
|
||||
|
||||
dev->temp_in_use--;
|
||||
|
||||
for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) {
|
||||
if (dev->temp_buffer[i].buffer == buffer) {
|
||||
dev->temp_buffer[i].in_use = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (buffer) {
|
||||
/* assume it is an unmanaged one. */
|
||||
yaffs_trace(YAFFS_TRACE_BUFFERS,
|
||||
"Releasing unmanaged temp buffer");
|
||||
kfree(buffer);
|
||||
dev->unmanaged_buffer_deallocs++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Functions for robustisizing TODO
|
||||
*
|
||||
*/
|
||||
|
||||
static void yaffs_handle_chunk_wr_ok(struct yaffs_dev *dev, int nand_chunk,
|
||||
const u8 *data,
|
||||
const struct yaffs_ext_tags *tags)
|
||||
{
|
||||
(void) dev;
|
||||
(void) nand_chunk;
|
||||
(void) data;
|
||||
(void) tags;
|
||||
}
|
||||
|
||||
static void yaffs_handle_chunk_update(struct yaffs_dev *dev, int nand_chunk,
|
||||
const struct yaffs_ext_tags *tags)
|
||||
{
|
||||
(void) dev;
|
||||
(void) nand_chunk;
|
||||
(void) tags;
|
||||
}
|
||||
|
||||
void yaffs_handle_chunk_error(struct yaffs_dev *dev,
|
||||
struct yaffs_block_info *bi)
|
||||
{
|
||||
if (!bi->gc_prioritise) {
|
||||
bi->gc_prioritise = 1;
|
||||
dev->has_pending_prioritised_gc = 1;
|
||||
bi->chunk_error_strikes++;
|
||||
|
||||
if (bi->chunk_error_strikes > 3) {
|
||||
bi->needs_retiring = 1; /* Too many stikes, so retire */
|
||||
yaffs_trace(YAFFS_TRACE_ALWAYS,
|
||||
"yaffs: Block struck out");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void yaffs_handle_chunk_wr_error(struct yaffs_dev *dev, int nand_chunk,
|
||||
int erased_ok)
|
||||
{
|
||||
int flash_block = nand_chunk / dev->param.chunks_per_block;
|
||||
struct yaffs_block_info *bi = yaffs_get_block_info(dev, flash_block);
|
||||
|
||||
yaffs_handle_chunk_error(dev, bi);
|
||||
|
||||
if (erased_ok) {
|
||||
/* Was an actual write failure,
|
||||
* so mark the block for retirement.*/
|
||||
bi->needs_retiring = 1;
|
||||
yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,
|
||||
"**>> Block %d needs retiring", flash_block);
|
||||
}
|
||||
|
||||
/* Delete the chunk */
|
||||
yaffs_chunk_del(dev, nand_chunk, 1, __LINE__);
|
||||
yaffs_skip_rest_of_block(dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* Verification code
|
||||
*/
|
||||
|
||||
/*
|
||||
* Simple hash function. Needs to have a reasonable spread
|
||||
*/
|
||||
|
||||
static inline int yaffs_hash_fn(int n)
|
||||
{
|
||||
if (n < 0)
|
||||
n = -n;
|
||||
return n % YAFFS_NOBJECT_BUCKETS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Access functions to useful fake objects.
|
||||
* Note that root might have a presence in NAND if permissions are set.
|
||||
*/
|
||||
|
||||
struct yaffs_obj *yaffs_root(struct yaffs_dev *dev)
|
||||
{
|
||||
return dev->root_dir;
|
||||
}
|
||||
|
||||
struct yaffs_obj *yaffs_lost_n_found(struct yaffs_dev *dev)
|
||||
{
|
||||
return dev->lost_n_found;
|
||||
}
|
||||
|
||||
/*
|
||||
* Erased NAND checking functions
|
||||
*/
|
||||
|
||||
int yaffs_check_ff(u8 *buffer, int n_bytes)
|
||||
{
|
||||
/* Horrible, slow implementation */
|
||||
while (n_bytes--) {
|
||||
if (*buffer != 0xff)
|
||||
return 0;
|
||||
buffer++;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int yaffs_check_chunk_erased(struct yaffs_dev *dev, int nand_chunk)
|
||||
{
|
||||
int retval = YAFFS_OK;
|
||||
u8 *data = yaffs_get_temp_buffer(dev);
|
||||
struct yaffs_ext_tags tags;
|
||||
|
||||
if (!yaffs_rd_chunk_tags_nand(dev, nand_chunk, data, &tags))
|
||||
{
|
||||
yaffs_trace(YAFFS_TRACE_ERROR, "yaffs_check_chunk_erased: unhandled error from rd_chunk_tags_nand");
|
||||
}
|
||||
|
||||
|
||||
if (tags.ecc_result > YAFFS_ECC_RESULT_NO_ERROR)
|
||||
retval = YAFFS_FAIL;
|
||||
|
||||
if (!yaffs_check_ff(data, dev->data_bytes_per_chunk) ||
|
||||
tags.chunk_used) {
|
||||
yaffs_trace(YAFFS_TRACE_NANDACCESS,
|
||||
"Chunk %d not erased", nand_chunk);
|
||||
retval = YAFFS_FAIL;
|
||||
}
|
||||
|
||||
|
||||
yaffs_release_temp_buffer(dev, data);
|
||||
|
||||
return retval;
|
||||
|
||||
}
|
||||
|
||||
static int yaffs_verify_chunk_written(struct yaffs_dev *dev,
|
||||
int nand_chunk,
|
||||
const u8 *data,
|
||||
struct yaffs_ext_tags *tags)
|
||||
{
|
||||
int retval = YAFFS_OK;
|
||||
struct yaffs_ext_tags temp_tags;
|
||||
u8 *buffer = yaffs_get_temp_buffer(dev);
|
||||
|
||||
if (!yaffs_rd_chunk_tags_nand(dev, nand_chunk, buffer, &temp_tags))
|
||||
{
|
||||
yaffs_trace(YAFFS_TRACE_ERROR, "yaffs_verify_chunk_written: unhandled error from rd_chunk_tags_nand");
|
||||
}
|
||||
|
||||
if (memcmp(buffer, data, dev->data_bytes_per_chunk) ||
|
||||
temp_tags.obj_id != tags->obj_id ||
|
||||
temp_tags.chunk_id != tags->chunk_id ||
|
||||
temp_tags.n_bytes != tags->n_bytes)
|
||||
retval = YAFFS_FAIL;
|
||||
|
||||
yaffs_release_temp_buffer(dev, buffer);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
int yaffs_check_alloc_available(struct yaffs_dev *dev, int n_chunks)
|
||||
{
|
||||
int reserved_chunks;
|
||||
int reserved_blocks = dev->param.n_reserved_blocks;
|
||||
int checkpt_blocks;
|
||||
|
||||
checkpt_blocks = yaffs_calc_checkpt_blocks_required(dev);
|
||||
|
||||
reserved_chunks =
|
||||
(reserved_blocks + checkpt_blocks) * dev->param.chunks_per_block;
|
||||
|
||||
return (dev->n_free_chunks > (reserved_chunks + n_chunks));
|
||||
}
|
||||
|
||||
static int yaffs_find_alloc_block(struct yaffs_dev *dev)
|
||||
{
|
||||
int i;
|
||||
struct yaffs_block_info *bi;
|
||||
|
||||
if (dev->n_erased_blocks < 1) {
|
||||
/* Hoosterman we've got a problem.
|
||||
* Can't get space to gc
|
||||
*/
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"yaffs tragedy: no more erased blocks");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Find an empty block. */
|
||||
|
||||
for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) {
|
||||
dev->alloc_block_finder++;
|
||||
if (dev->alloc_block_finder < dev->internal_start_block
|
||||
|| dev->alloc_block_finder > dev->internal_end_block) {
|
||||
dev->alloc_block_finder = dev->internal_start_block;
|
||||
}
|
||||
|
||||
bi = yaffs_get_block_info(dev, dev->alloc_block_finder);
|
||||
|
||||
if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) {
|
||||
bi->block_state = YAFFS_BLOCK_STATE_ALLOCATING;
|
||||
dev->seq_number++;
|
||||
bi->seq_number = dev->seq_number;
|
||||
dev->n_erased_blocks--;
|
||||
yaffs_trace(YAFFS_TRACE_ALLOCATE,
|
||||
"Allocated block %d, seq %d, %d left" ,
|
||||
dev->alloc_block_finder, dev->seq_number,
|
||||
dev->n_erased_blocks);
|
||||
return dev->alloc_block_finder;
|
||||
}
|
||||
}
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_ALWAYS,
|
||||
"yaffs tragedy: no more erased blocks, but there should have been %d",
|
||||
dev->n_erased_blocks);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int yaffs_alloc_chunk(struct yaffs_dev *dev, int use_reserver,
|
||||
struct yaffs_block_info **block_ptr)
|
||||
{
|
||||
int ret_val;
|
||||
struct yaffs_block_info *bi;
|
||||
|
||||
if (dev->alloc_block < 0) {
|
||||
/* Get next block to allocate off */
|
||||
dev->alloc_block = yaffs_find_alloc_block(dev);
|
||||
dev->alloc_page = 0;
|
||||
}
|
||||
|
||||
if (!use_reserver && !yaffs_check_alloc_available(dev, 1)) {
|
||||
/* No space unless we're allowed to use the reserve. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (dev->n_erased_blocks < dev->param.n_reserved_blocks
|
||||
&& dev->alloc_page == 0)
|
||||
yaffs_trace(YAFFS_TRACE_ALLOCATE, "Allocating reserve");
|
||||
|
||||
/* Next page please.... */
|
||||
if (dev->alloc_block >= 0) {
|
||||
bi = yaffs_get_block_info(dev, dev->alloc_block);
|
||||
|
||||
ret_val = (dev->alloc_block * dev->param.chunks_per_block) +
|
||||
dev->alloc_page;
|
||||
bi->pages_in_use++;
|
||||
yaffs_set_chunk_bit(dev, dev->alloc_block, dev->alloc_page);
|
||||
|
||||
dev->alloc_page++;
|
||||
|
||||
dev->n_free_chunks--;
|
||||
|
||||
/* If the block is full set the state to full */
|
||||
if (dev->alloc_page >= (u32)dev->param.chunks_per_block) {
|
||||
bi->block_state = YAFFS_BLOCK_STATE_FULL;
|
||||
dev->alloc_block = -1;
|
||||
}
|
||||
|
||||
if (block_ptr)
|
||||
*block_ptr = bi;
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"!!!!!!!!! Allocator out !!!!!!!!!!!!!!!!!");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int yaffs_get_erased_chunks(struct yaffs_dev *dev)
|
||||
{
|
||||
int n;
|
||||
|
||||
n = dev->n_erased_blocks * dev->param.chunks_per_block;
|
||||
|
||||
if (dev->alloc_block > 0)
|
||||
n += (dev->param.chunks_per_block - dev->alloc_page);
|
||||
|
||||
return n;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* yaffs_skip_rest_of_block() skips over the rest of the allocation block
|
||||
* if we don't want to write to it.
|
||||
*/
|
||||
void yaffs_skip_rest_of_block(struct yaffs_dev *dev)
|
||||
{
|
||||
struct yaffs_block_info *bi;
|
||||
|
||||
if (dev->alloc_block > 0) {
|
||||
bi = yaffs_get_block_info(dev, dev->alloc_block);
|
||||
if (bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING) {
|
||||
bi->block_state = YAFFS_BLOCK_STATE_FULL;
|
||||
dev->alloc_block = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int yaffs_write_new_chunk(struct yaffs_dev *dev,
|
||||
const u8 *data,
|
||||
struct yaffs_ext_tags *tags, int use_reserver)
|
||||
{
|
||||
u32 attempts = 0;
|
||||
int write_ok = 0;
|
||||
int chunk;
|
||||
|
||||
yaffs2_checkpt_invalidate(dev);
|
||||
|
||||
do {
|
||||
struct yaffs_block_info *bi = 0;
|
||||
int erased_ok = 0;
|
||||
|
||||
chunk = yaffs_alloc_chunk(dev, use_reserver, &bi);
|
||||
if (chunk < 0) {
|
||||
/* no space */
|
||||
break;
|
||||
}
|
||||
|
||||
/* First check this chunk is erased, if it needs
|
||||
* checking. The checking policy (unless forced
|
||||
* always on) is as follows:
|
||||
*
|
||||
* Check the first page we try to write in a block.
|
||||
* If the check passes then we don't need to check any
|
||||
* more. If the check fails, we check again...
|
||||
* If the block has been erased, we don't need to check.
|
||||
*
|
||||
* However, if the block has been prioritised for gc,
|
||||
* then we think there might be something odd about
|
||||
* this block and stop using it.
|
||||
*
|
||||
* Rationale: We should only ever see chunks that have
|
||||
* not been erased if there was a partially written
|
||||
* chunk due to power loss. This checking policy should
|
||||
* catch that case with very few checks and thus save a
|
||||
* lot of checks that are most likely not needed.
|
||||
*
|
||||
* Mods to the above
|
||||
* If an erase check fails or the write fails we skip the
|
||||
* rest of the block.
|
||||
*/
|
||||
|
||||
/* let's give it a try */
|
||||
attempts++;
|
||||
|
||||
if (dev->param.always_check_erased)
|
||||
bi->skip_erased_check = 0;
|
||||
|
||||
if (!bi->skip_erased_check) {
|
||||
erased_ok = yaffs_check_chunk_erased(dev, chunk);
|
||||
if (erased_ok != YAFFS_OK) {
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"**>> yaffs chunk %d was not erased",
|
||||
chunk);
|
||||
|
||||
/* If not erased, delete this one,
|
||||
* skip rest of block and
|
||||
* try another chunk */
|
||||
yaffs_chunk_del(dev, chunk, 1, __LINE__);
|
||||
yaffs_skip_rest_of_block(dev);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
write_ok = yaffs_wr_chunk_tags_nand(dev, chunk, data, tags);
|
||||
|
||||
if (!bi->skip_erased_check)
|
||||
write_ok =
|
||||
yaffs_verify_chunk_written(dev, chunk, data, tags);
|
||||
|
||||
if (write_ok != YAFFS_OK) {
|
||||
/* Clean up aborted write, skip to next block and
|
||||
* try another chunk */
|
||||
yaffs_handle_chunk_wr_error(dev, chunk, erased_ok);
|
||||
continue;
|
||||
}
|
||||
|
||||
bi->skip_erased_check = 1;
|
||||
|
||||
/* Copy the data into the robustification buffer */
|
||||
yaffs_handle_chunk_wr_ok(dev, chunk, data, tags);
|
||||
|
||||
} while (write_ok != YAFFS_OK &&
|
||||
(yaffs_wr_attempts <= 0 || attempts <= yaffs_wr_attempts));
|
||||
|
||||
if (!write_ok)
|
||||
chunk = -1;
|
||||
|
||||
if (attempts > 1) {
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"**>> yaffs write required %d attempts",
|
||||
attempts);
|
||||
dev->n_retried_writes += (attempts - 1);
|
||||
}
|
||||
|
||||
return chunk;
|
||||
}
|
||||
|
||||
/*
|
||||
* Block retiring for handling a broken block.
|
||||
*/
|
||||
|
||||
static void yaffs_retire_block(struct yaffs_dev *dev, int flash_block)
|
||||
{
|
||||
struct yaffs_block_info *bi = yaffs_get_block_info(dev, flash_block);
|
||||
|
||||
yaffs2_checkpt_invalidate(dev);
|
||||
|
||||
yaffs2_clear_oldest_dirty_seq(dev, bi);
|
||||
|
||||
if (yaffs_mark_bad(dev, flash_block) != YAFFS_OK) {
|
||||
if (yaffs_erase_block(dev, flash_block) != YAFFS_OK) {
|
||||
yaffs_trace(YAFFS_TRACE_ALWAYS,
|
||||
"yaffs: Failed to mark bad and erase block %d",
|
||||
flash_block);
|
||||
} else {
|
||||
struct yaffs_ext_tags tags;
|
||||
int chunk_id =
|
||||
flash_block * dev->param.chunks_per_block;
|
||||
|
||||
u8 *buffer = yaffs_get_temp_buffer(dev);
|
||||
|
||||
memset(buffer, 0xff, dev->data_bytes_per_chunk);
|
||||
memset(&tags, 0, sizeof(tags));
|
||||
tags.seq_number = YAFFS_SEQUENCE_BAD_BLOCK;
|
||||
if (dev->tagger.write_chunk_tags_fn(dev, chunk_id -
|
||||
dev->chunk_offset,
|
||||
buffer,
|
||||
&tags) != YAFFS_OK)
|
||||
yaffs_trace(YAFFS_TRACE_ALWAYS,
|
||||
"yaffs: Failed to write bad block marker to block %d",
|
||||
flash_block);
|
||||
|
||||
yaffs_release_temp_buffer(dev, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
bi->block_state = YAFFS_BLOCK_STATE_DEAD;
|
||||
bi->gc_prioritise = 0;
|
||||
bi->needs_retiring = 0;
|
||||
|
||||
dev->n_retired_blocks++;
|
||||
}
|
||||
|
||||
/*---------------- Name handling functions ------------*/
|
||||
|
||||
static void yaffs_load_name_from_oh(struct yaffs_dev *dev, YCHAR *name,
|
||||
const YCHAR *oh_name, int buff_size)
|
||||
{
|
||||
#ifdef CONFIG_YAFFS_AUTO_UNICODE
|
||||
if (dev->param.auto_unicode) {
|
||||
if (*oh_name) {
|
||||
/* It is an ASCII name, do an ASCII to
|
||||
* unicode conversion */
|
||||
const char *ascii_oh_name = (const char *)oh_name;
|
||||
int n = buff_size - 1;
|
||||
while (n > 0 && *ascii_oh_name) {
|
||||
*name = *ascii_oh_name;
|
||||
name++;
|
||||
ascii_oh_name++;
|
||||
n--;
|
||||
}
|
||||
} else {
|
||||
yaffs_strncpy(name, oh_name + 1, buff_size - 1);
|
||||
}
|
||||
} else {
|
||||
#else
|
||||
(void) dev;
|
||||
{
|
||||
#endif
|
||||
yaffs_strncpy(name, oh_name, buff_size - 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void yaffs_load_oh_from_name(struct yaffs_dev *dev, YCHAR *oh_name,
|
||||
const YCHAR *name)
|
||||
{
|
||||
#ifdef CONFIG_YAFFS_AUTO_UNICODE
|
||||
|
||||
int is_ascii;
|
||||
const YCHAR *w;
|
||||
|
||||
if (dev->param.auto_unicode) {
|
||||
|
||||
is_ascii = 1;
|
||||
w = name;
|
||||
|
||||
/* Figure out if the name will fit in ascii character set */
|
||||
while (is_ascii && *w) {
|
||||
if ((*w) & 0xff00)
|
||||
is_ascii = 0;
|
||||
w++;
|
||||
}
|
||||
|
||||
if (is_ascii) {
|
||||
/* It is an ASCII name, so convert unicode to ascii */
|
||||
char *ascii_oh_name = (char *)oh_name;
|
||||
int n = YAFFS_MAX_NAME_LENGTH - 1;
|
||||
while (n > 0 && *name) {
|
||||
*ascii_oh_name = *name;
|
||||
name++;
|
||||
ascii_oh_name++;
|
||||
n--;
|
||||
}
|
||||
} else {
|
||||
/* Unicode name, so save starting at the second YCHAR */
|
||||
*oh_name = 0;
|
||||
yaffs_strncpy(oh_name + 1, name, YAFFS_MAX_NAME_LENGTH - 2);
|
||||
}
|
||||
} else {
|
||||
#else
|
||||
dev = dev;
|
||||
{
|
||||
#endif
|
||||
yaffs_strncpy(oh_name, name, YAFFS_MAX_NAME_LENGTH - 1);
|
||||
}
|
||||
}
|
||||
|
||||
static u16 yaffs_calc_name_sum(const YCHAR *name)
|
||||
{
|
||||
u16 sum = 0;
|
||||
u16 i = 1;
|
||||
|
||||
if (!name)
|
||||
return 0;
|
||||
|
||||
while ((*name) && i < (YAFFS_MAX_NAME_LENGTH / 2)) {
|
||||
|
||||
/* 0x1f mask is case insensitive */
|
||||
sum += ((*name) & 0x1f) * i;
|
||||
i++;
|
||||
name++;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
|
||||
void yaffs_set_obj_name(struct yaffs_obj *obj, const YCHAR * name)
|
||||
{
|
||||
memset(obj->short_name, 0, sizeof(obj->short_name));
|
||||
|
||||
if (name && !name[0]) {
|
||||
yaffs_fix_null_name(obj, obj->short_name,
|
||||
YAFFS_SHORT_NAME_LENGTH);
|
||||
name = obj->short_name;
|
||||
} else if (name &&
|
||||
yaffs_strnlen(name, YAFFS_SHORT_NAME_LENGTH + 1) <=
|
||||
YAFFS_SHORT_NAME_LENGTH) {
|
||||
yaffs_strcpy(obj->short_name, name);
|
||||
}
|
||||
|
||||
obj->sum = yaffs_calc_name_sum(name);
|
||||
}
|
||||
|
||||
void yaffs_set_obj_name_from_oh(struct yaffs_obj *obj,
|
||||
const struct yaffs_obj_hdr *oh)
|
||||
{
|
||||
#ifdef CONFIG_YAFFS_AUTO_UNICODE
|
||||
YCHAR tmp_name[YAFFS_MAX_NAME_LENGTH + 1];
|
||||
memset(tmp_name, 0, sizeof(tmp_name));
|
||||
yaffs_load_name_from_oh(obj->my_dev, tmp_name, oh->name,
|
||||
YAFFS_MAX_NAME_LENGTH + 1);
|
||||
yaffs_set_obj_name(obj, tmp_name);
|
||||
#else
|
||||
yaffs_set_obj_name(obj, oh->name);
|
||||
#endif
|
||||
}
|
||||
|
||||
Y_LOFF_T yaffs_max_file_size(struct yaffs_dev *dev)
|
||||
{
|
||||
if(sizeof(Y_LOFF_T) < 8)
|
||||
return YAFFS_MAX_FILE_SIZE_32;
|
||||
else
|
||||
return ((Y_LOFF_T) YAFFS_MAX_CHUNK_ID) * dev->data_bytes_per_chunk;
|
||||
}
|
||||
|
||||
/*-------------------- TNODES -------------------
|
||||
|
||||
* List of spare tnodes
|
||||
* The list is hooked together using the first pointer
|
||||
* in the tnode.
|
||||
*/
|
||||
|
||||
struct yaffs_tnode *yaffs_get_tnode(struct yaffs_dev *dev)
|
||||
{
|
||||
struct yaffs_tnode *tn = yaffs_alloc_raw_tnode(dev);
|
||||
|
||||
if (tn) {
|
||||
memset(tn, 0, dev->tnode_size);
|
||||
dev->n_tnodes++;
|
||||
}
|
||||
|
||||
dev->checkpoint_blocks_required = 0; /* force recalculation */
|
||||
|
||||
return tn;
|
||||
}
|
||||
|
||||
/* FreeTnode frees up a tnode and puts it back on the free list */
|
||||
static void yaffs_free_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn)
|
||||
{
|
||||
yaffs_free_raw_tnode(dev, tn);
|
||||
dev->n_tnodes--;
|
||||
dev->checkpoint_blocks_required = 0; /* force recalculation */
|
||||
}
|
||||
|
||||
static void yaffs_deinit_tnodes_and_objs(struct yaffs_dev *dev)
|
||||
{
|
||||
yaffs_deinit_raw_tnodes_and_objs(dev);
|
||||
dev->n_obj = 0;
|
||||
dev->n_tnodes = 0;
|
||||
}
|
||||
|
||||
static void yaffs_load_tnode_0(struct yaffs_dev *dev, struct yaffs_tnode *tn,
|
||||
unsigned pos, unsigned val)
|
||||
{
|
||||
u32 *map = (u32 *) tn;
|
||||
u32 bit_in_map;
|
||||
u32 bit_in_word;
|
||||
u32 word_in_map;
|
||||
u32 mask;
|
||||
|
||||
pos &= YAFFS_TNODES_LEVEL0_MASK;
|
||||
val >>= dev->chunk_grp_bits;
|
||||
|
||||
bit_in_map = pos * dev->tnode_width;
|
||||
word_in_map = bit_in_map / 32;
|
||||
bit_in_word = bit_in_map & (32 - 1);
|
||||
|
||||
mask = dev->tnode_mask << bit_in_word;
|
||||
|
||||
map[word_in_map] &= ~mask;
|
||||
map[word_in_map] |= (mask & (val << bit_in_word));
|
||||
|
||||
if (dev->tnode_width > (32 - bit_in_word)) {
|
||||
bit_in_word = (32 - bit_in_word);
|
||||
word_in_map++;
|
||||
mask =
|
||||
dev->tnode_mask >> bit_in_word;
|
||||
map[word_in_map] &= ~mask;
|
||||
map[word_in_map] |= (mask & (val >> bit_in_word));
|
||||
}
|
||||
}
|
||||
|
||||
u32 yaffs_get_group_base(struct yaffs_dev *dev, struct yaffs_tnode *tn,
|
||||
unsigned pos)
|
||||
{
|
||||
u32 *map = (u32 *) tn;
|
||||
u32 bit_in_map;
|
||||
u32 bit_in_word;
|
||||
u32 word_in_map;
|
||||
u32 val;
|
||||
|
||||
pos &= YAFFS_TNODES_LEVEL0_MASK;
|
||||
|
||||
bit_in_map = pos * dev->tnode_width;
|
||||
word_in_map = bit_in_map / 32;
|
||||
bit_in_word = bit_in_map & (32 - 1);
|
||||
|
||||
val = map[word_in_map] >> bit_in_word;
|
||||
|
||||
if (dev->tnode_width > (32 - bit_in_word)) {
|
||||
bit_in_word = (32 - bit_in_word);
|
||||
word_in_map++;
|
||||
val |= (map[word_in_map] << bit_in_word);
|
||||
}
|
||||
|
||||
val &= dev->tnode_mask;
|
||||
val <<= dev->chunk_grp_bits;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
/* ------------------- End of individual tnode manipulation -----------------*/
|
||||
|
||||
/* ---------Functions to manipulate the look-up tree (made up of tnodes) ------
|
||||
* The look up tree is represented by the top tnode and the number of top_level
|
||||
* in the tree. 0 means only the level 0 tnode is in the tree.
|
||||
*/
|
||||
|
||||
/* FindLevel0Tnode finds the level 0 tnode, if one exists. */
|
||||
struct yaffs_tnode *yaffs_find_tnode_0(struct yaffs_dev *dev,
|
||||
struct yaffs_file_var *file_struct,
|
||||
u32 chunk_id)
|
||||
{
|
||||
struct yaffs_tnode *tn = file_struct->top;
|
||||
u32 i;
|
||||
int required_depth;
|
||||
int level = file_struct->top_level;
|
||||
|
||||
(void) dev;
|
||||
|
||||
/* Check sane level and chunk Id */
|
||||
if (level < 0 || level > YAFFS_TNODES_MAX_LEVEL)
|
||||
return NULL;
|
||||
|
||||
if (chunk_id > YAFFS_MAX_CHUNK_ID)
|
||||
return NULL;
|
||||
|
||||
/* First check we're tall enough (ie enough top_level) */
|
||||
|
||||
i = chunk_id >> YAFFS_TNODES_LEVEL0_BITS;
|
||||
required_depth = 0;
|
||||
while (i) {
|
||||
i >>= YAFFS_TNODES_INTERNAL_BITS;
|
||||
required_depth++;
|
||||
}
|
||||
|
||||
if (required_depth > file_struct->top_level)
|
||||
return NULL; /* Not tall enough, so we can't find it */
|
||||
|
||||
/* Traverse down to level 0 */
|
||||
while (level > 0 && tn) {
|
||||
tn = tn->internal[(chunk_id >>
|
||||
(YAFFS_TNODES_LEVEL0_BITS +
|
||||
(level - 1) *
|
||||
YAFFS_TNODES_INTERNAL_BITS)) &
|
||||
YAFFS_TNODES_INTERNAL_MASK];
|
||||
level--;
|
||||
}
|
||||
|
||||
return tn;
|
||||
}
|
||||
|
||||
/* add_find_tnode_0 finds the level 0 tnode if it exists,
|
||||
* otherwise first expands the tree.
|
||||
* This happens in two steps:
|
||||
* 1. If the tree isn't tall enough, then make it taller.
|
||||
* 2. Scan down the tree towards the level 0 tnode adding tnodes if required.
|
||||
*
|
||||
* Used when modifying the tree.
|
||||
*
|
||||
* If the tn argument is NULL, then a fresh tnode will be added otherwise the
|
||||
* specified tn will be plugged into the ttree.
|
||||
*/
|
||||
|
||||
struct yaffs_tnode *yaffs_add_find_tnode_0(struct yaffs_dev *dev,
|
||||
struct yaffs_file_var *file_struct,
|
||||
u32 chunk_id,
|
||||
struct yaffs_tnode *passed_tn)
|
||||
{
|
||||
int required_depth;
|
||||
int i;
|
||||
int l;
|
||||
struct yaffs_tnode *tn;
|
||||
u32 x;
|
||||
|
||||
/* Check sane level and page Id */
|
||||
if (file_struct->top_level < 0 ||
|
||||
file_struct->top_level > YAFFS_TNODES_MAX_LEVEL)
|
||||
return NULL;
|
||||
|
||||
if (chunk_id > YAFFS_MAX_CHUNK_ID)
|
||||
return NULL;
|
||||
|
||||
/* First check we're tall enough (ie enough top_level) */
|
||||
|
||||
x = chunk_id >> YAFFS_TNODES_LEVEL0_BITS;
|
||||
required_depth = 0;
|
||||
while (x) {
|
||||
x >>= YAFFS_TNODES_INTERNAL_BITS;
|
||||
required_depth++;
|
||||
}
|
||||
|
||||
if (required_depth > file_struct->top_level) {
|
||||
/* Not tall enough, gotta make the tree taller */
|
||||
for (i = file_struct->top_level; i < required_depth; i++) {
|
||||
|
||||
tn = yaffs_get_tnode(dev);
|
||||
|
||||
if (tn) {
|
||||
tn->internal[0] = file_struct->top;
|
||||
file_struct->top = tn;
|
||||
file_struct->top_level++;
|
||||
} else {
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"yaffs: no more tnodes");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Traverse down to level 0, adding anything we need */
|
||||
|
||||
l = file_struct->top_level;
|
||||
tn = file_struct->top;
|
||||
|
||||
if (l > 0) {
|
||||
while (l > 0 && tn) {
|
||||
x = (chunk_id >>
|
||||
(YAFFS_TNODES_LEVEL0_BITS +
|
||||
(l - 1) * YAFFS_TNODES_INTERNAL_BITS)) &
|
||||
YAFFS_TNODES_INTERNAL_MASK;
|
||||
|
||||
if ((l > 1) && !tn->internal[x]) {
|
||||
/* Add missing non-level-zero tnode */
|
||||
tn->internal[x] = yaffs_get_tnode(dev);
|
||||
if (!tn->internal[x])
|
||||
return NULL;
|
||||
} else if (l == 1) {
|
||||
/* Looking from level 1 at level 0 */
|
||||
if (passed_tn) {
|
||||
/* If we already have one, release it */
|
||||
if (tn->internal[x])
|
||||
yaffs_free_tnode(dev,
|
||||
tn->internal[x]);
|
||||
tn->internal[x] = passed_tn;
|
||||
|
||||
} else if (!tn->internal[x]) {
|
||||
/* Don't have one, none passed in */
|
||||
tn->internal[x] = yaffs_get_tnode(dev);
|
||||
if (!tn->internal[x])
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
tn = tn->internal[x];
|
||||
l--;
|
||||
}
|
||||
} else {
|
||||
/* top is level 0 */
|
||||
if (passed_tn) {
|
||||
memcpy(tn, passed_tn,
|
||||
(dev->tnode_width * YAFFS_NTNODES_LEVEL0) / 8);
|
||||
yaffs_free_tnode(dev, passed_tn);
|
||||
}
|
||||
}
|
||||
|
||||
return tn;
|
||||
}
|
||||
|
||||
static int yaffs_tags_match(const struct yaffs_ext_tags *tags, unsigned int obj_id,
|
||||
unsigned int chunk_obj)
|
||||
{
|
||||
return (tags->chunk_id == chunk_obj &&
|
||||
tags->obj_id == obj_id &&
|
||||
!tags->is_deleted) ? 1 : 0;
|
||||
|
||||
}
|
||||
|
||||
static int yaffs_find_chunk_in_group(struct yaffs_dev *dev, int the_chunk,
|
||||
struct yaffs_ext_tags *tags, int obj_id,
|
||||
int inode_chunk)
|
||||
{
|
||||
int j;
|
||||
|
||||
for (j = 0; the_chunk && j < dev->chunk_grp_size; j++) {
|
||||
if (yaffs_check_chunk_bit
|
||||
(dev, the_chunk / dev->param.chunks_per_block,
|
||||
the_chunk % dev->param.chunks_per_block)) {
|
||||
|
||||
if (dev->chunk_grp_size == 1)
|
||||
return the_chunk;
|
||||
else {
|
||||
if (!yaffs_rd_chunk_tags_nand(dev, the_chunk, NULL, tags))
|
||||
{
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"yaffs_find_chunk_in_group: unhandled error from rd_chunk_tags_nand");
|
||||
}
|
||||
if (yaffs_tags_match(tags,
|
||||
obj_id, inode_chunk)) {
|
||||
/* found it; */
|
||||
return the_chunk;
|
||||
}
|
||||
}
|
||||
}
|
||||
the_chunk++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int yaffs_find_chunk_in_file(struct yaffs_obj *in, int inode_chunk,
|
||||
struct yaffs_ext_tags *tags)
|
||||
{
|
||||
/*Get the Tnode, then get the level 0 offset chunk offset */
|
||||
struct yaffs_tnode *tn;
|
||||
int the_chunk = -1;
|
||||
struct yaffs_ext_tags local_tags;
|
||||
int ret_val = -1;
|
||||
struct yaffs_dev *dev = in->my_dev;
|
||||
|
||||
if (!tags) {
|
||||
/* Passed a NULL, so use our own tags space */
|
||||
tags = &local_tags;
|
||||
}
|
||||
|
||||
tn = yaffs_find_tnode_0(dev, &in->variant.file_variant, inode_chunk);
|
||||
|
||||
if (!tn)
|
||||
return ret_val;
|
||||
|
||||
the_chunk = yaffs_get_group_base(dev, tn, inode_chunk);
|
||||
|
||||
ret_val = yaffs_find_chunk_in_group(dev, the_chunk, tags, in->obj_id,
|
||||
inode_chunk);
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
static int yaffs_find_del_file_chunk(struct yaffs_obj *in, int inode_chunk,
|
||||
struct yaffs_ext_tags *tags)
|
||||
{
|
||||
/* Get the Tnode, then get the level 0 offset chunk offset */
|
||||
struct yaffs_tnode *tn;
|
||||
int the_chunk = -1;
|
||||
struct yaffs_ext_tags local_tags;
|
||||
struct yaffs_dev *dev = in->my_dev;
|
||||
int ret_val = -1;
|
||||
|
||||
if (!tags) {
|
||||
/* Passed a NULL, so use our own tags space */
|
||||
tags = &local_tags;
|
||||
}
|
||||
|
||||
tn = yaffs_find_tnode_0(dev, &in->variant.file_variant, inode_chunk);
|
||||
|
||||
if (!tn)
|
||||
return ret_val;
|
||||
|
||||
the_chunk = yaffs_get_group_base(dev, tn, inode_chunk);
|
||||
|
||||
ret_val = yaffs_find_chunk_in_group(dev, the_chunk, tags, in->obj_id,
|
||||
inode_chunk);
|
||||
|
||||
/* Delete the entry in the filestructure (if found) */
|
||||
if (ret_val != -1)
|
||||
yaffs_load_tnode_0(dev, tn, inode_chunk, 0);
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
int yaffs_put_chunk_in_file(struct yaffs_obj *in, int inode_chunk,
|
||||
int nand_chunk, int in_scan)
|
||||
{
|
||||
/* NB in_scan is zero unless scanning.
|
||||
* For forward scanning, in_scan is > 0;
|
||||
* for backward scanning in_scan is < 0
|
||||
*
|
||||
* nand_chunk = 0 is a dummy insert to make sure the tnodes are there.
|
||||
*/
|
||||
|
||||
struct yaffs_tnode *tn;
|
||||
struct yaffs_dev *dev = in->my_dev;
|
||||
int existing_cunk;
|
||||
struct yaffs_ext_tags existing_tags;
|
||||
struct yaffs_ext_tags new_tags;
|
||||
unsigned existing_serial, new_serial;
|
||||
|
||||
if (in->variant_type != YAFFS_OBJECT_TYPE_FILE) {
|
||||
/* Just ignore an attempt at putting a chunk into a non-file
|
||||
* during scanning.
|
||||
* If it is not during Scanning then something went wrong!
|
||||
*/
|
||||
if (!in_scan) {
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"yaffs tragedy:attempt to put data chunk into a non-file"
|
||||
);
|
||||
BUG();
|
||||
}
|
||||
|
||||
yaffs_chunk_del(dev, nand_chunk, 1, __LINE__);
|
||||
return YAFFS_OK;
|
||||
}
|
||||
|
||||
tn = yaffs_add_find_tnode_0(dev,
|
||||
&in->variant.file_variant,
|
||||
inode_chunk, NULL);
|
||||
if (!tn)
|
||||
return YAFFS_FAIL;
|
||||
|
||||
if (!nand_chunk)
|
||||
/* Dummy insert, bail now */
|
||||
return YAFFS_OK;
|
||||
|
||||
existing_cunk = yaffs_get_group_base(dev, tn, inode_chunk);
|
||||
|
||||
if (in_scan != 0) {
|
||||
/* If we're scanning then we need to test for duplicates
|
||||
* NB This does not need to be efficient since it should only
|
||||
* happen when the power fails during a write, then only one
|
||||
* chunk should ever be affected.
|
||||
*
|
||||
* Correction for YAFFS2: This could happen quite a lot and we
|
||||
* need to think about efficiency! TODO
|
||||
* Update: For backward scanning we don't need to re-read tags
|
||||
* so this is quite cheap.
|
||||
*/
|
||||
|
||||
if (existing_cunk > 0) {
|
||||
/* NB Right now existing chunk will not be real
|
||||
* chunk_id if the chunk group size > 1
|
||||
* thus we have to do a FindChunkInFile to get the
|
||||
* real chunk id.
|
||||
*
|
||||
* We have a duplicate now we need to decide which
|
||||
* one to use:
|
||||
*
|
||||
* Backwards scanning YAFFS2: The old one is what
|
||||
* we use, dump the new one.
|
||||
* YAFFS1: Get both sets of tags and compare serial
|
||||
* numbers.
|
||||
*/
|
||||
|
||||
if (in_scan > 0) {
|
||||
/* Only do this for forward scanning */
|
||||
if (!yaffs_rd_chunk_tags_nand(dev,
|
||||
nand_chunk,
|
||||
NULL, &new_tags))
|
||||
|
||||
{
|
||||
yaffs_trace(YAFFS_TRACE_ERROR, "yaffs_put_chunk_in_file: unhandled error from rd_chunk_tags_nand");
|
||||
}
|
||||
|
||||
/* Do a proper find */
|
||||
existing_cunk =
|
||||
yaffs_find_chunk_in_file(in, inode_chunk,
|
||||
&existing_tags);
|
||||
}
|
||||
|
||||
if (existing_cunk <= 0) {
|
||||
/*Hoosterman - how did this happen? */
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"yaffs tragedy: existing chunk < 0 in scan"
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/* NB The deleted flags should be false, otherwise
|
||||
* the chunks will not be loaded during a scan
|
||||
*/
|
||||
|
||||
if (in_scan > 0) {
|
||||
new_serial = new_tags.serial_number;
|
||||
existing_serial = existing_tags.serial_number;
|
||||
}
|
||||
|
||||
if ((in_scan > 0) &&
|
||||
(existing_cunk <= 0 ||
|
||||
((existing_serial + 1) & 3) == new_serial)) {
|
||||
/* Forward scanning.
|
||||
* Use new
|
||||
* Delete the old one and drop through to
|
||||
* update the tnode
|
||||
*/
|
||||
yaffs_chunk_del(dev, existing_cunk, 1,
|
||||
__LINE__);
|
||||
} else {
|
||||
/* Backward scanning or we want to use the
|
||||
* existing one
|
||||
* Delete the new one and return early so that
|
||||
* the tnode isn't changed
|
||||
*/
|
||||
yaffs_chunk_del(dev, nand_chunk, 1, __LINE__);
|
||||
return YAFFS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (existing_cunk == 0)
|
||||
in->n_data_chunks++;
|
||||
|
||||
yaffs_load_tnode_0(dev, tn, inode_chunk, nand_chunk);
|
||||
|
||||
return YAFFS_OK;
|
||||
}
|
||||
|
||||
static void yaffs_soft_del_chunk(struct yaffs_dev *dev, int chunk)
|
||||
{
|
||||
struct yaffs_block_info *the_block;
|
||||
unsigned block_no;
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_DELETION, "soft delete chunk %d", chunk);
|
||||
|
||||
block_no = chunk / dev->param.chunks_per_block;
|
||||
the_block = yaffs_get_block_info(dev, block_no);
|
||||
if (the_block) {
|
||||
the_block->soft_del_pages++;
|
||||
dev->n_free_chunks++;
|
||||
yaffs2_update_oldest_dirty_seq(dev, block_no, the_block);
|
||||
}
|
||||
}
|
||||
|
||||
/* SoftDeleteWorker scans backwards through the tnode tree and soft deletes all
|
||||
* the chunks in the file.
|
||||
* All soft deleting does is increment the block's softdelete count and pulls
|
||||
* the chunk out of the tnode.
|
||||
* Thus, essentially this is the same as DeleteWorker except that the chunks
|
||||
* are soft deleted.
|
||||
*/
|
||||
|
||||
static int yaffs_soft_del_worker(struct yaffs_obj *in, struct yaffs_tnode *tn,
|
||||
u32 level, int chunk_offset)
|
||||
{
|
||||
int i;
|
||||
int the_chunk;
|
||||
int all_done = 1;
|
||||
struct yaffs_dev *dev = in->my_dev;
|
||||
|
||||
if (!tn)
|
||||
return 1;
|
||||
|
||||
if (level > 0) {
|
||||
for (i = YAFFS_NTNODES_INTERNAL - 1;
|
||||
all_done && i >= 0;
|
||||
i--) {
|
||||
if (tn->internal[i]) {
|
||||
all_done =
|
||||
yaffs_soft_del_worker(in,
|
||||
tn->internal[i],
|
||||
level - 1,
|
||||
(chunk_offset <<
|
||||
YAFFS_TNODES_INTERNAL_BITS)
|
||||
+ i);
|
||||
if (all_done) {
|
||||
yaffs_free_tnode(dev,
|
||||
tn->internal[i]);
|
||||
tn->internal[i] = NULL;
|
||||
} else {
|
||||
/* Can this happen? */
|
||||
}
|
||||
}
|
||||
}
|
||||
return (all_done) ? 1 : 0;
|
||||
}
|
||||
|
||||
/* level 0 */
|
||||
for (i = YAFFS_NTNODES_LEVEL0 - 1; i >= 0; i--) {
|
||||
the_chunk = yaffs_get_group_base(dev, tn, i);
|
||||
if (the_chunk) {
|
||||
yaffs_soft_del_chunk(dev, the_chunk);
|
||||
yaffs_load_tnode_0(dev, tn, i, 0);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void yaffs_remove_obj_from_dir(struct yaffs_obj *obj)
|
||||
{
|
||||
struct yaffs_dev *dev = obj->my_dev;
|
||||
struct yaffs_obj *parent;
|
||||
|
||||
yaffs_verify_obj_in_dir(obj);
|
||||
parent = obj->parent;
|
||||
|
||||
yaffs_verify_dir(parent);
|
||||
|
||||
if (dev && dev->param.remove_obj_fn)
|
||||
dev->param.remove_obj_fn(obj);
|
||||
|
||||
list_del_init(&obj->siblings);
|
||||
obj->parent = NULL;
|
||||
|
||||
yaffs_verify_dir(parent);
|
||||
}
|
||||
|
||||
void yaffs_add_obj_to_dir(struct yaffs_obj *directory, struct yaffs_obj *obj)
|
||||
{
|
||||
if (!directory) {
|
||||
yaffs_trace(YAFFS_TRACE_ALWAYS,
|
||||
"tragedy: Trying to add an object to a null pointer directory"
|
||||
);
|
||||
BUG();
|
||||
return;
|
||||
}
|
||||
if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) {
|
||||
yaffs_trace(YAFFS_TRACE_ALWAYS,
|
||||
"tragedy: Trying to add an object to a non-directory"
|
||||
);
|
||||
BUG();
|
||||
}
|
||||
|
||||
if (obj->siblings.prev == NULL) {
|
||||
/* Not initialised */
|
||||
BUG();
|
||||
}
|
||||
|
||||
yaffs_verify_dir(directory);
|
||||
|
||||
yaffs_remove_obj_from_dir(obj);
|
||||
|
||||
/* Now add it */
|
||||
list_add(&obj->siblings, &directory->variant.dir_variant.children);
|
||||
obj->parent = directory;
|
||||
|
||||
if (directory == obj->my_dev->unlinked_dir
|
||||
|| directory == obj->my_dev->del_dir) {
|
||||
obj->unlinked = 1;
|
||||
obj->my_dev->n_unlinked_files++;
|
||||
obj->rename_allowed = 0;
|
||||
}
|
||||
|
||||
yaffs_verify_dir(directory);
|
||||
yaffs_verify_obj_in_dir(obj);
|
||||
}
|
||||
|
||||
static int yaffs_change_obj_name(struct yaffs_obj *obj,
|
||||
struct yaffs_obj *new_dir,
|
||||
const YCHAR *new_name, int force, int shadows)
|
||||
{
|
||||
int unlink_op;
|
||||
int del_op;
|
||||
struct yaffs_obj *existing_target;
|
||||
|
||||
if (new_dir == NULL)
|
||||
new_dir = obj->parent; /* use the old directory */
|
||||
|
||||
if (new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) {
|
||||
yaffs_trace(YAFFS_TRACE_ALWAYS,
|
||||
"tragedy: yaffs_change_obj_name: new_dir is not a directory"
|
||||
);
|
||||
BUG();
|
||||
}
|
||||
|
||||
unlink_op = (new_dir == obj->my_dev->unlinked_dir);
|
||||
del_op = (new_dir == obj->my_dev->del_dir);
|
||||
|
||||
existing_target = yaffs_find_by_name(new_dir, new_name);
|
||||
|
||||
/* If the object is a file going into the unlinked directory,
|
||||
* then it is OK to just stuff it in since duplicate names are OK.
|
||||
* else only proceed if the new name does not exist and we're putting
|
||||
* it into a directory.
|
||||
*/
|
||||
if (!(unlink_op || del_op || force ||
|
||||
shadows > 0 || !existing_target) ||
|
||||
new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY)
|
||||
return YAFFS_FAIL;
|
||||
|
||||
yaffs_set_obj_name(obj, new_name);
|
||||
obj->dirty = 1;
|
||||
yaffs_add_obj_to_dir(new_dir, obj);
|
||||
|
||||
if (unlink_op)
|
||||
obj->unlinked = 1;
|
||||
|
||||
/* If it is a deletion then we mark it as a shrink for gc */
|
||||
if (yaffs_update_oh(obj, new_name, 0, del_op, shadows, NULL) >= 0)
|
||||
return YAFFS_OK;
|
||||
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
|
||||
/*------------------------ Short Operations Cache ------------------------------
|
||||
* In many situations where there is no high level buffering a lot of
|
||||
* reads might be short sequential reads, and a lot of writes may be short
|
||||
* sequential writes. eg. scanning/writing a jpeg file.
|
||||
* In these cases, a short read/write cache can provide a huge perfomance
|
||||
* benefit with dumb-as-a-rock code.
|
||||
* In Linux, the page cache provides read buffering and the short op cache
|
||||
* provides write buffering.
|
||||
*
|
||||
* There are a small number (~10) of cache chunks per device so that we don't
|
||||
* need a very intelligent search.
|
||||
*/
|
||||
|
||||
static int yaffs_obj_cache_dirty(struct yaffs_obj *obj)
|
||||
{
|
||||
struct yaffs_dev *dev = obj->my_dev;
|
||||
int i;
|
||||
struct yaffs_cache *cache;
|
||||
int n_caches = obj->my_dev->param.n_caches;
|
||||
|
||||
for (i = 0; i < n_caches; i++) {
|
||||
cache = &dev->cache[i];
|
||||
if (cache->object == obj && cache->dirty)
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void yaffs_flush_single_cache(struct yaffs_cache *cache, int discard)
|
||||
{
|
||||
|
||||
if (!cache || cache->locked)
|
||||
return;
|
||||
|
||||
/* Write it out and free it up if need be.*/
|
||||
if (cache->dirty) {
|
||||
yaffs_wr_data_obj(cache->object,
|
||||
cache->chunk_id,
|
||||
cache->data,
|
||||
cache->n_bytes,
|
||||
1);
|
||||
|
||||
cache->dirty = 0;
|
||||
}
|
||||
|
||||
if (discard)
|
||||
cache->object = NULL;
|
||||
}
|
||||
|
||||
static void yaffs_flush_file_cache(struct yaffs_obj *obj, int discard)
|
||||
{
|
||||
struct yaffs_dev *dev = obj->my_dev;
|
||||
int i;
|
||||
struct yaffs_cache *cache;
|
||||
int n_caches = obj->my_dev->param.n_caches;
|
||||
|
||||
if (n_caches < 1)
|
||||
return;
|
||||
|
||||
|
||||
/* Find the chunks for this object and flush them. */
|
||||
for (i = 0; i < n_caches; i++) {
|
||||
cache = &dev->cache[i];
|
||||
if (cache->object == obj)
|
||||
yaffs_flush_single_cache(cache, discard);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void yaffs_flush_whole_cache(struct yaffs_dev *dev, int discard)
|
||||
{
|
||||
struct yaffs_obj *obj;
|
||||
int n_caches = dev->param.n_caches;
|
||||
int i;
|
||||
|
||||
/* Find a dirty object in the cache and flush it...
|
||||
* until there are no further dirty objects.
|
||||
*/
|
||||
do {
|
||||
obj = NULL;
|
||||
for (i = 0; i < n_caches && !obj; i++) {
|
||||
if (dev->cache[i].object && dev->cache[i].dirty)
|
||||
obj = dev->cache[i].object;
|
||||
}
|
||||
if (obj)
|
||||
yaffs_flush_file_cache(obj, discard);
|
||||
} while (obj);
|
||||
|
||||
}
|
||||
|
||||
/* Grab us an unused cache chunk for use.
|
||||
* First look for an empty one.
|
||||
* Then look for the least recently used non-dirty one.
|
||||
* Then look for the least recently used dirty one...., flush and look again.
|
||||
*/
|
||||
static struct yaffs_cache *yaffs_grab_chunk_worker(struct yaffs_dev *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (dev->param.n_caches > 0) {
|
||||
for (i = 0; i < dev->param.n_caches; i++) {
|
||||
if (!dev->cache[i].object)
|
||||
return &dev->cache[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct yaffs_cache *yaffs_grab_chunk_cache(struct yaffs_dev *dev)
|
||||
{
|
||||
struct yaffs_cache *cache;
|
||||
int usage;
|
||||
int i;
|
||||
|
||||
if (dev->param.n_caches < 1)
|
||||
return NULL;
|
||||
|
||||
/* First look for an unused cache */
|
||||
|
||||
cache = yaffs_grab_chunk_worker(dev);
|
||||
|
||||
if (cache)
|
||||
return cache;
|
||||
|
||||
/*
|
||||
* Thery were all in use.
|
||||
* Find the LRU cache and flush it if it is dirty.
|
||||
*/
|
||||
|
||||
usage = -1;
|
||||
cache = NULL;
|
||||
|
||||
for (i = 0; i < dev->param.n_caches; i++) {
|
||||
if (dev->cache[i].object &&
|
||||
!dev->cache[i].locked &&
|
||||
(dev->cache[i].last_use < usage || !cache)) {
|
||||
usage = dev->cache[i].last_use;
|
||||
cache = &dev->cache[i];
|
||||
}
|
||||
}
|
||||
|
||||
#if 1
|
||||
yaffs_flush_single_cache(cache, 1);
|
||||
#else
|
||||
yaffs_flush_file_cache(cache->object, 1);
|
||||
cache = yaffs_grab_chunk_worker(dev);
|
||||
#endif
|
||||
|
||||
return cache;
|
||||
}
|
||||
|
||||
/* Find a cached chunk */
|
||||
static struct yaffs_cache *yaffs_find_chunk_cache(const struct yaffs_obj *obj,
|
||||
int chunk_id)
|
||||
{
|
||||
struct yaffs_dev *dev = obj->my_dev;
|
||||
int i;
|
||||
|
||||
if (dev->param.n_caches < 1)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < dev->param.n_caches; i++) {
|
||||
if (dev->cache[i].object == obj &&
|
||||
dev->cache[i].chunk_id == chunk_id) {
|
||||
dev->cache_hits++;
|
||||
|
||||
return &dev->cache[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Mark the chunk for the least recently used algorithym */
|
||||
static void yaffs_use_cache(struct yaffs_dev *dev, struct yaffs_cache *cache,
|
||||
int is_write)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (dev->param.n_caches < 1)
|
||||
return;
|
||||
|
||||
if (dev->cache_last_use < 0 ||
|
||||
dev->cache_last_use > 100000000) {
|
||||
/* Reset the cache usages */
|
||||
for (i = 1; i < dev->param.n_caches; i++)
|
||||
dev->cache[i].last_use = 0;
|
||||
|
||||
dev->cache_last_use = 0;
|
||||
}
|
||||
dev->cache_last_use++;
|
||||
cache->last_use = dev->cache_last_use;
|
||||
|
||||
if (is_write)
|
||||
cache->dirty = 1;
|
||||
}
|
||||
|
||||
/* Invalidate a single cache page.
|
||||
* Do this when a whole page gets written,
|
||||
* ie the short cache for this page is no longer valid.
|
||||
*/
|
||||
static void yaffs_invalidate_chunk_cache(struct yaffs_obj *object, int chunk_id)
|
||||
{
|
||||
struct yaffs_cache *cache;
|
||||
|
||||
if (object->my_dev->param.n_caches > 0) {
|
||||
cache = yaffs_find_chunk_cache(object, chunk_id);
|
||||
|
||||
if (cache)
|
||||
cache->object = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Invalidate all the cache pages associated with this object
|
||||
* Do this whenever ther file is deleted or resized.
|
||||
*/
|
||||
static void yaffs_invalidate_whole_cache(struct yaffs_obj *in)
|
||||
{
|
||||
int i;
|
||||
struct yaffs_dev *dev = in->my_dev;
|
||||
|
||||
if (dev->param.n_caches > 0) {
|
||||
/* Invalidate it. */
|
||||
for (i = 0; i < dev->param.n_caches; i++) {
|
||||
if (dev->cache[i].object == in)
|
||||
dev->cache[i].object = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void yaffs_unhash_obj(struct yaffs_obj *obj)
|
||||
{
|
||||
int bucket;
|
||||
struct yaffs_dev *dev = obj->my_dev;
|
||||
|
||||
/* If it is still linked into the bucket list, free from the list */
|
||||
if (!list_empty(&obj->hash_link)) {
|
||||
list_del_init(&obj->hash_link);
|
||||
bucket = yaffs_hash_fn(obj->obj_id);
|
||||
dev->obj_bucket[bucket].count--;
|
||||
}
|
||||
}
|
||||
|
||||
/* FreeObject frees up a Object and puts it back on the free list */
|
||||
static void yaffs_free_obj(struct yaffs_obj *obj)
|
||||
{
|
||||
struct yaffs_dev *dev;
|
||||
|
||||
if (!obj) {
|
||||
BUG();
|
||||
return;
|
||||
}
|
||||
dev = obj->my_dev;
|
||||
yaffs_trace(YAFFS_TRACE_OS, "FreeObject %p inode %p",
|
||||
obj, obj->my_inode);
|
||||
if (obj->parent)
|
||||
BUG();
|
||||
if (!list_empty(&obj->siblings))
|
||||
BUG();
|
||||
|
||||
if (obj->my_inode) {
|
||||
/* We're still hooked up to a cached inode.
|
||||
* Don't delete now, but mark for later deletion
|
||||
*/
|
||||
obj->defered_free = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
yaffs_unhash_obj(obj);
|
||||
|
||||
yaffs_free_raw_obj(dev, obj);
|
||||
dev->n_obj--;
|
||||
dev->checkpoint_blocks_required = 0; /* force recalculation */
|
||||
}
|
||||
|
||||
void yaffs_handle_defered_free(struct yaffs_obj *obj)
|
||||
{
|
||||
if (obj->defered_free)
|
||||
yaffs_free_obj(obj);
|
||||
}
|
||||
|
||||
static int yaffs_generic_obj_del(struct yaffs_obj *in)
|
||||
{
|
||||
/* Iinvalidate the file's data in the cache, without flushing. */
|
||||
yaffs_invalidate_whole_cache(in);
|
||||
|
||||
if (in->my_dev->param.is_yaffs2 && in->parent != in->my_dev->del_dir) {
|
||||
/* Move to unlinked directory so we have a deletion record */
|
||||
yaffs_change_obj_name(in, in->my_dev->del_dir, _Y("deleted"), 0,
|
||||
0);
|
||||
}
|
||||
|
||||
yaffs_remove_obj_from_dir(in);
|
||||
yaffs_chunk_del(in->my_dev, in->hdr_chunk, 1, __LINE__);
|
||||
in->hdr_chunk = 0;
|
||||
|
||||
yaffs_free_obj(in);
|
||||
return YAFFS_OK;
|
||||
|
||||
}
|
||||
|
||||
static void yaffs_soft_del_file(struct yaffs_obj *obj)
|
||||
{
|
||||
if (!obj->deleted ||
|
||||
obj->variant_type != YAFFS_OBJECT_TYPE_FILE ||
|
||||
obj->soft_del)
|
||||
return;
|
||||
|
||||
if (obj->n_data_chunks <= 0) {
|
||||
/* Empty file with no duplicate object headers,
|
||||
* just delete it immediately */
|
||||
yaffs_free_tnode(obj->my_dev, obj->variant.file_variant.top);
|
||||
obj->variant.file_variant.top = NULL;
|
||||
yaffs_trace(YAFFS_TRACE_TRACING,
|
||||
"yaffs: Deleting empty file %d",
|
||||
obj->obj_id);
|
||||
yaffs_generic_obj_del(obj);
|
||||
} else {
|
||||
yaffs_soft_del_worker(obj,
|
||||
obj->variant.file_variant.top,
|
||||
obj->variant.
|
||||
file_variant.top_level, 0);
|
||||
obj->soft_del = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Pruning removes any part of the file structure tree that is beyond the
|
||||
* bounds of the file (ie that does not point to chunks).
|
||||
*
|
||||
* A file should only get pruned when its size is reduced.
|
||||
*
|
||||
* Before pruning, the chunks must be pulled from the tree and the
|
||||
* level 0 tnode entries must be zeroed out.
|
||||
* Could also use this for file deletion, but that's probably better handled
|
||||
* by a special case.
|
||||
*
|
||||
* This function is recursive. For levels > 0 the function is called again on
|
||||
* any sub-tree. For level == 0 we just check if the sub-tree has data.
|
||||
* If there is no data in a subtree then it is pruned.
|
||||
*/
|
||||
|
||||
static struct yaffs_tnode *yaffs_prune_worker(struct yaffs_dev *dev,
|
||||
struct yaffs_tnode *tn, u32 level,
|
||||
int del0)
|
||||
{
|
||||
int i;
|
||||
int has_data;
|
||||
|
||||
if (!tn)
|
||||
return tn;
|
||||
|
||||
has_data = 0;
|
||||
|
||||
if (level > 0) {
|
||||
for (i = 0; i < YAFFS_NTNODES_INTERNAL; i++) {
|
||||
if (tn->internal[i]) {
|
||||
tn->internal[i] =
|
||||
yaffs_prune_worker(dev,
|
||||
tn->internal[i],
|
||||
level - 1,
|
||||
(i == 0) ? del0 : 1);
|
||||
}
|
||||
|
||||
if (tn->internal[i])
|
||||
has_data++;
|
||||
}
|
||||
} else {
|
||||
int tnode_size_u32 = dev->tnode_size / sizeof(u32);
|
||||
u32 *map = (u32 *) tn;
|
||||
|
||||
for (i = 0; !has_data && i < tnode_size_u32; i++) {
|
||||
if (map[i])
|
||||
has_data++;
|
||||
}
|
||||
}
|
||||
|
||||
if (has_data == 0 && del0) {
|
||||
/* Free and return NULL */
|
||||
yaffs_free_tnode(dev, tn);
|
||||
tn = NULL;
|
||||
}
|
||||
return tn;
|
||||
}
|
||||
|
||||
static int yaffs_prune_tree(struct yaffs_dev *dev,
|
||||
struct yaffs_file_var *file_struct)
|
||||
{
|
||||
int i;
|
||||
int has_data;
|
||||
int done = 0;
|
||||
struct yaffs_tnode *tn;
|
||||
|
||||
if (file_struct->top_level < 1)
|
||||
return YAFFS_OK;
|
||||
|
||||
file_struct->top =
|
||||
yaffs_prune_worker(dev, file_struct->top, file_struct->top_level, 0);
|
||||
|
||||
/* Now we have a tree with all the non-zero branches NULL but
|
||||
* the height is the same as it was.
|
||||
* Let's see if we can trim internal tnodes to shorten the tree.
|
||||
* We can do this if only the 0th element in the tnode is in use
|
||||
* (ie all the non-zero are NULL)
|
||||
*/
|
||||
|
||||
while (file_struct->top_level && !done) {
|
||||
tn = file_struct->top;
|
||||
|
||||
has_data = 0;
|
||||
for (i = 1; i < YAFFS_NTNODES_INTERNAL; i++) {
|
||||
if (tn->internal[i])
|
||||
has_data++;
|
||||
}
|
||||
|
||||
if (!has_data) {
|
||||
file_struct->top = tn->internal[0];
|
||||
file_struct->top_level--;
|
||||
yaffs_free_tnode(dev, tn);
|
||||
} else {
|
||||
done = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return YAFFS_OK;
|
||||
}
|
||||
|
||||
/*-------------------- End of File Structure functions.-------------------*/
|
||||
|
||||
/* alloc_empty_obj gets us a clean Object.*/
|
||||
static struct yaffs_obj *yaffs_alloc_empty_obj(struct yaffs_dev *dev)
|
||||
{
|
||||
struct yaffs_obj *obj = yaffs_alloc_raw_obj(dev);
|
||||
|
||||
if (!obj)
|
||||
return obj;
|
||||
|
||||
dev->n_obj++;
|
||||
|
||||
/* Now sweeten it up... */
|
||||
|
||||
memset(obj, 0, sizeof(struct yaffs_obj));
|
||||
obj->being_created = 1;
|
||||
|
||||
obj->my_dev = dev;
|
||||
obj->hdr_chunk = 0;
|
||||
obj->variant_type = YAFFS_OBJECT_TYPE_UNKNOWN;
|
||||
INIT_LIST_HEAD(&(obj->hard_links));
|
||||
INIT_LIST_HEAD(&(obj->hash_link));
|
||||
INIT_LIST_HEAD(&obj->siblings);
|
||||
|
||||
/* Now make the directory sane */
|
||||
if (dev->root_dir) {
|
||||
obj->parent = dev->root_dir;
|
||||
list_add(&(obj->siblings),
|
||||
&dev->root_dir->variant.dir_variant.children);
|
||||
}
|
||||
|
||||
/* Add it to the lost and found directory.
|
||||
* NB Can't put root or lost-n-found in lost-n-found so
|
||||
* check if lost-n-found exists first
|
||||
*/
|
||||
if (dev->lost_n_found)
|
||||
yaffs_add_obj_to_dir(dev->lost_n_found, obj);
|
||||
|
||||
obj->being_created = 0;
|
||||
|
||||
dev->checkpoint_blocks_required = 0; /* force recalculation */
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
static int yaffs_find_nice_bucket(struct yaffs_dev *dev)
|
||||
{
|
||||
int i;
|
||||
int l = 999;
|
||||
int lowest = 999999;
|
||||
|
||||
/* Search for the shortest list or one that
|
||||
* isn't too long.
|
||||
*/
|
||||
|
||||
for (i = 0; i < 10 && lowest > 4; i++) {
|
||||
dev->bucket_finder++;
|
||||
dev->bucket_finder %= YAFFS_NOBJECT_BUCKETS;
|
||||
if (dev->obj_bucket[dev->bucket_finder].count < lowest) {
|
||||
lowest = dev->obj_bucket[dev->bucket_finder].count;
|
||||
l = dev->bucket_finder;
|
||||
}
|
||||
}
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
static int yaffs_new_obj_id(struct yaffs_dev *dev)
|
||||
{
|
||||
int bucket = yaffs_find_nice_bucket(dev);
|
||||
int found = 0;
|
||||
struct list_head *i;
|
||||
u32 n = (u32) bucket;
|
||||
|
||||
/* Now find an object value that has not already been taken
|
||||
* by scanning the list.
|
||||
*/
|
||||
|
||||
while (!found) {
|
||||
found = 1;
|
||||
n += YAFFS_NOBJECT_BUCKETS;
|
||||
if (1 || dev->obj_bucket[bucket].count > 0) {
|
||||
list_for_each(i, &dev->obj_bucket[bucket].list) {
|
||||
/* If there is already one in the list */
|
||||
if (i && list_entry(i, struct yaffs_obj,
|
||||
hash_link)->obj_id == n) {
|
||||
found = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
static void yaffs_hash_obj(struct yaffs_obj *in)
|
||||
{
|
||||
int bucket = yaffs_hash_fn(in->obj_id);
|
||||
struct yaffs_dev *dev = in->my_dev;
|
||||
|
||||
list_add(&in->hash_link, &dev->obj_bucket[bucket].list);
|
||||
dev->obj_bucket[bucket].count++;
|
||||
}
|
||||
|
||||
struct yaffs_obj *yaffs_find_by_number(struct yaffs_dev *dev, u32 number)
|
||||
{
|
||||
int bucket = yaffs_hash_fn(number);
|
||||
struct list_head *i;
|
||||
struct yaffs_obj *in;
|
||||
|
||||
list_for_each(i, &dev->obj_bucket[bucket].list) {
|
||||
/* Look if it is in the list */
|
||||
in = list_entry(i, struct yaffs_obj, hash_link);
|
||||
if (in->obj_id == number) {
|
||||
/* Don't show if it is defered free */
|
||||
if (in->defered_free)
|
||||
return NULL;
|
||||
return in;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct yaffs_obj *yaffs_new_obj(struct yaffs_dev *dev, int number,
|
||||
enum yaffs_obj_type type)
|
||||
{
|
||||
struct yaffs_obj *the_obj = NULL;
|
||||
struct yaffs_tnode *tn = NULL;
|
||||
|
||||
if (number < 0)
|
||||
number = yaffs_new_obj_id(dev);
|
||||
|
||||
if (type == YAFFS_OBJECT_TYPE_FILE) {
|
||||
tn = yaffs_get_tnode(dev);
|
||||
if (!tn)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
the_obj = yaffs_alloc_empty_obj(dev);
|
||||
if (!the_obj) {
|
||||
if (tn)
|
||||
yaffs_free_tnode(dev, tn);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
the_obj->fake = 0;
|
||||
the_obj->rename_allowed = 1;
|
||||
the_obj->unlink_allowed = 1;
|
||||
the_obj->obj_id = number;
|
||||
yaffs_hash_obj(the_obj);
|
||||
the_obj->variant_type = type;
|
||||
yaffs_load_current_time(the_obj, 1, 1);
|
||||
|
||||
switch (type) {
|
||||
case YAFFS_OBJECT_TYPE_FILE:
|
||||
the_obj->variant.file_variant.file_size = 0;
|
||||
the_obj->variant.file_variant.scanned_size = 0;
|
||||
the_obj->variant.file_variant.shrink_size =
|
||||
yaffs_max_file_size(dev);
|
||||
the_obj->variant.file_variant.top_level = 0;
|
||||
the_obj->variant.file_variant.top = tn;
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_DIRECTORY:
|
||||
INIT_LIST_HEAD(&the_obj->variant.dir_variant.children);
|
||||
INIT_LIST_HEAD(&the_obj->variant.dir_variant.dirty);
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_SYMLINK:
|
||||
case YAFFS_OBJECT_TYPE_HARDLINK:
|
||||
case YAFFS_OBJECT_TYPE_SPECIAL:
|
||||
/* No action required */
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_UNKNOWN:
|
||||
/* todo this should not happen */
|
||||
break;
|
||||
}
|
||||
return the_obj;
|
||||
}
|
||||
|
||||
static struct yaffs_obj *yaffs_create_fake_dir(struct yaffs_dev *dev,
|
||||
int number, u32 mode)
|
||||
{
|
||||
|
||||
struct yaffs_obj *obj =
|
||||
yaffs_new_obj(dev, number, YAFFS_OBJECT_TYPE_DIRECTORY);
|
||||
|
||||
if (!obj)
|
||||
return NULL;
|
||||
|
||||
obj->fake = 1; /* it is fake so it might not use NAND */
|
||||
obj->rename_allowed = 0;
|
||||
obj->unlink_allowed = 0;
|
||||
obj->deleted = 0;
|
||||
obj->unlinked = 0;
|
||||
obj->yst_mode = mode;
|
||||
obj->my_dev = dev;
|
||||
obj->hdr_chunk = 0; /* Not a valid chunk. */
|
||||
return obj;
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void yaffs_init_tnodes_and_objs(struct yaffs_dev *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
dev->n_obj = 0;
|
||||
dev->n_tnodes = 0;
|
||||
yaffs_init_raw_tnodes_and_objs(dev);
|
||||
|
||||
for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) {
|
||||
INIT_LIST_HEAD(&dev->obj_bucket[i].list);
|
||||
dev->obj_bucket[i].count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
struct yaffs_obj *yaffs_find_or_create_by_number(struct yaffs_dev *dev,
|
||||
int number,
|
||||
enum yaffs_obj_type type)
|
||||
{
|
||||
struct yaffs_obj *the_obj = NULL;
|
||||
|
||||
if (number > 0)
|
||||
the_obj = yaffs_find_by_number(dev, number);
|
||||
|
||||
if (!the_obj)
|
||||
the_obj = yaffs_new_obj(dev, number, type);
|
||||
|
||||
return the_obj;
|
||||
|
||||
}
|
||||
|
||||
YCHAR *yaffs_clone_str(const YCHAR *str)
|
||||
{
|
||||
YCHAR *new_str = NULL;
|
||||
int len;
|
||||
|
||||
if (!str)
|
||||
str = _Y("");
|
||||
|
||||
len = yaffs_strnlen(str, YAFFS_MAX_ALIAS_LENGTH);
|
||||
new_str = kmalloc((len + 1) * sizeof(YCHAR), GFP_NOFS);
|
||||
if (new_str) {
|
||||
yaffs_strncpy(new_str, str, len);
|
||||
new_str[len] = 0;
|
||||
}
|
||||
return new_str;
|
||||
|
||||
}
|
||||
/*
|
||||
*yaffs_update_parent() handles fixing a directories mtime and ctime when a new
|
||||
* link (ie. name) is created or deleted in the directory.
|
||||
*
|
||||
* ie.
|
||||
* create dir/a : update dir's mtime/ctime
|
||||
* rm dir/a: update dir's mtime/ctime
|
||||
* modify dir/a: don't update dir's mtimme/ctime
|
||||
*
|
||||
* This can be handled immediately or defered. Defering helps reduce the number
|
||||
* of updates when many files in a directory are changed within a brief period.
|
||||
*
|
||||
* If the directory updating is defered then yaffs_update_dirty_dirs must be
|
||||
* called periodically.
|
||||
*/
|
||||
|
||||
static void yaffs_update_parent(struct yaffs_obj *obj)
|
||||
{
|
||||
struct yaffs_dev *dev;
|
||||
|
||||
if (!obj)
|
||||
return;
|
||||
dev = obj->my_dev;
|
||||
obj->dirty = 1;
|
||||
yaffs_load_current_time(obj, 0, 1);
|
||||
if (dev->param.defered_dir_update) {
|
||||
struct list_head *link = &obj->variant.dir_variant.dirty;
|
||||
|
||||
if (list_empty(link)) {
|
||||
list_add(link, &dev->dirty_dirs);
|
||||
yaffs_trace(YAFFS_TRACE_BACKGROUND,
|
||||
"Added object %d to dirty directories",
|
||||
obj->obj_id);
|
||||
}
|
||||
|
||||
} else {
|
||||
yaffs_update_oh(obj, NULL, 0, 0, 0, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void yaffs_update_dirty_dirs(struct yaffs_dev *dev)
|
||||
{
|
||||
struct list_head *link;
|
||||
struct yaffs_obj *obj;
|
||||
struct yaffs_dir_var *d_s;
|
||||
union yaffs_obj_var *o_v;
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_BACKGROUND, "Update dirty directories");
|
||||
|
||||
while (!list_empty(&dev->dirty_dirs)) {
|
||||
link = dev->dirty_dirs.next;
|
||||
list_del_init(link);
|
||||
|
||||
d_s = list_entry(link, struct yaffs_dir_var, dirty);
|
||||
o_v = list_entry(d_s, union yaffs_obj_var, dir_variant);
|
||||
obj = list_entry(o_v, struct yaffs_obj, variant);
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_BACKGROUND, "Update directory %d",
|
||||
obj->obj_id);
|
||||
|
||||
if (obj->dirty)
|
||||
yaffs_update_oh(obj, NULL, 0, 0, 0, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Mknod (create) a new object.
|
||||
* equiv_obj only has meaning for a hard link;
|
||||
* alias_str only has meaning for a symlink.
|
||||
* rdev only has meaning for devices (a subset of special objects)
|
||||
*/
|
||||
|
||||
static struct yaffs_obj *yaffs_create_obj(enum yaffs_obj_type type,
|
||||
struct yaffs_obj *parent,
|
||||
const YCHAR *name,
|
||||
u32 mode,
|
||||
u32 uid,
|
||||
u32 gid,
|
||||
struct yaffs_obj *equiv_obj,
|
||||
const YCHAR *alias_str, u32 rdev)
|
||||
{
|
||||
struct yaffs_obj *in;
|
||||
YCHAR *str = NULL;
|
||||
struct yaffs_dev *dev = parent->my_dev;
|
||||
|
||||
/* Check if the entry exists.
|
||||
* If it does then fail the call since we don't want a dup. */
|
||||
if (yaffs_find_by_name(parent, name))
|
||||
return NULL;
|
||||
|
||||
if (type == YAFFS_OBJECT_TYPE_SYMLINK) {
|
||||
str = yaffs_clone_str(alias_str);
|
||||
if (!str)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
in = yaffs_new_obj(dev, -1, type);
|
||||
|
||||
if (!in) {
|
||||
kfree(str);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
in->hdr_chunk = 0;
|
||||
in->valid = 1;
|
||||
in->variant_type = type;
|
||||
|
||||
in->yst_mode = mode;
|
||||
|
||||
yaffs_attribs_init(in, gid, uid, rdev);
|
||||
|
||||
in->n_data_chunks = 0;
|
||||
|
||||
yaffs_set_obj_name(in, name);
|
||||
in->dirty = 1;
|
||||
|
||||
yaffs_add_obj_to_dir(parent, in);
|
||||
|
||||
in->my_dev = parent->my_dev;
|
||||
|
||||
switch (type) {
|
||||
case YAFFS_OBJECT_TYPE_SYMLINK:
|
||||
in->variant.symlink_variant.alias = str;
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_HARDLINK:
|
||||
in->variant.hardlink_variant.equiv_obj = equiv_obj;
|
||||
in->variant.hardlink_variant.equiv_id = equiv_obj->obj_id;
|
||||
list_add(&in->hard_links, &equiv_obj->hard_links);
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_FILE:
|
||||
case YAFFS_OBJECT_TYPE_DIRECTORY:
|
||||
case YAFFS_OBJECT_TYPE_SPECIAL:
|
||||
case YAFFS_OBJECT_TYPE_UNKNOWN:
|
||||
/* do nothing */
|
||||
break;
|
||||
}
|
||||
|
||||
if (yaffs_update_oh(in, name, 0, 0, 0, NULL) < 0) {
|
||||
/* Could not create the object header, fail */
|
||||
yaffs_del_obj(in);
|
||||
in = NULL;
|
||||
}
|
||||
|
||||
if (in)
|
||||
yaffs_update_parent(parent);
|
||||
|
||||
return in;
|
||||
}
|
||||
|
||||
struct yaffs_obj *yaffs_create_file(struct yaffs_obj *parent,
|
||||
const YCHAR *name, u32 mode, u32 uid,
|
||||
u32 gid)
|
||||
{
|
||||
return yaffs_create_obj(YAFFS_OBJECT_TYPE_FILE, parent, name, mode,
|
||||
uid, gid, NULL, NULL, 0);
|
||||
}
|
||||
|
||||
struct yaffs_obj *yaffs_create_dir(struct yaffs_obj *parent, const YCHAR *name,
|
||||
u32 mode, u32 uid, u32 gid)
|
||||
{
|
||||
return yaffs_create_obj(YAFFS_OBJECT_TYPE_DIRECTORY, parent, name,
|
||||
mode, uid, gid, NULL, NULL, 0);
|
||||
}
|
||||
|
||||
struct yaffs_obj *yaffs_create_special(struct yaffs_obj *parent,
|
||||
const YCHAR *name, u32 mode, u32 uid,
|
||||
u32 gid, u32 rdev)
|
||||
{
|
||||
return yaffs_create_obj(YAFFS_OBJECT_TYPE_SPECIAL, parent, name, mode,
|
||||
uid, gid, NULL, NULL, rdev);
|
||||
}
|
||||
|
||||
struct yaffs_obj *yaffs_create_symlink(struct yaffs_obj *parent,
|
||||
const YCHAR *name, u32 mode, u32 uid,
|
||||
u32 gid, const YCHAR *alias)
|
||||
{
|
||||
return yaffs_create_obj(YAFFS_OBJECT_TYPE_SYMLINK, parent, name, mode,
|
||||
uid, gid, NULL, alias, 0);
|
||||
}
|
||||
|
||||
/* yaffs_link_obj returns the object id of the equivalent object.*/
|
||||
struct yaffs_obj *yaffs_link_obj(struct yaffs_obj *parent, const YCHAR * name,
|
||||
struct yaffs_obj *equiv_obj)
|
||||
{
|
||||
/* Get the real object in case we were fed a hard link obj */
|
||||
equiv_obj = yaffs_get_equivalent_obj(equiv_obj);
|
||||
|
||||
if (yaffs_create_obj(YAFFS_OBJECT_TYPE_HARDLINK,
|
||||
parent, name, 0, 0, 0,
|
||||
equiv_obj, NULL, 0))
|
||||
return equiv_obj;
|
||||
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*---------------------- Block Management and Page Allocation -------------*/
|
||||
|
||||
static void yaffs_deinit_blocks(struct yaffs_dev *dev)
|
||||
{
|
||||
if (dev->block_info_alt && dev->block_info)
|
||||
vfree(dev->block_info);
|
||||
else
|
||||
kfree(dev->block_info);
|
||||
|
||||
dev->block_info_alt = 0;
|
||||
|
||||
dev->block_info = NULL;
|
||||
|
||||
if (dev->chunk_bits_alt && dev->chunk_bits)
|
||||
vfree(dev->chunk_bits);
|
||||
else
|
||||
kfree(dev->chunk_bits);
|
||||
dev->chunk_bits_alt = 0;
|
||||
dev->chunk_bits = NULL;
|
||||
}
|
||||
|
||||
static int yaffs_init_blocks(struct yaffs_dev *dev)
|
||||
{
|
||||
int n_blocks = dev->internal_end_block - dev->internal_start_block + 1;
|
||||
|
||||
dev->block_info = NULL;
|
||||
dev->chunk_bits = NULL;
|
||||
dev->alloc_block = -1; /* force it to get a new one */
|
||||
|
||||
/* If the first allocation strategy fails, thry the alternate one */
|
||||
dev->block_info =
|
||||
kmalloc(n_blocks * sizeof(struct yaffs_block_info), GFP_NOFS);
|
||||
if (!dev->block_info) {
|
||||
dev->block_info =
|
||||
vmalloc(n_blocks * sizeof(struct yaffs_block_info));
|
||||
dev->block_info_alt = 1;
|
||||
} else {
|
||||
dev->block_info_alt = 0;
|
||||
}
|
||||
|
||||
if (!dev->block_info)
|
||||
goto alloc_error;
|
||||
|
||||
/* Set up dynamic blockinfo stuff. Round up bytes. */
|
||||
dev->chunk_bit_stride = (dev->param.chunks_per_block + 7) / 8;
|
||||
dev->chunk_bits =
|
||||
kmalloc(dev->chunk_bit_stride * n_blocks, GFP_NOFS);
|
||||
if (!dev->chunk_bits) {
|
||||
dev->chunk_bits =
|
||||
vmalloc(dev->chunk_bit_stride * n_blocks);
|
||||
dev->chunk_bits_alt = 1;
|
||||
} else {
|
||||
dev->chunk_bits_alt = 0;
|
||||
}
|
||||
if (!dev->chunk_bits)
|
||||
goto alloc_error;
|
||||
|
||||
|
||||
memset(dev->block_info, 0, n_blocks * sizeof(struct yaffs_block_info));
|
||||
memset(dev->chunk_bits, 0, dev->chunk_bit_stride * n_blocks);
|
||||
return YAFFS_OK;
|
||||
|
||||
alloc_error:
|
||||
yaffs_deinit_blocks(dev);
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
|
||||
|
||||
void yaffs_block_became_dirty(struct yaffs_dev *dev, int block_no)
|
||||
{
|
||||
struct yaffs_block_info *bi = yaffs_get_block_info(dev, block_no);
|
||||
int erased_ok = 0;
|
||||
int i;
|
||||
|
||||
/* If the block is still healthy erase it and mark as clean.
|
||||
* If the block has had a data failure, then retire it.
|
||||
*/
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_GC | YAFFS_TRACE_ERASE,
|
||||
"yaffs_block_became_dirty block %d state %d %s",
|
||||
block_no, bi->block_state,
|
||||
(bi->needs_retiring) ? "needs retiring" : "");
|
||||
|
||||
yaffs2_clear_oldest_dirty_seq(dev, bi);
|
||||
|
||||
bi->block_state = YAFFS_BLOCK_STATE_DIRTY;
|
||||
|
||||
/* If this is the block being garbage collected then stop gc'ing */
|
||||
if ((u32)block_no == dev->gc_block)
|
||||
dev->gc_block = 0;
|
||||
|
||||
/* If this block is currently the best candidate for gc
|
||||
* then drop as a candidate */
|
||||
if ((u32)block_no == dev->gc_dirtiest) {
|
||||
dev->gc_dirtiest = 0;
|
||||
dev->gc_pages_in_use = 0;
|
||||
}
|
||||
|
||||
if (!bi->needs_retiring) {
|
||||
yaffs2_checkpt_invalidate(dev);
|
||||
erased_ok = yaffs_erase_block(dev, block_no);
|
||||
if (!erased_ok) {
|
||||
dev->n_erase_failures++;
|
||||
yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,
|
||||
"**>> Erasure failed %d", block_no);
|
||||
}
|
||||
}
|
||||
|
||||
/* Verify erasure if needed */
|
||||
if (erased_ok &&
|
||||
((yaffs_trace_mask & YAFFS_TRACE_ERASE) ||
|
||||
!yaffs_skip_verification(dev))) {
|
||||
for (i = 0; i < dev->param.chunks_per_block; i++) {
|
||||
if (!yaffs_check_chunk_erased(dev,
|
||||
block_no * dev->param.chunks_per_block + i)) {
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
">>Block %d erasure supposedly OK, but chunk %d not erased",
|
||||
block_no, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!erased_ok) {
|
||||
/* We lost a block of free space */
|
||||
dev->n_free_chunks -= dev->param.chunks_per_block;
|
||||
yaffs_retire_block(dev, block_no);
|
||||
yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,
|
||||
"**>> Block %d retired", block_no);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Clean it up... */
|
||||
bi->block_state = YAFFS_BLOCK_STATE_EMPTY;
|
||||
bi->seq_number = 0;
|
||||
dev->n_erased_blocks++;
|
||||
bi->pages_in_use = 0;
|
||||
bi->soft_del_pages = 0;
|
||||
bi->has_shrink_hdr = 0;
|
||||
bi->skip_erased_check = 1; /* Clean, so no need to check */
|
||||
bi->gc_prioritise = 0;
|
||||
bi->has_summary = 0;
|
||||
|
||||
yaffs_clear_chunk_bits(dev, block_no);
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_ERASE, "Erased block %d", block_no);
|
||||
}
|
||||
|
||||
static inline int yaffs_gc_process_chunk(struct yaffs_dev *dev,
|
||||
struct yaffs_block_info *bi,
|
||||
int old_chunk, u8 *buffer)
|
||||
{
|
||||
int new_chunk;
|
||||
int mark_flash = 1;
|
||||
struct yaffs_ext_tags tags;
|
||||
struct yaffs_obj *object;
|
||||
int matching_chunk;
|
||||
int ret_val = YAFFS_OK;
|
||||
|
||||
memset(&tags, 0, sizeof(tags));
|
||||
if (!yaffs_rd_chunk_tags_nand(dev, old_chunk,
|
||||
buffer, &tags))
|
||||
|
||||
{
|
||||
yaffs_trace(YAFFS_TRACE_ERROR, "yaffs_gc_process_chunk: unhandled error from rd_chunk_tags_nand");
|
||||
}
|
||||
object = yaffs_find_by_number(dev, tags.obj_id);
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_GC_DETAIL,
|
||||
"Collecting chunk in block %d, %d %d %d ",
|
||||
dev->gc_chunk, tags.obj_id,
|
||||
tags.chunk_id, tags.n_bytes);
|
||||
|
||||
if (object && !yaffs_skip_verification(dev)) {
|
||||
if (tags.chunk_id == 0)
|
||||
matching_chunk =
|
||||
object->hdr_chunk;
|
||||
else if (object->soft_del)
|
||||
/* Defeat the test */
|
||||
matching_chunk = old_chunk;
|
||||
else
|
||||
matching_chunk =
|
||||
yaffs_find_chunk_in_file
|
||||
(object, tags.chunk_id,
|
||||
NULL);
|
||||
|
||||
if (old_chunk != matching_chunk)
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"gc: page in gc mismatch: %d %d %d %d",
|
||||
old_chunk,
|
||||
matching_chunk,
|
||||
tags.obj_id,
|
||||
tags.chunk_id);
|
||||
}
|
||||
|
||||
if (!object) {
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"page %d in gc has no object: %d %d %d ",
|
||||
old_chunk,
|
||||
tags.obj_id, tags.chunk_id,
|
||||
tags.n_bytes);
|
||||
}
|
||||
|
||||
if (object &&
|
||||
object->deleted &&
|
||||
object->soft_del && tags.chunk_id != 0) {
|
||||
/* Data chunk in a soft deleted file,
|
||||
* throw it away.
|
||||
* It's a soft deleted data chunk,
|
||||
* No need to copy this, just forget
|
||||
* about it and fix up the object.
|
||||
*/
|
||||
|
||||
/* Free chunks already includes
|
||||
* softdeleted chunks, how ever this
|
||||
* chunk is going to soon be really
|
||||
* deleted which will increment free
|
||||
* chunks. We have to decrement free
|
||||
* chunks so this works out properly.
|
||||
*/
|
||||
dev->n_free_chunks--;
|
||||
bi->soft_del_pages--;
|
||||
|
||||
object->n_data_chunks--;
|
||||
if (object->n_data_chunks <= 0) {
|
||||
/* remeber to clean up obj */
|
||||
dev->gc_cleanup_list[dev->n_clean_ups] = tags.obj_id;
|
||||
dev->n_clean_ups++;
|
||||
}
|
||||
mark_flash = 0;
|
||||
} else if (object) {
|
||||
/* It's either a data chunk in a live
|
||||
* file or an ObjectHeader, so we're
|
||||
* interested in it.
|
||||
* NB Need to keep the ObjectHeaders of
|
||||
* deleted files until the whole file
|
||||
* has been deleted off
|
||||
*/
|
||||
tags.serial_number++;
|
||||
dev->n_gc_copies++;
|
||||
|
||||
if (tags.chunk_id == 0) {
|
||||
/* It is an object Id,
|
||||
* We need to nuke the
|
||||
* shrinkheader flags since its
|
||||
* work is done.
|
||||
* Also need to clean up
|
||||
* shadowing.
|
||||
*/
|
||||
struct yaffs_obj_hdr *oh;
|
||||
oh = (struct yaffs_obj_hdr *) buffer;
|
||||
|
||||
oh->is_shrink = 0;
|
||||
tags.extra_is_shrink = 0;
|
||||
oh->shadows_obj = 0;
|
||||
oh->inband_shadowed_obj_id = 0;
|
||||
tags.extra_shadows = 0;
|
||||
|
||||
/* Update file size */
|
||||
if (object->variant_type == YAFFS_OBJECT_TYPE_FILE) {
|
||||
yaffs_oh_size_load(oh,
|
||||
object->variant.file_variant.file_size);
|
||||
tags.extra_file_size =
|
||||
object->variant.file_variant.file_size;
|
||||
}
|
||||
|
||||
yaffs_verify_oh(object, oh, &tags, 1);
|
||||
new_chunk =
|
||||
yaffs_write_new_chunk(dev, (u8 *) oh, &tags, 1);
|
||||
} else {
|
||||
new_chunk =
|
||||
yaffs_write_new_chunk(dev, buffer, &tags, 1);
|
||||
}
|
||||
|
||||
if (new_chunk < 0) {
|
||||
ret_val = YAFFS_FAIL;
|
||||
} else {
|
||||
|
||||
/* Now fix up the Tnodes etc. */
|
||||
|
||||
if (tags.chunk_id == 0) {
|
||||
/* It's a header */
|
||||
object->hdr_chunk = new_chunk;
|
||||
object->serial = tags.serial_number;
|
||||
} else {
|
||||
/* It's a data chunk */
|
||||
yaffs_put_chunk_in_file(object, tags.chunk_id,
|
||||
new_chunk, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ret_val == YAFFS_OK)
|
||||
yaffs_chunk_del(dev, old_chunk, mark_flash, __LINE__);
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
static int yaffs_gc_block(struct yaffs_dev *dev, int block, int whole_block)
|
||||
{
|
||||
int old_chunk;
|
||||
int ret_val = YAFFS_OK;
|
||||
u32 i;
|
||||
int is_checkpt_block;
|
||||
int max_copies;
|
||||
int chunks_before = yaffs_get_erased_chunks(dev);
|
||||
int chunks_after;
|
||||
struct yaffs_block_info *bi = yaffs_get_block_info(dev, block);
|
||||
|
||||
is_checkpt_block = (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT);
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_TRACING,
|
||||
"Collecting block %d, in use %d, shrink %d, whole_block %d",
|
||||
block, bi->pages_in_use, bi->has_shrink_hdr,
|
||||
whole_block);
|
||||
|
||||
/*yaffs_verify_free_chunks(dev); */
|
||||
|
||||
if (bi->block_state == YAFFS_BLOCK_STATE_FULL)
|
||||
bi->block_state = YAFFS_BLOCK_STATE_COLLECTING;
|
||||
|
||||
bi->has_shrink_hdr = 0; /* clear the flag so that the block can erase */
|
||||
|
||||
dev->gc_disable = 1;
|
||||
|
||||
yaffs_summary_gc(dev, block);
|
||||
|
||||
if (is_checkpt_block || !yaffs_still_some_chunks(dev, block)) {
|
||||
yaffs_trace(YAFFS_TRACE_TRACING,
|
||||
"Collecting block %d that has no chunks in use",
|
||||
block);
|
||||
yaffs_block_became_dirty(dev, block);
|
||||
} else {
|
||||
|
||||
u8 *buffer = yaffs_get_temp_buffer(dev);
|
||||
|
||||
yaffs_verify_blk(dev, bi, block);
|
||||
|
||||
max_copies = (whole_block) ? dev->param.chunks_per_block : 5;
|
||||
old_chunk = block * dev->param.chunks_per_block + dev->gc_chunk;
|
||||
|
||||
for (/* init already done */ ;
|
||||
ret_val == YAFFS_OK &&
|
||||
dev->gc_chunk < (u32)dev->param.chunks_per_block &&
|
||||
(bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) &&
|
||||
max_copies > 0;
|
||||
dev->gc_chunk++, old_chunk++) {
|
||||
if (yaffs_check_chunk_bit(dev, block, dev->gc_chunk)) {
|
||||
/* Page is in use and might need to be copied */
|
||||
max_copies--;
|
||||
ret_val = yaffs_gc_process_chunk(dev, bi,
|
||||
old_chunk, buffer);
|
||||
}
|
||||
}
|
||||
yaffs_release_temp_buffer(dev, buffer);
|
||||
}
|
||||
|
||||
yaffs_verify_collected_blk(dev, bi, block);
|
||||
|
||||
if (bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) {
|
||||
/*
|
||||
* The gc did not complete. Set block state back to FULL
|
||||
* because checkpointing does not restore gc.
|
||||
*/
|
||||
bi->block_state = YAFFS_BLOCK_STATE_FULL;
|
||||
} else {
|
||||
/* The gc completed. */
|
||||
/* Do any required cleanups */
|
||||
for (i = 0; i < dev->n_clean_ups; i++) {
|
||||
/* Time to delete the file too */
|
||||
struct yaffs_obj *object =
|
||||
yaffs_find_by_number(dev, dev->gc_cleanup_list[i]);
|
||||
if (object) {
|
||||
yaffs_free_tnode(dev,
|
||||
object->variant.file_variant.top);
|
||||
object->variant.file_variant.top = NULL;
|
||||
yaffs_trace(YAFFS_TRACE_GC,
|
||||
"yaffs: About to finally delete object %d",
|
||||
object->obj_id);
|
||||
yaffs_generic_obj_del(object);
|
||||
object->my_dev->n_deleted_files--;
|
||||
}
|
||||
|
||||
}
|
||||
chunks_after = yaffs_get_erased_chunks(dev);
|
||||
if (chunks_before >= chunks_after)
|
||||
yaffs_trace(YAFFS_TRACE_GC,
|
||||
"gc did not increase free chunks before %d after %d",
|
||||
chunks_before, chunks_after);
|
||||
dev->gc_block = 0;
|
||||
dev->gc_chunk = 0;
|
||||
dev->n_clean_ups = 0;
|
||||
}
|
||||
|
||||
dev->gc_disable = 0;
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
/*
|
||||
* find_gc_block() selects the dirtiest block (or close enough)
|
||||
* for garbage collection.
|
||||
*/
|
||||
|
||||
static unsigned yaffs_find_gc_block(struct yaffs_dev *dev,
|
||||
int aggressive, int background)
|
||||
{
|
||||
int i;
|
||||
int iterations;
|
||||
unsigned selected = 0;
|
||||
int prioritised = 0;
|
||||
int prioritised_exist = 0;
|
||||
struct yaffs_block_info *bi;
|
||||
int threshold;
|
||||
|
||||
/* First let's see if we need to grab a prioritised block */
|
||||
if (dev->has_pending_prioritised_gc && !aggressive) {
|
||||
dev->gc_dirtiest = 0;
|
||||
bi = dev->block_info;
|
||||
for (i = dev->internal_start_block;
|
||||
i <= dev->internal_end_block && !selected; i++) {
|
||||
|
||||
if (bi->gc_prioritise) {
|
||||
prioritised_exist = 1;
|
||||
if (bi->block_state == YAFFS_BLOCK_STATE_FULL &&
|
||||
yaffs_block_ok_for_gc(dev, bi)) {
|
||||
selected = i;
|
||||
prioritised = 1;
|
||||
}
|
||||
}
|
||||
bi++;
|
||||
}
|
||||
|
||||
/*
|
||||
* If there is a prioritised block and none was selected then
|
||||
* this happened because there is at least one old dirty block
|
||||
* gumming up the works. Let's gc the oldest dirty block.
|
||||
*/
|
||||
|
||||
if (prioritised_exist &&
|
||||
!selected && dev->oldest_dirty_block > 0)
|
||||
selected = dev->oldest_dirty_block;
|
||||
|
||||
if (!prioritised_exist) /* None found, so we can clear this */
|
||||
dev->has_pending_prioritised_gc = 0;
|
||||
}
|
||||
|
||||
/* If we're doing aggressive GC then we are happy to take a less-dirty
|
||||
* block, and search harder.
|
||||
* else (leasurely gc), then we only bother to do this if the
|
||||
* block has only a few pages in use.
|
||||
*/
|
||||
|
||||
if (!selected) {
|
||||
int pages_used;
|
||||
int n_blocks =
|
||||
dev->internal_end_block - dev->internal_start_block + 1;
|
||||
if (aggressive) {
|
||||
threshold = dev->param.chunks_per_block;
|
||||
iterations = n_blocks;
|
||||
} else {
|
||||
int max_threshold;
|
||||
|
||||
if (background)
|
||||
max_threshold = dev->param.chunks_per_block / 2;
|
||||
else
|
||||
max_threshold = dev->param.chunks_per_block / 8;
|
||||
|
||||
if (max_threshold < YAFFS_GC_PASSIVE_THRESHOLD)
|
||||
max_threshold = YAFFS_GC_PASSIVE_THRESHOLD;
|
||||
|
||||
threshold = background ? (dev->gc_not_done + 2) * 2 : 0;
|
||||
if (threshold < YAFFS_GC_PASSIVE_THRESHOLD)
|
||||
threshold = YAFFS_GC_PASSIVE_THRESHOLD;
|
||||
if (threshold > max_threshold)
|
||||
threshold = max_threshold;
|
||||
|
||||
iterations = n_blocks / 16 + 1;
|
||||
if (iterations > 100)
|
||||
iterations = 100;
|
||||
}
|
||||
|
||||
for (i = 0;
|
||||
i < iterations &&
|
||||
(dev->gc_dirtiest < 1 ||
|
||||
dev->gc_pages_in_use > YAFFS_GC_GOOD_ENOUGH);
|
||||
i++) {
|
||||
dev->gc_block_finder++;
|
||||
if (dev->gc_block_finder < (u32)dev->internal_start_block ||
|
||||
(int)dev->gc_block_finder > dev->internal_end_block)
|
||||
dev->gc_block_finder =
|
||||
dev->internal_start_block;
|
||||
|
||||
bi = yaffs_get_block_info(dev, dev->gc_block_finder);
|
||||
|
||||
pages_used = bi->pages_in_use - bi->soft_del_pages;
|
||||
|
||||
if (bi->block_state == YAFFS_BLOCK_STATE_FULL &&
|
||||
pages_used < dev->param.chunks_per_block &&
|
||||
(dev->gc_dirtiest < 1 ||
|
||||
pages_used < (int)dev->gc_pages_in_use) &&
|
||||
yaffs_block_ok_for_gc(dev, bi)) {
|
||||
dev->gc_dirtiest = dev->gc_block_finder;
|
||||
dev->gc_pages_in_use = pages_used;
|
||||
}
|
||||
}
|
||||
|
||||
if (dev->gc_dirtiest > 0 && dev->gc_pages_in_use <= (u32)threshold)
|
||||
selected = dev->gc_dirtiest;
|
||||
}
|
||||
|
||||
/*
|
||||
* If nothing has been selected for a while, try the oldest dirty
|
||||
* because that's gumming up the works.
|
||||
*/
|
||||
|
||||
if (!selected && dev->param.is_yaffs2 &&
|
||||
dev->gc_not_done >= (background ? 10 : 20)) {
|
||||
yaffs2_find_oldest_dirty_seq(dev);
|
||||
if (dev->oldest_dirty_block > 0) {
|
||||
selected = dev->oldest_dirty_block;
|
||||
dev->gc_dirtiest = selected;
|
||||
dev->oldest_dirty_gc_count++;
|
||||
bi = yaffs_get_block_info(dev, selected);
|
||||
dev->gc_pages_in_use =
|
||||
bi->pages_in_use - bi->soft_del_pages;
|
||||
} else {
|
||||
dev->gc_not_done = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (selected) {
|
||||
yaffs_trace(YAFFS_TRACE_GC,
|
||||
"GC Selected block %d with %d free, prioritised:%d",
|
||||
selected,
|
||||
dev->param.chunks_per_block - dev->gc_pages_in_use,
|
||||
prioritised);
|
||||
|
||||
dev->n_gc_blocks++;
|
||||
if (background)
|
||||
dev->bg_gcs++;
|
||||
|
||||
dev->gc_dirtiest = 0;
|
||||
dev->gc_pages_in_use = 0;
|
||||
dev->gc_not_done = 0;
|
||||
if (dev->refresh_skip > 0)
|
||||
dev->refresh_skip--;
|
||||
} else {
|
||||
dev->gc_not_done++;
|
||||
yaffs_trace(YAFFS_TRACE_GC,
|
||||
"GC none: finder %d skip %d threshold %d dirtiest %d using %d oldest %d%s",
|
||||
dev->gc_block_finder, dev->gc_not_done, threshold,
|
||||
dev->gc_dirtiest, dev->gc_pages_in_use,
|
||||
dev->oldest_dirty_block, background ? " bg" : "");
|
||||
}
|
||||
|
||||
return selected;
|
||||
}
|
||||
|
||||
/* New garbage collector
|
||||
* If we're very low on erased blocks then we do aggressive garbage collection
|
||||
* otherwise we do "leasurely" garbage collection.
|
||||
* Aggressive gc looks further (whole array) and will accept less dirty blocks.
|
||||
* Passive gc only inspects smaller areas and only accepts more dirty blocks.
|
||||
*
|
||||
* The idea is to help clear out space in a more spread-out manner.
|
||||
* Dunno if it really does anything useful.
|
||||
*/
|
||||
static int yaffs_check_gc(struct yaffs_dev *dev, int background)
|
||||
{
|
||||
int aggressive = 0;
|
||||
int gc_ok = YAFFS_OK;
|
||||
int max_tries = 0;
|
||||
int min_erased;
|
||||
int erased_chunks;
|
||||
int checkpt_block_adjust;
|
||||
|
||||
if (dev->param.gc_control_fn &&
|
||||
(dev->param.gc_control_fn(dev) & 1) == 0)
|
||||
return YAFFS_OK;
|
||||
|
||||
if (dev->gc_disable)
|
||||
/* Bail out so we don't get recursive gc */
|
||||
return YAFFS_OK;
|
||||
|
||||
/* This loop should pass the first time.
|
||||
* Only loops here if the collection does not increase space.
|
||||
*/
|
||||
|
||||
do {
|
||||
max_tries++;
|
||||
|
||||
checkpt_block_adjust = yaffs_calc_checkpt_blocks_required(dev);
|
||||
|
||||
min_erased =
|
||||
dev->param.n_reserved_blocks + checkpt_block_adjust + 1;
|
||||
erased_chunks =
|
||||
dev->n_erased_blocks * dev->param.chunks_per_block;
|
||||
|
||||
/* If we need a block soon then do aggressive gc. */
|
||||
if (dev->n_erased_blocks < min_erased)
|
||||
aggressive = 1;
|
||||
else {
|
||||
if (!background
|
||||
&& erased_chunks > (dev->n_free_chunks / 4))
|
||||
break;
|
||||
|
||||
if (dev->gc_skip > 20)
|
||||
dev->gc_skip = 20;
|
||||
if (erased_chunks < dev->n_free_chunks / 2 ||
|
||||
dev->gc_skip < 1 || background)
|
||||
aggressive = 0;
|
||||
else {
|
||||
dev->gc_skip--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
dev->gc_skip = 5;
|
||||
|
||||
/* If we don't already have a block being gc'd then see if we
|
||||
* should start another */
|
||||
|
||||
if (dev->gc_block < 1 && !aggressive) {
|
||||
dev->gc_block = yaffs2_find_refresh_block(dev);
|
||||
dev->gc_chunk = 0;
|
||||
dev->n_clean_ups = 0;
|
||||
}
|
||||
if (dev->gc_block < 1) {
|
||||
dev->gc_block =
|
||||
yaffs_find_gc_block(dev, aggressive, background);
|
||||
dev->gc_chunk = 0;
|
||||
dev->n_clean_ups = 0;
|
||||
}
|
||||
|
||||
if (dev->gc_block > 0) {
|
||||
dev->all_gcs++;
|
||||
if (!aggressive)
|
||||
dev->passive_gc_count++;
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_GC,
|
||||
"yaffs: GC n_erased_blocks %d aggressive %d",
|
||||
dev->n_erased_blocks, aggressive);
|
||||
|
||||
gc_ok = yaffs_gc_block(dev, dev->gc_block, aggressive);
|
||||
}
|
||||
|
||||
if (dev->n_erased_blocks < (dev->param.n_reserved_blocks) &&
|
||||
dev->gc_block > 0) {
|
||||
yaffs_trace(YAFFS_TRACE_GC,
|
||||
"yaffs: GC !!!no reclaim!!! n_erased_blocks %d after try %d block %d",
|
||||
dev->n_erased_blocks, max_tries,
|
||||
dev->gc_block);
|
||||
}
|
||||
} while ((dev->n_erased_blocks < dev->param.n_reserved_blocks) &&
|
||||
(dev->gc_block > 0) && (max_tries < 2));
|
||||
|
||||
return aggressive ? gc_ok : YAFFS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* yaffs_bg_gc()
|
||||
* Garbage collects. Intended to be called from a background thread.
|
||||
* Returns non-zero if at least half the free chunks are erased.
|
||||
*/
|
||||
int yaffs_bg_gc(struct yaffs_dev *dev, unsigned urgency)
|
||||
{
|
||||
int erased_chunks = dev->n_erased_blocks * dev->param.chunks_per_block;
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_BACKGROUND, "Background gc %u", urgency);
|
||||
|
||||
yaffs_check_gc(dev, 1);
|
||||
return erased_chunks > dev->n_free_chunks / 2;
|
||||
}
|
||||
|
||||
/*-------------------- Data file manipulation -----------------*/
|
||||
|
||||
static int yaffs_rd_data_obj(struct yaffs_obj *in, int inode_chunk, u8 * buffer)
|
||||
{
|
||||
int nand_chunk = yaffs_find_chunk_in_file(in, inode_chunk, NULL);
|
||||
|
||||
if (nand_chunk >= 0)
|
||||
return yaffs_rd_chunk_tags_nand(in->my_dev, nand_chunk,
|
||||
buffer, NULL);
|
||||
else {
|
||||
yaffs_trace(YAFFS_TRACE_NANDACCESS,
|
||||
"Chunk %d not found zero instead",
|
||||
nand_chunk);
|
||||
/* get sane (zero) data if you read a hole */
|
||||
memset(buffer, 0, in->my_dev->data_bytes_per_chunk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void yaffs_chunk_del(struct yaffs_dev *dev, int chunk_id, int mark_flash,
|
||||
int lyn)
|
||||
{
|
||||
int block;
|
||||
int page;
|
||||
struct yaffs_ext_tags tags;
|
||||
struct yaffs_block_info *bi;
|
||||
|
||||
if (chunk_id <= 0)
|
||||
return;
|
||||
|
||||
dev->n_deletions++;
|
||||
block = chunk_id / dev->param.chunks_per_block;
|
||||
page = chunk_id % dev->param.chunks_per_block;
|
||||
|
||||
if (!yaffs_check_chunk_bit(dev, block, page))
|
||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
||||
"Deleting invalid chunk %d", chunk_id);
|
||||
|
||||
bi = yaffs_get_block_info(dev, block);
|
||||
|
||||
yaffs2_update_oldest_dirty_seq(dev, block, bi);
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_DELETION,
|
||||
"line %d delete of chunk %d",
|
||||
lyn, chunk_id);
|
||||
|
||||
if (!dev->param.is_yaffs2 && mark_flash &&
|
||||
bi->block_state != YAFFS_BLOCK_STATE_COLLECTING) {
|
||||
|
||||
memset(&tags, 0, sizeof(tags));
|
||||
tags.is_deleted = 1;
|
||||
yaffs_wr_chunk_tags_nand(dev, chunk_id, NULL, &tags);
|
||||
yaffs_handle_chunk_update(dev, chunk_id, &tags);
|
||||
} else {
|
||||
dev->n_unmarked_deletions++;
|
||||
}
|
||||
|
||||
/* Pull out of the management area.
|
||||
* If the whole block became dirty, this will kick off an erasure.
|
||||
*/
|
||||
if (bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING ||
|
||||
bi->block_state == YAFFS_BLOCK_STATE_FULL ||
|
||||
bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN ||
|
||||
bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) {
|
||||
dev->n_free_chunks++;
|
||||
yaffs_clear_chunk_bit(dev, block, page);
|
||||
bi->pages_in_use--;
|
||||
|
||||
if (bi->pages_in_use == 0 &&
|
||||
!bi->has_shrink_hdr &&
|
||||
bi->block_state != YAFFS_BLOCK_STATE_ALLOCATING &&
|
||||
bi->block_state != YAFFS_BLOCK_STATE_NEEDS_SCAN) {
|
||||
yaffs_block_became_dirty(dev, block);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int yaffs_wr_data_obj(struct yaffs_obj *in, int inode_chunk,
|
||||
const u8 *buffer, int n_bytes, int use_reserve)
|
||||
{
|
||||
/* Find old chunk Need to do this to get serial number
|
||||
* Write new one and patch into tree.
|
||||
* Invalidate old tags.
|
||||
*/
|
||||
|
||||
int prev_chunk_id;
|
||||
struct yaffs_ext_tags prev_tags;
|
||||
int new_chunk_id;
|
||||
struct yaffs_ext_tags new_tags;
|
||||
struct yaffs_dev *dev = in->my_dev;
|
||||
|
||||
yaffs_check_gc(dev, 0);
|
||||
|
||||
/* Get the previous chunk at this location in the file if it exists.
|
||||
* If it does not exist then put a zero into the tree. This creates
|
||||
* the tnode now, rather than later when it is harder to clean up.
|
||||
*/
|
||||
prev_chunk_id = yaffs_find_chunk_in_file(in, inode_chunk, &prev_tags);
|
||||
if (prev_chunk_id < 1 &&
|
||||
!yaffs_put_chunk_in_file(in, inode_chunk, 0, 0))
|
||||
return 0;
|
||||
|
||||
/* Set up new tags */
|
||||
memset(&new_tags, 0, sizeof(new_tags));
|
||||
|
||||
new_tags.chunk_id = inode_chunk;
|
||||
new_tags.obj_id = in->obj_id;
|
||||
new_tags.serial_number =
|
||||
(prev_chunk_id > 0) ? prev_tags.serial_number + 1 : 1;
|
||||
new_tags.n_bytes = n_bytes;
|
||||
|
||||
if (n_bytes < 1 || (u32)n_bytes > dev->param.total_bytes_per_chunk) {
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"Writing %d bytes to chunk!!!!!!!!!",
|
||||
n_bytes);
|
||||
BUG();
|
||||
}
|
||||
|
||||
new_chunk_id =
|
||||
yaffs_write_new_chunk(dev, buffer, &new_tags, use_reserve);
|
||||
|
||||
if (new_chunk_id > 0) {
|
||||
yaffs_put_chunk_in_file(in, inode_chunk, new_chunk_id, 0);
|
||||
|
||||
if (prev_chunk_id > 0)
|
||||
yaffs_chunk_del(dev, prev_chunk_id, 1, __LINE__);
|
||||
|
||||
yaffs_verify_file_sane(in);
|
||||
}
|
||||
return new_chunk_id;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int yaffs_do_xattrib_mod(struct yaffs_obj *obj, int set,
|
||||
const YCHAR *name, const void *value, int size,
|
||||
int flags)
|
||||
{
|
||||
struct yaffs_xattr_mod xmod;
|
||||
int result;
|
||||
|
||||
xmod.set = set;
|
||||
xmod.name = name;
|
||||
xmod.data = value;
|
||||
xmod.size = size;
|
||||
xmod.flags = flags;
|
||||
xmod.result = -ENOSPC;
|
||||
|
||||
result = yaffs_update_oh(obj, NULL, 0, 0, 0, &xmod);
|
||||
|
||||
if (result > 0)
|
||||
return xmod.result;
|
||||
else
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
static int yaffs_apply_xattrib_mod(struct yaffs_obj *obj, char *buffer,
|
||||
struct yaffs_xattr_mod *xmod)
|
||||
{
|
||||
int retval = 0;
|
||||
int x_offs = sizeof(struct yaffs_obj_hdr);
|
||||
struct yaffs_dev *dev = obj->my_dev;
|
||||
int x_size = dev->data_bytes_per_chunk - sizeof(struct yaffs_obj_hdr);
|
||||
char *x_buffer = buffer + x_offs;
|
||||
|
||||
if (xmod->set)
|
||||
retval =
|
||||
nval_set(x_buffer, x_size, xmod->name, xmod->data,
|
||||
xmod->size, xmod->flags);
|
||||
else
|
||||
retval = nval_del(x_buffer, x_size, xmod->name);
|
||||
|
||||
obj->has_xattr = nval_hasvalues(x_buffer, x_size);
|
||||
obj->xattr_known = 1;
|
||||
xmod->result = retval;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int yaffs_do_xattrib_fetch(struct yaffs_obj *obj, const YCHAR *name,
|
||||
void *value, int size)
|
||||
{
|
||||
char *buffer = NULL;
|
||||
int result;
|
||||
struct yaffs_ext_tags tags;
|
||||
struct yaffs_dev *dev = obj->my_dev;
|
||||
int x_offs = sizeof(struct yaffs_obj_hdr);
|
||||
int x_size = dev->data_bytes_per_chunk - sizeof(struct yaffs_obj_hdr);
|
||||
char *x_buffer;
|
||||
int retval = 0;
|
||||
|
||||
if (obj->hdr_chunk < 1)
|
||||
return -ENODATA;
|
||||
|
||||
/* If we know that the object has no xattribs then don't do all the
|
||||
* reading and parsing.
|
||||
*/
|
||||
if (obj->xattr_known && !obj->has_xattr) {
|
||||
if (name)
|
||||
return -ENODATA;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
buffer = (char *)yaffs_get_temp_buffer(dev);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
result =
|
||||
yaffs_rd_chunk_tags_nand(dev, obj->hdr_chunk, (u8 *) buffer, &tags);
|
||||
|
||||
if (result != YAFFS_OK)
|
||||
retval = -ENOENT;
|
||||
else {
|
||||
x_buffer = buffer + x_offs;
|
||||
|
||||
if (!obj->xattr_known) {
|
||||
obj->has_xattr = nval_hasvalues(x_buffer, x_size);
|
||||
obj->xattr_known = 1;
|
||||
}
|
||||
|
||||
if (name)
|
||||
retval = nval_get(x_buffer, x_size, name, value, size);
|
||||
else
|
||||
retval = nval_list(x_buffer, x_size, value, size);
|
||||
}
|
||||
yaffs_release_temp_buffer(dev, (u8 *) buffer);
|
||||
return retval;
|
||||
}
|
||||
|
||||
int yaffs_set_xattrib(struct yaffs_obj *obj, const YCHAR * name,
|
||||
const void *value, int size, int flags)
|
||||
{
|
||||
return yaffs_do_xattrib_mod(obj, 1, name, value, size, flags);
|
||||
}
|
||||
|
||||
int yaffs_remove_xattrib(struct yaffs_obj *obj, const YCHAR * name)
|
||||
{
|
||||
return yaffs_do_xattrib_mod(obj, 0, name, NULL, 0, 0);
|
||||
}
|
||||
|
||||
int yaffs_get_xattrib(struct yaffs_obj *obj, const YCHAR * name, void *value,
|
||||
int size)
|
||||
{
|
||||
return yaffs_do_xattrib_fetch(obj, name, value, size);
|
||||
}
|
||||
|
||||
int yaffs_list_xattrib(struct yaffs_obj *obj, char *buffer, int size)
|
||||
{
|
||||
return yaffs_do_xattrib_fetch(obj, NULL, buffer, size);
|
||||
}
|
||||
|
||||
static void yaffs_check_obj_details_loaded(struct yaffs_obj *in)
|
||||
{
|
||||
u8 *buf;
|
||||
struct yaffs_obj_hdr *oh;
|
||||
struct yaffs_dev *dev;
|
||||
struct yaffs_ext_tags tags;
|
||||
//int result;
|
||||
//int alloc_failed = 0;
|
||||
|
||||
if (!in || !in->lazy_loaded || in->hdr_chunk < 1)
|
||||
return;
|
||||
|
||||
dev = in->my_dev;
|
||||
in->lazy_loaded = 0;
|
||||
buf = yaffs_get_temp_buffer(dev);
|
||||
|
||||
if (!yaffs_rd_chunk_tags_nand(dev, in->hdr_chunk, buf, &tags))
|
||||
{
|
||||
yaffs_trace(YAFFS_TRACE_ERROR, "yaffs_check_obj_details_loaded: unhandled error from rd_chunk_tags_nand");
|
||||
}
|
||||
oh = (struct yaffs_obj_hdr *)buf;
|
||||
|
||||
in->yst_mode = oh->yst_mode;
|
||||
yaffs_load_attribs(in, oh);
|
||||
yaffs_set_obj_name_from_oh(in, oh);
|
||||
|
||||
if (in->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) {
|
||||
in->variant.symlink_variant.alias =
|
||||
yaffs_clone_str(oh->alias);
|
||||
if (!in->variant.symlink_variant.alias)
|
||||
{
|
||||
// alloc_failed = 1;
|
||||
yaffs_trace(YAFFS_TRACE_ERROR, "yaffs_check_obj_details_loaded: alloc_failed = 1??");
|
||||
}
|
||||
}
|
||||
yaffs_release_temp_buffer(dev, buf);
|
||||
}
|
||||
|
||||
/* UpdateObjectHeader updates the header on NAND for an object.
|
||||
* If name is not NULL, then that new name is used.
|
||||
*/
|
||||
int yaffs_update_oh(struct yaffs_obj *in, const YCHAR *name, int force,
|
||||
int is_shrink, int shadows, struct yaffs_xattr_mod *xmod)
|
||||
{
|
||||
|
||||
struct yaffs_block_info *bi;
|
||||
struct yaffs_dev *dev = in->my_dev;
|
||||
int prev_chunk_id;
|
||||
int ret_val = 0;
|
||||
//int result = 0;
|
||||
int new_chunk_id;
|
||||
struct yaffs_ext_tags new_tags;
|
||||
struct yaffs_ext_tags old_tags;
|
||||
const YCHAR *alias = NULL;
|
||||
u8 *buffer = NULL;
|
||||
YCHAR old_name[YAFFS_MAX_NAME_LENGTH + 1];
|
||||
struct yaffs_obj_hdr *oh = NULL;
|
||||
Y_LOFF_T file_size = 0;
|
||||
|
||||
yaffs_strcpy(old_name, _Y("silly old name"));
|
||||
|
||||
if (in->fake && in != dev->root_dir && !force && !xmod)
|
||||
return ret_val;
|
||||
|
||||
yaffs_check_gc(dev, 0);
|
||||
yaffs_check_obj_details_loaded(in);
|
||||
|
||||
buffer = yaffs_get_temp_buffer(in->my_dev);
|
||||
oh = (struct yaffs_obj_hdr *)buffer;
|
||||
|
||||
prev_chunk_id = in->hdr_chunk;
|
||||
|
||||
if (prev_chunk_id > 0) {
|
||||
if (!yaffs_rd_chunk_tags_nand(dev, prev_chunk_id, buffer, &old_tags))
|
||||
{
|
||||
yaffs_trace(YAFFS_TRACE_ERROR, "yaffs_update_oh: unhandled error from rd_chunk_tags_nand");
|
||||
}
|
||||
|
||||
yaffs_verify_oh(in, oh, &old_tags, 0);
|
||||
memcpy(old_name, oh->name, sizeof(oh->name));
|
||||
memset(buffer, 0xff, sizeof(struct yaffs_obj_hdr));
|
||||
} else {
|
||||
memset(buffer, 0xff, dev->data_bytes_per_chunk);
|
||||
}
|
||||
|
||||
oh->type = in->variant_type;
|
||||
oh->yst_mode = in->yst_mode;
|
||||
oh->shadows_obj = oh->inband_shadowed_obj_id = shadows;
|
||||
|
||||
yaffs_load_attribs_oh(oh, in);
|
||||
|
||||
if (in->parent)
|
||||
oh->parent_obj_id = in->parent->obj_id;
|
||||
else
|
||||
oh->parent_obj_id = 0;
|
||||
|
||||
if (name && *name) {
|
||||
memset(oh->name, 0, sizeof(oh->name));
|
||||
yaffs_load_oh_from_name(dev, oh->name, name);
|
||||
} else if (prev_chunk_id > 0) {
|
||||
memcpy(oh->name, old_name, sizeof(oh->name));
|
||||
} else {
|
||||
memset(oh->name, 0, sizeof(oh->name));
|
||||
}
|
||||
|
||||
oh->is_shrink = is_shrink;
|
||||
|
||||
switch (in->variant_type) {
|
||||
case YAFFS_OBJECT_TYPE_UNKNOWN:
|
||||
/* Should not happen */
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_FILE:
|
||||
if (oh->parent_obj_id != YAFFS_OBJECTID_DELETED &&
|
||||
oh->parent_obj_id != YAFFS_OBJECTID_UNLINKED)
|
||||
file_size = in->variant.file_variant.file_size;
|
||||
yaffs_oh_size_load(oh, file_size);
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_HARDLINK:
|
||||
oh->equiv_id = in->variant.hardlink_variant.equiv_id;
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_SPECIAL:
|
||||
/* Do nothing */
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_DIRECTORY:
|
||||
/* Do nothing */
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_SYMLINK:
|
||||
alias = in->variant.symlink_variant.alias;
|
||||
if (!alias)
|
||||
alias = _Y("no alias");
|
||||
yaffs_strncpy(oh->alias, alias, YAFFS_MAX_ALIAS_LENGTH);
|
||||
oh->alias[YAFFS_MAX_ALIAS_LENGTH] = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/* process any xattrib modifications */
|
||||
if (xmod)
|
||||
yaffs_apply_xattrib_mod(in, (char *)buffer, xmod);
|
||||
|
||||
/* Tags */
|
||||
memset(&new_tags, 0, sizeof(new_tags));
|
||||
in->serial++;
|
||||
new_tags.chunk_id = 0;
|
||||
new_tags.obj_id = in->obj_id;
|
||||
new_tags.serial_number = in->serial;
|
||||
|
||||
/* Add extra info for file header */
|
||||
new_tags.extra_available = 1;
|
||||
new_tags.extra_parent_id = oh->parent_obj_id;
|
||||
new_tags.extra_file_size = file_size;
|
||||
new_tags.extra_is_shrink = oh->is_shrink;
|
||||
new_tags.extra_equiv_id = oh->equiv_id;
|
||||
new_tags.extra_shadows = (oh->shadows_obj > 0) ? 1 : 0;
|
||||
new_tags.extra_obj_type = in->variant_type;
|
||||
yaffs_verify_oh(in, oh, &new_tags, 1);
|
||||
|
||||
/* Create new chunk in NAND */
|
||||
new_chunk_id =
|
||||
yaffs_write_new_chunk(dev, buffer, &new_tags,
|
||||
(prev_chunk_id > 0) ? 1 : 0);
|
||||
|
||||
if (buffer)
|
||||
yaffs_release_temp_buffer(dev, buffer);
|
||||
|
||||
if (new_chunk_id < 0)
|
||||
return new_chunk_id;
|
||||
|
||||
in->hdr_chunk = new_chunk_id;
|
||||
|
||||
if (prev_chunk_id > 0)
|
||||
yaffs_chunk_del(dev, prev_chunk_id, 1, __LINE__);
|
||||
|
||||
if (!yaffs_obj_cache_dirty(in))
|
||||
in->dirty = 0;
|
||||
|
||||
/* If this was a shrink, then mark the block
|
||||
* that the chunk lives on */
|
||||
if (is_shrink) {
|
||||
bi = yaffs_get_block_info(in->my_dev,
|
||||
new_chunk_id /
|
||||
in->my_dev->param.chunks_per_block);
|
||||
bi->has_shrink_hdr = 1;
|
||||
}
|
||||
|
||||
|
||||
return new_chunk_id;
|
||||
}
|
||||
|
||||
/*--------------------- File read/write ------------------------
|
||||
* Read and write have very similar structures.
|
||||
* In general the read/write has three parts to it
|
||||
* An incomplete chunk to start with (if the read/write is not chunk-aligned)
|
||||
* Some complete chunks
|
||||
* An incomplete chunk to end off with
|
||||
*
|
||||
* Curve-balls: the first chunk might also be the last chunk.
|
||||
*/
|
||||
|
||||
int yaffs_file_rd(struct yaffs_obj *in, u8 * buffer, Y_LOFF_T offset, int n_bytes)
|
||||
{
|
||||
int chunk;
|
||||
u32 start;
|
||||
int n_copy;
|
||||
int n = n_bytes;
|
||||
int n_done = 0;
|
||||
struct yaffs_cache *cache;
|
||||
struct yaffs_dev *dev;
|
||||
|
||||
dev = in->my_dev;
|
||||
|
||||
while (n > 0) {
|
||||
yaffs_addr_to_chunk(dev, offset, &chunk, &start);
|
||||
chunk++;
|
||||
|
||||
/* OK now check for the curveball where the start and end are in
|
||||
* the same chunk.
|
||||
*/
|
||||
if ((start + n) < (u32)dev->data_bytes_per_chunk)
|
||||
n_copy = n;
|
||||
else
|
||||
n_copy = dev->data_bytes_per_chunk - start;
|
||||
|
||||
cache = yaffs_find_chunk_cache(in, chunk);
|
||||
|
||||
/* If the chunk is already in the cache or it is less than
|
||||
* a whole chunk or we're using inband tags then use the cache
|
||||
* (if there is caching) else bypass the cache.
|
||||
*/
|
||||
if (cache || n_copy != dev->data_bytes_per_chunk ||
|
||||
dev->param.inband_tags) {
|
||||
if (dev->param.n_caches > 0) {
|
||||
|
||||
/* If we can't find the data in the cache,
|
||||
* then load it up. */
|
||||
|
||||
if (!cache) {
|
||||
cache =
|
||||
yaffs_grab_chunk_cache(in->my_dev);
|
||||
cache->object = in;
|
||||
cache->chunk_id = chunk;
|
||||
cache->dirty = 0;
|
||||
cache->locked = 0;
|
||||
yaffs_rd_data_obj(in, chunk,
|
||||
cache->data);
|
||||
cache->n_bytes = 0;
|
||||
}
|
||||
|
||||
yaffs_use_cache(dev, cache, 0);
|
||||
|
||||
cache->locked = 1;
|
||||
|
||||
memcpy(buffer, &cache->data[start], n_copy);
|
||||
|
||||
cache->locked = 0;
|
||||
} else {
|
||||
/* Read into the local buffer then copy.. */
|
||||
|
||||
u8 *local_buffer =
|
||||
yaffs_get_temp_buffer(dev);
|
||||
yaffs_rd_data_obj(in, chunk, local_buffer);
|
||||
|
||||
memcpy(buffer, &local_buffer[start], n_copy);
|
||||
|
||||
yaffs_release_temp_buffer(dev, local_buffer);
|
||||
}
|
||||
} else {
|
||||
/* A full chunk. Read directly into the buffer. */
|
||||
yaffs_rd_data_obj(in, chunk, buffer);
|
||||
}
|
||||
n -= n_copy;
|
||||
offset += n_copy;
|
||||
buffer += n_copy;
|
||||
n_done += n_copy;
|
||||
}
|
||||
return n_done;
|
||||
}
|
||||
|
||||
int yaffs_do_file_wr(struct yaffs_obj *in, const u8 *buffer, Y_LOFF_T offset,
|
||||
int n_bytes, int write_through)
|
||||
{
|
||||
|
||||
int chunk;
|
||||
u32 start;
|
||||
int n_copy;
|
||||
int n = n_bytes;
|
||||
int n_done = 0;
|
||||
int n_writeback;
|
||||
Y_LOFF_T start_write = offset;
|
||||
int chunk_written = 0;
|
||||
u32 n_bytes_read;
|
||||
Y_LOFF_T chunk_start;
|
||||
struct yaffs_dev *dev;
|
||||
|
||||
dev = in->my_dev;
|
||||
|
||||
while (n > 0 && chunk_written >= 0) {
|
||||
yaffs_addr_to_chunk(dev, offset, &chunk, &start);
|
||||
|
||||
if (((Y_LOFF_T)chunk) *
|
||||
(s32)(dev->data_bytes_per_chunk + start) != offset ||
|
||||
(s32)start >= dev->data_bytes_per_chunk) {
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"AddrToChunk of offset %ud gives chunk %d start %d",
|
||||
(unsigned int)offset, chunk, start);
|
||||
}
|
||||
chunk++; /* File pos to chunk in file offset */
|
||||
|
||||
/* OK now check for the curveball where the start and end are in
|
||||
* the same chunk.
|
||||
*/
|
||||
|
||||
if ((start + n) < (u32)dev->data_bytes_per_chunk) {
|
||||
n_copy = n;
|
||||
|
||||
/* Now calculate how many bytes to write back....
|
||||
* If we're overwriting and not writing to then end of
|
||||
* file then we need to write back as much as was there
|
||||
* before.
|
||||
*/
|
||||
|
||||
chunk_start = (((Y_LOFF_T)(chunk - 1)) *
|
||||
dev->data_bytes_per_chunk);
|
||||
|
||||
if (chunk_start > in->variant.file_variant.file_size)
|
||||
n_bytes_read = 0; /* Past end of file */
|
||||
else
|
||||
n_bytes_read =
|
||||
in->variant.file_variant.file_size -
|
||||
chunk_start;
|
||||
|
||||
if ((int)n_bytes_read > dev->data_bytes_per_chunk)
|
||||
n_bytes_read = dev->data_bytes_per_chunk;
|
||||
|
||||
n_writeback =
|
||||
(n_bytes_read >
|
||||
(start + n)) ? n_bytes_read : (start + n);
|
||||
|
||||
if (n_writeback < 0 ||
|
||||
n_writeback > dev->data_bytes_per_chunk)
|
||||
BUG();
|
||||
|
||||
} else {
|
||||
n_copy = dev->data_bytes_per_chunk - start;
|
||||
n_writeback = dev->data_bytes_per_chunk;
|
||||
}
|
||||
|
||||
if (n_copy != dev->data_bytes_per_chunk ||
|
||||
!dev->param.cache_bypass_aligned ||
|
||||
dev->param.inband_tags) {
|
||||
/* An incomplete start or end chunk (or maybe both
|
||||
* start and end chunk), or we're using inband tags,
|
||||
* or we're forcing writes through the cache,
|
||||
* so we want to use the cache buffers.
|
||||
*/
|
||||
if (dev->param.n_caches > 0) {
|
||||
struct yaffs_cache *cache;
|
||||
|
||||
/* If we can't find the data in the cache, then
|
||||
* load the cache */
|
||||
cache = yaffs_find_chunk_cache(in, chunk);
|
||||
|
||||
if (!cache &&
|
||||
yaffs_check_alloc_available(dev, 1)) {
|
||||
cache = yaffs_grab_chunk_cache(dev);
|
||||
cache->object = in;
|
||||
cache->chunk_id = chunk;
|
||||
cache->dirty = 0;
|
||||
cache->locked = 0;
|
||||
yaffs_rd_data_obj(in, chunk,
|
||||
cache->data);
|
||||
} else if (cache &&
|
||||
!cache->dirty &&
|
||||
!yaffs_check_alloc_available(dev,
|
||||
1)) {
|
||||
/* Drop the cache if it was a read cache
|
||||
* item and no space check has been made
|
||||
* for it.
|
||||
*/
|
||||
cache = NULL;
|
||||
}
|
||||
|
||||
if (cache) {
|
||||
yaffs_use_cache(dev, cache, 1);
|
||||
cache->locked = 1;
|
||||
|
||||
memcpy(&cache->data[start], buffer,
|
||||
n_copy);
|
||||
|
||||
cache->locked = 0;
|
||||
cache->n_bytes = n_writeback;
|
||||
|
||||
if (write_through) {
|
||||
chunk_written =
|
||||
yaffs_wr_data_obj
|
||||
(cache->object,
|
||||
cache->chunk_id,
|
||||
cache->data,
|
||||
cache->n_bytes, 1);
|
||||
cache->dirty = 0;
|
||||
}
|
||||
} else {
|
||||
chunk_written = -1; /* fail write */
|
||||
}
|
||||
} else {
|
||||
/* An incomplete start or end chunk (or maybe
|
||||
* both start and end chunk). Read into the
|
||||
* local buffer then copy over and write back.
|
||||
*/
|
||||
|
||||
u8 *local_buffer = yaffs_get_temp_buffer(dev);
|
||||
|
||||
yaffs_rd_data_obj(in, chunk, local_buffer);
|
||||
memcpy(&local_buffer[start], buffer, n_copy);
|
||||
|
||||
chunk_written =
|
||||
yaffs_wr_data_obj(in, chunk,
|
||||
local_buffer,
|
||||
n_writeback, 0);
|
||||
|
||||
yaffs_release_temp_buffer(dev, local_buffer);
|
||||
}
|
||||
} else {
|
||||
/* A full chunk. Write directly from the buffer. */
|
||||
|
||||
chunk_written =
|
||||
yaffs_wr_data_obj(in, chunk, buffer,
|
||||
dev->data_bytes_per_chunk, 0);
|
||||
|
||||
/* Since we've overwritten the cached data,
|
||||
* we better invalidate it. */
|
||||
yaffs_invalidate_chunk_cache(in, chunk);
|
||||
}
|
||||
|
||||
if (chunk_written >= 0) {
|
||||
n -= n_copy;
|
||||
offset += n_copy;
|
||||
buffer += n_copy;
|
||||
n_done += n_copy;
|
||||
}
|
||||
}
|
||||
|
||||
/* Update file object */
|
||||
|
||||
if ((start_write + n_done) > in->variant.file_variant.file_size)
|
||||
in->variant.file_variant.file_size = (start_write + n_done);
|
||||
|
||||
in->dirty = 1;
|
||||
return n_done;
|
||||
}
|
||||
|
||||
int yaffs_wr_file(struct yaffs_obj *in, const u8 *buffer, Y_LOFF_T offset,
|
||||
int n_bytes, int write_through)
|
||||
{
|
||||
yaffs2_handle_hole(in, offset);
|
||||
return yaffs_do_file_wr(in, buffer, offset, n_bytes, write_through);
|
||||
}
|
||||
|
||||
/* ---------------------- File resizing stuff ------------------ */
|
||||
|
||||
static void yaffs_prune_chunks(struct yaffs_obj *in, Y_LOFF_T new_size)
|
||||
{
|
||||
|
||||
struct yaffs_dev *dev = in->my_dev;
|
||||
Y_LOFF_T old_size = in->variant.file_variant.file_size;
|
||||
int i;
|
||||
int chunk_id;
|
||||
u32 dummy;
|
||||
int last_del;
|
||||
int start_del;
|
||||
|
||||
if (old_size > 0)
|
||||
yaffs_addr_to_chunk(dev, old_size - 1, &last_del, &dummy);
|
||||
else
|
||||
last_del = 0;
|
||||
|
||||
yaffs_addr_to_chunk(dev, new_size + dev->data_bytes_per_chunk - 1,
|
||||
&start_del, &dummy);
|
||||
last_del++;
|
||||
start_del++;
|
||||
|
||||
/* Delete backwards so that we don't end up with holes if
|
||||
* power is lost part-way through the operation.
|
||||
*/
|
||||
for (i = last_del; i >= start_del; i--) {
|
||||
/* NB this could be optimised somewhat,
|
||||
* eg. could retrieve the tags and write them without
|
||||
* using yaffs_chunk_del
|
||||
*/
|
||||
|
||||
chunk_id = yaffs_find_del_file_chunk(in, i, NULL);
|
||||
|
||||
if (chunk_id < 1)
|
||||
continue;
|
||||
|
||||
if (chunk_id <
|
||||
(dev->internal_start_block * dev->param.chunks_per_block) ||
|
||||
chunk_id >=
|
||||
((dev->internal_end_block + 1) *
|
||||
dev->param.chunks_per_block)) {
|
||||
yaffs_trace(YAFFS_TRACE_ALWAYS,
|
||||
"Found daft chunk_id %d for %d",
|
||||
chunk_id, i);
|
||||
} else {
|
||||
in->n_data_chunks--;
|
||||
yaffs_chunk_del(dev, chunk_id, 1, __LINE__);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void yaffs_resize_file_down(struct yaffs_obj *obj, Y_LOFF_T new_size)
|
||||
{
|
||||
int new_full;
|
||||
u32 new_partial;
|
||||
struct yaffs_dev *dev = obj->my_dev;
|
||||
|
||||
yaffs_addr_to_chunk(dev, new_size, &new_full, &new_partial);
|
||||
|
||||
yaffs_prune_chunks(obj, new_size);
|
||||
|
||||
if (new_partial != 0) {
|
||||
int last_chunk = 1 + new_full;
|
||||
u8 *local_buffer = yaffs_get_temp_buffer(dev);
|
||||
|
||||
/* Rewrite the last chunk with its new size and zero pad */
|
||||
yaffs_rd_data_obj(obj, last_chunk, local_buffer);
|
||||
memset(local_buffer + new_partial, 0,
|
||||
dev->data_bytes_per_chunk - new_partial);
|
||||
|
||||
yaffs_wr_data_obj(obj, last_chunk, local_buffer,
|
||||
new_partial, 1);
|
||||
|
||||
yaffs_release_temp_buffer(dev, local_buffer);
|
||||
}
|
||||
|
||||
obj->variant.file_variant.file_size = new_size;
|
||||
|
||||
yaffs_prune_tree(dev, &obj->variant.file_variant);
|
||||
}
|
||||
|
||||
int yaffs_resize_file(struct yaffs_obj *in, Y_LOFF_T new_size)
|
||||
{
|
||||
struct yaffs_dev *dev = in->my_dev;
|
||||
Y_LOFF_T old_size = in->variant.file_variant.file_size;
|
||||
|
||||
yaffs_flush_file_cache(in, 1);
|
||||
yaffs_invalidate_whole_cache(in);
|
||||
|
||||
yaffs_check_gc(dev, 0);
|
||||
|
||||
if (in->variant_type != YAFFS_OBJECT_TYPE_FILE)
|
||||
return YAFFS_FAIL;
|
||||
|
||||
if (new_size == old_size)
|
||||
return YAFFS_OK;
|
||||
|
||||
if (new_size > old_size) {
|
||||
yaffs2_handle_hole(in, new_size);
|
||||
in->variant.file_variant.file_size = new_size;
|
||||
} else {
|
||||
/* new_size < old_size */
|
||||
yaffs_resize_file_down(in, new_size);
|
||||
}
|
||||
|
||||
/* Write a new object header to reflect the resize.
|
||||
* show we've shrunk the file, if need be
|
||||
* Do this only if the file is not in the deleted directories
|
||||
* and is not shadowed.
|
||||
*/
|
||||
if (in->parent &&
|
||||
!in->is_shadowed &&
|
||||
in->parent->obj_id != YAFFS_OBJECTID_UNLINKED &&
|
||||
in->parent->obj_id != YAFFS_OBJECTID_DELETED)
|
||||
yaffs_update_oh(in, NULL, 0, 0, 0, NULL);
|
||||
|
||||
return YAFFS_OK;
|
||||
}
|
||||
|
||||
int yaffs_flush_file(struct yaffs_obj *in,
|
||||
int update_time,
|
||||
int data_sync,
|
||||
int discard_cache)
|
||||
{
|
||||
if (!in->dirty)
|
||||
return YAFFS_OK;
|
||||
|
||||
yaffs_flush_file_cache(in, discard_cache);
|
||||
|
||||
if (data_sync)
|
||||
return YAFFS_OK;
|
||||
|
||||
if (update_time)
|
||||
yaffs_load_current_time(in, 0, 0);
|
||||
|
||||
return (yaffs_update_oh(in, NULL, 0, 0, 0, NULL) >= 0) ?
|
||||
YAFFS_OK : YAFFS_FAIL;
|
||||
}
|
||||
|
||||
|
||||
/* yaffs_del_file deletes the whole file data
|
||||
* and the inode associated with the file.
|
||||
* It does not delete the links associated with the file.
|
||||
*/
|
||||
static int yaffs_unlink_file_if_needed(struct yaffs_obj *in)
|
||||
{
|
||||
int ret_val;
|
||||
int del_now = 0;
|
||||
struct yaffs_dev *dev = in->my_dev;
|
||||
|
||||
if (!in->my_inode)
|
||||
del_now = 1;
|
||||
|
||||
if (del_now) {
|
||||
ret_val =
|
||||
yaffs_change_obj_name(in, in->my_dev->del_dir,
|
||||
_Y("deleted"), 0, 0);
|
||||
yaffs_trace(YAFFS_TRACE_TRACING,
|
||||
"yaffs: immediate deletion of file %d",
|
||||
in->obj_id);
|
||||
in->deleted = 1;
|
||||
in->my_dev->n_deleted_files++;
|
||||
if (dev->param.disable_soft_del || dev->param.is_yaffs2)
|
||||
yaffs_resize_file(in, 0);
|
||||
yaffs_soft_del_file(in);
|
||||
} else {
|
||||
ret_val =
|
||||
yaffs_change_obj_name(in, in->my_dev->unlinked_dir,
|
||||
_Y("unlinked"), 0, 0);
|
||||
}
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
static int yaffs_del_file(struct yaffs_obj *in)
|
||||
{
|
||||
int ret_val = YAFFS_OK;
|
||||
int deleted; /* Need to cache value on stack if in is freed */
|
||||
struct yaffs_dev *dev = in->my_dev;
|
||||
|
||||
if (dev->param.disable_soft_del || dev->param.is_yaffs2)
|
||||
yaffs_resize_file(in, 0);
|
||||
|
||||
if (in->n_data_chunks > 0) {
|
||||
/* Use soft deletion if there is data in the file.
|
||||
* That won't be the case if it has been resized to zero.
|
||||
*/
|
||||
if (!in->unlinked)
|
||||
ret_val = yaffs_unlink_file_if_needed(in);
|
||||
|
||||
deleted = in->deleted;
|
||||
|
||||
if (ret_val == YAFFS_OK && in->unlinked && !in->deleted) {
|
||||
in->deleted = 1;
|
||||
deleted = 1;
|
||||
in->my_dev->n_deleted_files++;
|
||||
yaffs_soft_del_file(in);
|
||||
}
|
||||
return deleted ? YAFFS_OK : YAFFS_FAIL;
|
||||
} else {
|
||||
/* The file has no data chunks so we toss it immediately */
|
||||
yaffs_free_tnode(in->my_dev, in->variant.file_variant.top);
|
||||
in->variant.file_variant.top = NULL;
|
||||
yaffs_generic_obj_del(in);
|
||||
|
||||
return YAFFS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
int yaffs_is_non_empty_dir(struct yaffs_obj *obj)
|
||||
{
|
||||
return (obj &&
|
||||
obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) &&
|
||||
!(list_empty(&obj->variant.dir_variant.children));
|
||||
}
|
||||
|
||||
static int yaffs_del_dir(struct yaffs_obj *obj)
|
||||
{
|
||||
/* First check that the directory is empty. */
|
||||
if (yaffs_is_non_empty_dir(obj))
|
||||
return YAFFS_FAIL;
|
||||
|
||||
return yaffs_generic_obj_del(obj);
|
||||
}
|
||||
|
||||
static int yaffs_del_symlink(struct yaffs_obj *in)
|
||||
{
|
||||
kfree(in->variant.symlink_variant.alias);
|
||||
in->variant.symlink_variant.alias = NULL;
|
||||
|
||||
return yaffs_generic_obj_del(in);
|
||||
}
|
||||
|
||||
static int yaffs_del_link(struct yaffs_obj *in)
|
||||
{
|
||||
/* remove this hardlink from the list associated with the equivalent
|
||||
* object
|
||||
*/
|
||||
list_del_init(&in->hard_links);
|
||||
return yaffs_generic_obj_del(in);
|
||||
}
|
||||
|
||||
int yaffs_del_obj(struct yaffs_obj *obj)
|
||||
{
|
||||
int ret_val = -1;
|
||||
|
||||
switch (obj->variant_type) {
|
||||
case YAFFS_OBJECT_TYPE_FILE:
|
||||
ret_val = yaffs_del_file(obj);
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_DIRECTORY:
|
||||
if (!list_empty(&obj->variant.dir_variant.dirty)) {
|
||||
yaffs_trace(YAFFS_TRACE_BACKGROUND,
|
||||
"Remove object %d from dirty directories",
|
||||
obj->obj_id);
|
||||
list_del_init(&obj->variant.dir_variant.dirty);
|
||||
}
|
||||
return yaffs_del_dir(obj);
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_SYMLINK:
|
||||
ret_val = yaffs_del_symlink(obj);
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_HARDLINK:
|
||||
ret_val = yaffs_del_link(obj);
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_SPECIAL:
|
||||
ret_val = yaffs_generic_obj_del(obj);
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_UNKNOWN:
|
||||
ret_val = 0;
|
||||
break; /* should not happen. */
|
||||
}
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
|
||||
static void yaffs_empty_dir_to_dir(struct yaffs_obj *from_dir,
|
||||
struct yaffs_obj *to_dir)
|
||||
{
|
||||
struct yaffs_obj *obj;
|
||||
struct list_head *lh;
|
||||
struct list_head *n;
|
||||
|
||||
list_for_each_safe(lh, n, &from_dir->variant.dir_variant.children) {
|
||||
obj = list_entry(lh, struct yaffs_obj, siblings);
|
||||
yaffs_add_obj_to_dir(to_dir, obj);
|
||||
}
|
||||
}
|
||||
|
||||
struct yaffs_obj *yaffs_retype_obj(struct yaffs_obj *obj,
|
||||
enum yaffs_obj_type type)
|
||||
{
|
||||
/* Tear down the old variant */
|
||||
switch (obj->variant_type) {
|
||||
case YAFFS_OBJECT_TYPE_FILE:
|
||||
/* Nuke file data */
|
||||
yaffs_resize_file(obj, 0);
|
||||
yaffs_free_tnode(obj->my_dev, obj->variant.file_variant.top);
|
||||
obj->variant.file_variant.top = NULL;
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_DIRECTORY:
|
||||
/* Put the children in lost and found. */
|
||||
yaffs_empty_dir_to_dir(obj, obj->my_dev->lost_n_found);
|
||||
if (!list_empty(&obj->variant.dir_variant.dirty))
|
||||
list_del_init(&obj->variant.dir_variant.dirty);
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_SYMLINK:
|
||||
/* Nuke symplink data */
|
||||
kfree(obj->variant.symlink_variant.alias);
|
||||
obj->variant.symlink_variant.alias = NULL;
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_HARDLINK:
|
||||
list_del_init(&obj->hard_links);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
memset(&obj->variant, 0, sizeof(obj->variant));
|
||||
|
||||
/*Set up new variant if the memset is not enough. */
|
||||
switch (type) {
|
||||
case YAFFS_OBJECT_TYPE_DIRECTORY:
|
||||
INIT_LIST_HEAD(&obj->variant.dir_variant.children);
|
||||
INIT_LIST_HEAD(&obj->variant.dir_variant.dirty);
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_FILE:
|
||||
case YAFFS_OBJECT_TYPE_SYMLINK:
|
||||
case YAFFS_OBJECT_TYPE_HARDLINK:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
obj->variant_type = type;
|
||||
|
||||
return obj;
|
||||
|
||||
}
|
||||
|
||||
static int yaffs_unlink_worker(struct yaffs_obj *obj)
|
||||
{
|
||||
int del_now = 0;
|
||||
|
||||
if (!obj)
|
||||
return YAFFS_FAIL;
|
||||
|
||||
if (!obj->my_inode)
|
||||
del_now = 1;
|
||||
|
||||
yaffs_update_parent(obj->parent);
|
||||
|
||||
if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) {
|
||||
return yaffs_del_link(obj);
|
||||
} else if (!list_empty(&obj->hard_links)) {
|
||||
/* Curve ball: We're unlinking an object that has a hardlink.
|
||||
*
|
||||
* This problem arises because we are not strictly following
|
||||
* The Linux link/inode model.
|
||||
*
|
||||
* We can't really delete the object.
|
||||
* Instead, we do the following:
|
||||
* - Select a hardlink.
|
||||
* - Unhook it from the hard links
|
||||
* - Move it from its parent directory so that the rename works.
|
||||
* - Rename the object to the hardlink's name.
|
||||
* - Delete the hardlink
|
||||
*/
|
||||
|
||||
struct yaffs_obj *hl;
|
||||
struct yaffs_obj *parent;
|
||||
int ret_val;
|
||||
YCHAR name[YAFFS_MAX_NAME_LENGTH + 1];
|
||||
|
||||
hl = list_entry(obj->hard_links.next, struct yaffs_obj,
|
||||
hard_links);
|
||||
|
||||
yaffs_get_obj_name(hl, name, YAFFS_MAX_NAME_LENGTH + 1);
|
||||
parent = hl->parent;
|
||||
|
||||
list_del_init(&hl->hard_links);
|
||||
|
||||
yaffs_add_obj_to_dir(obj->my_dev->unlinked_dir, hl);
|
||||
|
||||
ret_val = yaffs_change_obj_name(obj, parent, name, 0, 0);
|
||||
|
||||
if (ret_val == YAFFS_OK)
|
||||
ret_val = yaffs_generic_obj_del(hl);
|
||||
|
||||
return ret_val;
|
||||
|
||||
} else if (del_now) {
|
||||
switch (obj->variant_type) {
|
||||
case YAFFS_OBJECT_TYPE_FILE:
|
||||
return yaffs_del_file(obj);
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_DIRECTORY:
|
||||
list_del_init(&obj->variant.dir_variant.dirty);
|
||||
return yaffs_del_dir(obj);
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_SYMLINK:
|
||||
return yaffs_del_symlink(obj);
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_SPECIAL:
|
||||
return yaffs_generic_obj_del(obj);
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_HARDLINK:
|
||||
case YAFFS_OBJECT_TYPE_UNKNOWN:
|
||||
default:
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
} else if (yaffs_is_non_empty_dir(obj)) {
|
||||
return YAFFS_FAIL;
|
||||
} else {
|
||||
return yaffs_change_obj_name(obj, obj->my_dev->unlinked_dir,
|
||||
_Y("unlinked"), 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static int yaffs_unlink_obj(struct yaffs_obj *obj)
|
||||
{
|
||||
if (obj && obj->unlink_allowed)
|
||||
return yaffs_unlink_worker(obj);
|
||||
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
|
||||
int yaffs_unlinker(struct yaffs_obj *dir, const YCHAR *name)
|
||||
{
|
||||
struct yaffs_obj *obj;
|
||||
|
||||
obj = yaffs_find_by_name(dir, name);
|
||||
return yaffs_unlink_obj(obj);
|
||||
}
|
||||
|
||||
/* Note:
|
||||
* If old_name is NULL then we take old_dir as the object to be renamed.
|
||||
*/
|
||||
int yaffs_rename_obj(struct yaffs_obj *old_dir, const YCHAR *old_name,
|
||||
struct yaffs_obj *new_dir, const YCHAR *new_name)
|
||||
{
|
||||
struct yaffs_obj *obj = NULL;
|
||||
struct yaffs_obj *existing_target = NULL;
|
||||
int force = 0;
|
||||
int result;
|
||||
struct yaffs_dev *dev;
|
||||
|
||||
if (!old_dir || old_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) {
|
||||
BUG();
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
if (!new_dir || new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) {
|
||||
BUG();
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
|
||||
dev = old_dir->my_dev;
|
||||
|
||||
#ifdef CONFIG_YAFFS_CASE_INSENSITIVE
|
||||
/* Special case for case insemsitive systems.
|
||||
* While look-up is case insensitive, the name isn't.
|
||||
* Therefore we might want to change x.txt to X.txt
|
||||
*/
|
||||
if (old_dir == new_dir &&
|
||||
old_name && new_name &&
|
||||
yaffs_strcmp(old_name, new_name) == 0)
|
||||
force = 1;
|
||||
#endif
|
||||
|
||||
if (yaffs_strnlen(new_name, YAFFS_MAX_NAME_LENGTH + 1) >
|
||||
YAFFS_MAX_NAME_LENGTH)
|
||||
/* ENAMETOOLONG */
|
||||
return YAFFS_FAIL;
|
||||
|
||||
if (old_name)
|
||||
obj = yaffs_find_by_name(old_dir, old_name);
|
||||
else{
|
||||
obj = old_dir;
|
||||
old_dir = obj->parent;
|
||||
}
|
||||
|
||||
if (obj && obj->rename_allowed) {
|
||||
/* Now handle an existing target, if there is one */
|
||||
existing_target = yaffs_find_by_name(new_dir, new_name);
|
||||
if (yaffs_is_non_empty_dir(existing_target)) {
|
||||
return YAFFS_FAIL; /* ENOTEMPTY */
|
||||
} else if (existing_target && existing_target != obj) {
|
||||
/* Nuke the target first, using shadowing,
|
||||
* but only if it isn't the same object.
|
||||
*
|
||||
* Note we must disable gc here otherwise it can mess
|
||||
* up the shadowing.
|
||||
*
|
||||
*/
|
||||
dev->gc_disable = 1;
|
||||
yaffs_change_obj_name(obj, new_dir, new_name, force,
|
||||
existing_target->obj_id);
|
||||
existing_target->is_shadowed = 1;
|
||||
yaffs_unlink_obj(existing_target);
|
||||
dev->gc_disable = 0;
|
||||
}
|
||||
|
||||
result = yaffs_change_obj_name(obj, new_dir, new_name, 1, 0);
|
||||
|
||||
yaffs_update_parent(old_dir);
|
||||
if (new_dir != old_dir)
|
||||
yaffs_update_parent(new_dir);
|
||||
|
||||
return result;
|
||||
}
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
|
||||
/*----------------------- Initialisation Scanning ---------------------- */
|
||||
|
||||
void yaffs_handle_shadowed_obj(struct yaffs_dev *dev, int obj_id,
|
||||
int backward_scanning)
|
||||
{
|
||||
struct yaffs_obj *obj;
|
||||
|
||||
if (backward_scanning) {
|
||||
/* Handle YAFFS2 case (backward scanning)
|
||||
* If the shadowed object exists then ignore.
|
||||
*/
|
||||
obj = yaffs_find_by_number(dev, obj_id);
|
||||
if (obj)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Let's create it (if it does not exist) assuming it is a file so that
|
||||
* it can do shrinking etc.
|
||||
* We put it in unlinked dir to be cleaned up after the scanning
|
||||
*/
|
||||
obj =
|
||||
yaffs_find_or_create_by_number(dev, obj_id, YAFFS_OBJECT_TYPE_FILE);
|
||||
if (!obj)
|
||||
return;
|
||||
obj->is_shadowed = 1;
|
||||
yaffs_add_obj_to_dir(dev->unlinked_dir, obj);
|
||||
obj->variant.file_variant.shrink_size = 0;
|
||||
obj->valid = 1; /* So that we don't read any other info. */
|
||||
}
|
||||
|
||||
void yaffs_link_fixup(struct yaffs_dev *dev, struct list_head *hard_list)
|
||||
{
|
||||
struct list_head *lh;
|
||||
struct list_head *save;
|
||||
struct yaffs_obj *hl;
|
||||
struct yaffs_obj *in;
|
||||
|
||||
list_for_each_safe(lh, save, hard_list) {
|
||||
hl = list_entry(lh, struct yaffs_obj, hard_links);
|
||||
in = yaffs_find_by_number(dev,
|
||||
hl->variant.hardlink_variant.equiv_id);
|
||||
|
||||
if (in) {
|
||||
/* Add the hardlink pointers */
|
||||
hl->variant.hardlink_variant.equiv_obj = in;
|
||||
list_add(&hl->hard_links, &in->hard_links);
|
||||
} else {
|
||||
/* Todo Need to report/handle this better.
|
||||
* Got a problem... hardlink to a non-existant object
|
||||
*/
|
||||
hl->variant.hardlink_variant.equiv_obj = NULL;
|
||||
INIT_LIST_HEAD(&hl->hard_links);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void yaffs_strip_deleted_objs(struct yaffs_dev *dev)
|
||||
{
|
||||
/*
|
||||
* Sort out state of unlinked and deleted objects after scanning.
|
||||
*/
|
||||
struct list_head *i;
|
||||
struct list_head *n;
|
||||
struct yaffs_obj *l;
|
||||
|
||||
if (dev->read_only)
|
||||
return;
|
||||
|
||||
/* Soft delete all the unlinked files */
|
||||
list_for_each_safe(i, n,
|
||||
&dev->unlinked_dir->variant.dir_variant.children) {
|
||||
l = list_entry(i, struct yaffs_obj, siblings);
|
||||
yaffs_del_obj(l);
|
||||
}
|
||||
|
||||
list_for_each_safe(i, n, &dev->del_dir->variant.dir_variant.children) {
|
||||
l = list_entry(i, struct yaffs_obj, siblings);
|
||||
yaffs_del_obj(l);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This code iterates through all the objects making sure that they are rooted.
|
||||
* Any unrooted objects are re-rooted in lost+found.
|
||||
* An object needs to be in one of:
|
||||
* - Directly under deleted, unlinked
|
||||
* - Directly or indirectly under root.
|
||||
*
|
||||
* Note:
|
||||
* This code assumes that we don't ever change the current relationships
|
||||
* between directories:
|
||||
* root_dir->parent == unlinked_dir->parent == del_dir->parent == NULL
|
||||
* lost-n-found->parent == root_dir
|
||||
*
|
||||
* This fixes the problem where directories might have inadvertently been
|
||||
* deleted leaving the object "hanging" without being rooted in the
|
||||
* directory tree.
|
||||
*/
|
||||
|
||||
static int yaffs_has_null_parent(struct yaffs_dev *dev, struct yaffs_obj *obj)
|
||||
{
|
||||
return (obj == dev->del_dir ||
|
||||
obj == dev->unlinked_dir || obj == dev->root_dir);
|
||||
}
|
||||
|
||||
static void yaffs_fix_hanging_objs(struct yaffs_dev *dev)
|
||||
{
|
||||
struct yaffs_obj *obj;
|
||||
struct yaffs_obj *parent;
|
||||
int i;
|
||||
struct list_head *lh;
|
||||
struct list_head *n;
|
||||
int depth_limit;
|
||||
int hanging;
|
||||
|
||||
if (dev->read_only)
|
||||
return;
|
||||
|
||||
/* Iterate through the objects in each hash entry,
|
||||
* looking at each object.
|
||||
* Make sure it is rooted.
|
||||
*/
|
||||
|
||||
for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) {
|
||||
list_for_each_safe(lh, n, &dev->obj_bucket[i].list) {
|
||||
obj = list_entry(lh, struct yaffs_obj, hash_link);
|
||||
parent = obj->parent;
|
||||
|
||||
if (yaffs_has_null_parent(dev, obj)) {
|
||||
/* These directories are not hanging */
|
||||
hanging = 0;
|
||||
} else if (!parent ||
|
||||
parent->variant_type !=
|
||||
YAFFS_OBJECT_TYPE_DIRECTORY) {
|
||||
hanging = 1;
|
||||
} else if (yaffs_has_null_parent(dev, parent)) {
|
||||
hanging = 0;
|
||||
} else {
|
||||
/*
|
||||
* Need to follow the parent chain to
|
||||
* see if it is hanging.
|
||||
*/
|
||||
hanging = 0;
|
||||
depth_limit = 100;
|
||||
|
||||
while (parent != dev->root_dir &&
|
||||
parent->parent &&
|
||||
parent->parent->variant_type ==
|
||||
YAFFS_OBJECT_TYPE_DIRECTORY &&
|
||||
depth_limit > 0) {
|
||||
parent = parent->parent;
|
||||
depth_limit--;
|
||||
}
|
||||
if (parent != dev->root_dir)
|
||||
hanging = 1;
|
||||
}
|
||||
if (hanging) {
|
||||
yaffs_trace(YAFFS_TRACE_SCAN,
|
||||
"Hanging object %d moved to lost and found",
|
||||
obj->obj_id);
|
||||
yaffs_add_obj_to_dir(dev->lost_n_found, obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete directory contents for cleaning up lost and found.
|
||||
*/
|
||||
static void yaffs_del_dir_contents(struct yaffs_obj *dir)
|
||||
{
|
||||
struct yaffs_obj *obj;
|
||||
struct list_head *lh;
|
||||
struct list_head *n;
|
||||
|
||||
if (dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY)
|
||||
BUG();
|
||||
|
||||
list_for_each_safe(lh, n, &dir->variant.dir_variant.children) {
|
||||
obj = list_entry(lh, struct yaffs_obj, siblings);
|
||||
if (obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY)
|
||||
yaffs_del_dir_contents(obj);
|
||||
yaffs_trace(YAFFS_TRACE_SCAN,
|
||||
"Deleting lost_found object %d",
|
||||
obj->obj_id);
|
||||
yaffs_unlink_obj(obj);
|
||||
}
|
||||
}
|
||||
|
||||
static void yaffs_empty_l_n_f(struct yaffs_dev *dev)
|
||||
{
|
||||
yaffs_del_dir_contents(dev->lost_n_found);
|
||||
}
|
||||
|
||||
|
||||
struct yaffs_obj *yaffs_find_by_name(struct yaffs_obj *directory,
|
||||
const YCHAR *name)
|
||||
{
|
||||
int sum;
|
||||
struct list_head *i;
|
||||
YCHAR buffer[YAFFS_MAX_NAME_LENGTH + 1];
|
||||
struct yaffs_obj *l;
|
||||
|
||||
if (!name)
|
||||
return NULL;
|
||||
|
||||
if (!directory) {
|
||||
yaffs_trace(YAFFS_TRACE_ALWAYS,
|
||||
"tragedy: yaffs_find_by_name: null pointer directory"
|
||||
);
|
||||
BUG();
|
||||
return NULL;
|
||||
}
|
||||
if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) {
|
||||
yaffs_trace(YAFFS_TRACE_ALWAYS,
|
||||
"tragedy: yaffs_find_by_name: non-directory"
|
||||
);
|
||||
BUG();
|
||||
}
|
||||
|
||||
sum = yaffs_calc_name_sum(name);
|
||||
|
||||
list_for_each(i, &directory->variant.dir_variant.children) {
|
||||
l = list_entry(i, struct yaffs_obj, siblings);
|
||||
|
||||
if (l->parent != directory)
|
||||
BUG();
|
||||
|
||||
yaffs_check_obj_details_loaded(l);
|
||||
|
||||
/* Special case for lost-n-found */
|
||||
if (l->obj_id == YAFFS_OBJECTID_LOSTNFOUND) {
|
||||
if (!yaffs_strcmp(name, YAFFS_LOSTNFOUND_NAME))
|
||||
return l;
|
||||
} else if (l->sum == sum || l->hdr_chunk <= 0) {
|
||||
/* LostnFound chunk called Objxxx
|
||||
* Do a real check
|
||||
*/
|
||||
yaffs_get_obj_name(l, buffer,
|
||||
YAFFS_MAX_NAME_LENGTH + 1);
|
||||
if (!yaffs_strncmp(name, buffer, YAFFS_MAX_NAME_LENGTH))
|
||||
return l;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* GetEquivalentObject dereferences any hard links to get to the
|
||||
* actual object.
|
||||
*/
|
||||
|
||||
struct yaffs_obj *yaffs_get_equivalent_obj(struct yaffs_obj *obj)
|
||||
{
|
||||
if (obj && obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) {
|
||||
obj = obj->variant.hardlink_variant.equiv_obj;
|
||||
yaffs_check_obj_details_loaded(obj);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
/*
|
||||
* A note or two on object names.
|
||||
* * If the object name is missing, we then make one up in the form objnnn
|
||||
*
|
||||
* * ASCII names are stored in the object header's name field from byte zero
|
||||
* * Unicode names are historically stored starting from byte zero.
|
||||
*
|
||||
* Then there are automatic Unicode names...
|
||||
* The purpose of these is to save names in a way that can be read as
|
||||
* ASCII or Unicode names as appropriate, thus allowing a Unicode and ASCII
|
||||
* system to share files.
|
||||
*
|
||||
* These automatic unicode are stored slightly differently...
|
||||
* - If the name can fit in the ASCII character space then they are saved as
|
||||
* ascii names as per above.
|
||||
* - If the name needs Unicode then the name is saved in Unicode
|
||||
* starting at oh->name[1].
|
||||
|
||||
*/
|
||||
static void yaffs_fix_null_name(struct yaffs_obj *obj, YCHAR *name,
|
||||
int buffer_size)
|
||||
{
|
||||
/* Create an object name if we could not find one. */
|
||||
if (yaffs_strnlen(name, YAFFS_MAX_NAME_LENGTH) == 0) {
|
||||
YCHAR local_name[20];
|
||||
YCHAR num_string[20];
|
||||
YCHAR *x = &num_string[19];
|
||||
unsigned v = obj->obj_id;
|
||||
num_string[19] = 0;
|
||||
while (v > 0) {
|
||||
x--;
|
||||
*x = '0' + (v % 10);
|
||||
v /= 10;
|
||||
}
|
||||
/* make up a name */
|
||||
yaffs_strcpy(local_name, YAFFS_LOSTNFOUND_PREFIX);
|
||||
yaffs_strcat(local_name, x);
|
||||
yaffs_strncpy(name, local_name, buffer_size - 1);
|
||||
}
|
||||
}
|
||||
|
||||
int yaffs_get_obj_name(struct yaffs_obj *obj, YCHAR *name, int buffer_size)
|
||||
{
|
||||
memset(name, 0, buffer_size * sizeof(YCHAR));
|
||||
yaffs_check_obj_details_loaded(obj);
|
||||
if (obj->obj_id == YAFFS_OBJECTID_LOSTNFOUND) {
|
||||
yaffs_strncpy(name, YAFFS_LOSTNFOUND_NAME, buffer_size - 1);
|
||||
} else if (obj->short_name[0]) {
|
||||
yaffs_strcpy(name, obj->short_name);
|
||||
} else if (obj->hdr_chunk > 0) {
|
||||
//int result;
|
||||
u8 *buffer = yaffs_get_temp_buffer(obj->my_dev);
|
||||
|
||||
struct yaffs_obj_hdr *oh = (struct yaffs_obj_hdr *)buffer;
|
||||
|
||||
memset(buffer, 0, obj->my_dev->data_bytes_per_chunk);
|
||||
|
||||
if (obj->hdr_chunk > 0) {
|
||||
if (!yaffs_rd_chunk_tags_nand(obj->my_dev,
|
||||
obj->hdr_chunk,
|
||||
buffer, NULL))
|
||||
{
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"yaffs_get_obj_name: unhandled error from rd_chunk_tags_nand");
|
||||
}
|
||||
}
|
||||
yaffs_load_name_from_oh(obj->my_dev, name, oh->name,
|
||||
buffer_size);
|
||||
|
||||
yaffs_release_temp_buffer(obj->my_dev, buffer);
|
||||
}
|
||||
|
||||
yaffs_fix_null_name(obj, name, buffer_size);
|
||||
|
||||
return yaffs_strnlen(name, YAFFS_MAX_NAME_LENGTH);
|
||||
}
|
||||
|
||||
Y_LOFF_T yaffs_get_obj_length(struct yaffs_obj *obj)
|
||||
{
|
||||
/* Dereference any hard linking */
|
||||
obj = yaffs_get_equivalent_obj(obj);
|
||||
|
||||
if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE)
|
||||
return obj->variant.file_variant.file_size;
|
||||
if (obj->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) {
|
||||
if (!obj->variant.symlink_variant.alias)
|
||||
return 0;
|
||||
return yaffs_strnlen(obj->variant.symlink_variant.alias,
|
||||
YAFFS_MAX_ALIAS_LENGTH);
|
||||
} else {
|
||||
/* Only a directory should drop through to here */
|
||||
return obj->my_dev->data_bytes_per_chunk;
|
||||
}
|
||||
}
|
||||
|
||||
int yaffs_get_obj_link_count(struct yaffs_obj *obj)
|
||||
{
|
||||
int count = 0;
|
||||
struct list_head *i;
|
||||
|
||||
if (!obj->unlinked)
|
||||
count++; /* the object itself */
|
||||
|
||||
list_for_each(i, &obj->hard_links)
|
||||
count++; /* add the hard links; */
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int yaffs_get_obj_inode(struct yaffs_obj *obj)
|
||||
{
|
||||
obj = yaffs_get_equivalent_obj(obj);
|
||||
|
||||
return obj->obj_id;
|
||||
}
|
||||
|
||||
unsigned yaffs_get_obj_type(struct yaffs_obj *obj)
|
||||
{
|
||||
obj = yaffs_get_equivalent_obj(obj);
|
||||
|
||||
switch (obj->variant_type) {
|
||||
case YAFFS_OBJECT_TYPE_FILE:
|
||||
return DT_REG;
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_DIRECTORY:
|
||||
return DT_DIR;
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_SYMLINK:
|
||||
return DT_LNK;
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_HARDLINK:
|
||||
return DT_REG;
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_SPECIAL:
|
||||
if (S_ISFIFO(obj->yst_mode))
|
||||
return DT_FIFO;
|
||||
if (S_ISCHR(obj->yst_mode))
|
||||
return DT_CHR;
|
||||
if (S_ISBLK(obj->yst_mode))
|
||||
return DT_BLK;
|
||||
if (S_ISSOCK(obj->yst_mode))
|
||||
return DT_SOCK;
|
||||
return DT_REG;
|
||||
break;
|
||||
default:
|
||||
return DT_REG;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
YCHAR *yaffs_get_symlink_alias(struct yaffs_obj *obj)
|
||||
{
|
||||
obj = yaffs_get_equivalent_obj(obj);
|
||||
if (obj->variant_type == YAFFS_OBJECT_TYPE_SYMLINK)
|
||||
return yaffs_clone_str(obj->variant.symlink_variant.alias);
|
||||
else
|
||||
return yaffs_clone_str(_Y(""));
|
||||
}
|
||||
|
||||
/*--------------------------- Initialisation code -------------------------- */
|
||||
|
||||
static int yaffs_check_dev_fns(struct yaffs_dev *dev)
|
||||
{
|
||||
struct yaffs_driver *drv = &dev->drv;
|
||||
struct yaffs_tags_handler *tagger = &dev->tagger;
|
||||
|
||||
/* Common functions, gotta have */
|
||||
if (!drv->drv_read_chunk_fn ||
|
||||
!drv->drv_write_chunk_fn ||
|
||||
!drv->drv_erase_fn)
|
||||
return 0;
|
||||
|
||||
if (dev->param.is_yaffs2 &&
|
||||
(!drv->drv_mark_bad_fn || !drv->drv_check_bad_fn))
|
||||
return 0;
|
||||
|
||||
/* Install the default tags marshalling functions if needed. */
|
||||
yaffs_tags_compat_install(dev);
|
||||
yaffs_tags_marshall_install(dev);
|
||||
|
||||
/* Check we now have the marshalling functions required. */
|
||||
if (!tagger->write_chunk_tags_fn ||
|
||||
!tagger->read_chunk_tags_fn ||
|
||||
!tagger->query_block_fn ||
|
||||
!tagger->mark_bad_fn)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int yaffs_create_initial_dir(struct yaffs_dev *dev)
|
||||
{
|
||||
/* Initialise the unlinked, deleted, root and lost+found directories */
|
||||
dev->lost_n_found = dev->root_dir = NULL;
|
||||
dev->unlinked_dir = dev->del_dir = NULL;
|
||||
dev->unlinked_dir =
|
||||
yaffs_create_fake_dir(dev, YAFFS_OBJECTID_UNLINKED, S_IFDIR);
|
||||
dev->del_dir =
|
||||
yaffs_create_fake_dir(dev, YAFFS_OBJECTID_DELETED, S_IFDIR);
|
||||
dev->root_dir =
|
||||
yaffs_create_fake_dir(dev, YAFFS_OBJECTID_ROOT,
|
||||
YAFFS_ROOT_MODE | S_IFDIR);
|
||||
dev->lost_n_found =
|
||||
yaffs_create_fake_dir(dev, YAFFS_OBJECTID_LOSTNFOUND,
|
||||
YAFFS_LOSTNFOUND_MODE | S_IFDIR);
|
||||
|
||||
if (dev->lost_n_found && dev->root_dir && dev->unlinked_dir
|
||||
&& dev->del_dir) {
|
||||
yaffs_add_obj_to_dir(dev->root_dir, dev->lost_n_found);
|
||||
return YAFFS_OK;
|
||||
}
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
|
||||
/* Low level init.
|
||||
* Typically only used by yaffs_guts_initialise, but also used by the
|
||||
* Low level yaffs driver tests.
|
||||
*/
|
||||
|
||||
int yaffs_guts_ll_init(struct yaffs_dev *dev)
|
||||
{
|
||||
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_TRACING, "yaffs: yaffs_ll_init()");
|
||||
|
||||
if (!dev) {
|
||||
yaffs_trace(YAFFS_TRACE_ALWAYS,
|
||||
"yaffs: Need a device"
|
||||
);
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
|
||||
if (dev->ll_init)
|
||||
return YAFFS_OK;
|
||||
|
||||
dev->internal_start_block = dev->param.start_block;
|
||||
dev->internal_end_block = dev->param.end_block;
|
||||
dev->block_offset = 0;
|
||||
dev->chunk_offset = 0;
|
||||
dev->n_free_chunks = 0;
|
||||
|
||||
dev->gc_block = 0;
|
||||
|
||||
if (dev->param.start_block == 0) {
|
||||
dev->internal_start_block = dev->param.start_block + 1;
|
||||
dev->internal_end_block = dev->param.end_block + 1;
|
||||
dev->block_offset = 1;
|
||||
dev->chunk_offset = dev->param.chunks_per_block;
|
||||
}
|
||||
|
||||
/* Check geometry parameters. */
|
||||
|
||||
if ((!dev->param.inband_tags && dev->param.is_yaffs2 &&
|
||||
dev->param.total_bytes_per_chunk < 1024) ||
|
||||
(!dev->param.is_yaffs2 &&
|
||||
dev->param.total_bytes_per_chunk < 512) ||
|
||||
(dev->param.inband_tags && !dev->param.is_yaffs2) ||
|
||||
dev->param.chunks_per_block < 2 ||
|
||||
dev->param.n_reserved_blocks < 2 ||
|
||||
dev->internal_start_block <= 0 ||
|
||||
dev->internal_end_block <= 0 ||
|
||||
dev->internal_end_block <=
|
||||
(dev->internal_start_block + dev->param.n_reserved_blocks + 2)
|
||||
) {
|
||||
/* otherwise it is too small */
|
||||
yaffs_trace(YAFFS_TRACE_ALWAYS,
|
||||
"NAND geometry problems: chunk size %d, type is yaffs%s, inband_tags %d ",
|
||||
dev->param.total_bytes_per_chunk,
|
||||
dev->param.is_yaffs2 ? "2" : "",
|
||||
dev->param.inband_tags);
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
|
||||
/* Sort out space for inband tags, if required */
|
||||
if (dev->param.inband_tags)
|
||||
dev->data_bytes_per_chunk =
|
||||
dev->param.total_bytes_per_chunk -
|
||||
sizeof(struct yaffs_packed_tags2_tags_only);
|
||||
else
|
||||
dev->data_bytes_per_chunk = dev->param.total_bytes_per_chunk;
|
||||
|
||||
/* Got the right mix of functions? */
|
||||
if (!yaffs_check_dev_fns(dev)) {
|
||||
/* Function missing */
|
||||
yaffs_trace(YAFFS_TRACE_ALWAYS,
|
||||
"device function(s) missing or wrong");
|
||||
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
|
||||
if (yaffs_init_nand(dev) != YAFFS_OK) {
|
||||
yaffs_trace(YAFFS_TRACE_ALWAYS, "InitialiseNAND failed");
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
|
||||
return YAFFS_OK;
|
||||
}
|
||||
|
||||
|
||||
int yaffs_guts_format_dev(struct yaffs_dev *dev)
|
||||
{
|
||||
int i;
|
||||
enum yaffs_block_state state;
|
||||
u32 dummy;
|
||||
|
||||
if(yaffs_guts_ll_init(dev) != YAFFS_OK)
|
||||
return YAFFS_FAIL;
|
||||
|
||||
if(dev->is_mounted)
|
||||
return YAFFS_FAIL;
|
||||
|
||||
for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) {
|
||||
yaffs_query_init_block_state(dev, i, &state, &dummy);
|
||||
if (state != YAFFS_BLOCK_STATE_DEAD)
|
||||
yaffs_erase_block(dev, i);
|
||||
}
|
||||
|
||||
return YAFFS_OK;
|
||||
}
|
||||
|
||||
|
||||
int yaffs_guts_initialise(struct yaffs_dev *dev)
|
||||
{
|
||||
int init_failed = 0;
|
||||
unsigned x;
|
||||
int bits;
|
||||
|
||||
if(yaffs_guts_ll_init(dev) != YAFFS_OK)
|
||||
return YAFFS_FAIL;
|
||||
|
||||
if (dev->is_mounted) {
|
||||
yaffs_trace(YAFFS_TRACE_ALWAYS, "device already mounted");
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
|
||||
dev->is_mounted = 1;
|
||||
|
||||
/* OK now calculate a few things for the device */
|
||||
|
||||
/*
|
||||
* Calculate all the chunk size manipulation numbers:
|
||||
*/
|
||||
x = dev->data_bytes_per_chunk;
|
||||
/* We always use dev->chunk_shift and dev->chunk_div */
|
||||
dev->chunk_shift = calc_shifts(x);
|
||||
x >>= dev->chunk_shift;
|
||||
dev->chunk_div = x;
|
||||
/* We only use chunk mask if chunk_div is 1 */
|
||||
dev->chunk_mask = (1 << dev->chunk_shift) - 1;
|
||||
|
||||
/*
|
||||
* Calculate chunk_grp_bits.
|
||||
* We need to find the next power of 2 > than internal_end_block
|
||||
*/
|
||||
|
||||
x = dev->param.chunks_per_block * (dev->internal_end_block + 1);
|
||||
|
||||
bits = calc_shifts_ceiling(x);
|
||||
|
||||
/* Set up tnode width if wide tnodes are enabled. */
|
||||
if (!dev->param.wide_tnodes_disabled) {
|
||||
/* bits must be even so that we end up with 32-bit words */
|
||||
if (bits & 1)
|
||||
bits++;
|
||||
if (bits < 16)
|
||||
dev->tnode_width = 16;
|
||||
else
|
||||
dev->tnode_width = bits;
|
||||
} else {
|
||||
dev->tnode_width = 16;
|
||||
}
|
||||
|
||||
dev->tnode_mask = (1 << dev->tnode_width) - 1;
|
||||
|
||||
/* Level0 Tnodes are 16 bits or wider (if wide tnodes are enabled),
|
||||
* so if the bitwidth of the
|
||||
* chunk range we're using is greater than 16 we need
|
||||
* to figure out chunk shift and chunk_grp_size
|
||||
*/
|
||||
|
||||
if (bits <= (int)dev->tnode_width)
|
||||
dev->chunk_grp_bits = 0;
|
||||
else
|
||||
dev->chunk_grp_bits = bits - dev->tnode_width;
|
||||
|
||||
dev->tnode_size = (dev->tnode_width * YAFFS_NTNODES_LEVEL0) / 8;
|
||||
if (dev->tnode_size < sizeof(struct yaffs_tnode))
|
||||
dev->tnode_size = sizeof(struct yaffs_tnode);
|
||||
|
||||
dev->chunk_grp_size = 1 << dev->chunk_grp_bits;
|
||||
|
||||
if (dev->param.chunks_per_block < dev->chunk_grp_size) {
|
||||
/* We have a problem because the soft delete won't work if
|
||||
* the chunk group size > chunks per block.
|
||||
* This can be remedied by using larger "virtual blocks".
|
||||
*/
|
||||
yaffs_trace(YAFFS_TRACE_ALWAYS, "chunk group too large");
|
||||
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
|
||||
/* Finished verifying the device, continue with initialisation */
|
||||
|
||||
/* More device initialisation */
|
||||
dev->all_gcs = 0;
|
||||
dev->passive_gc_count = 0;
|
||||
dev->oldest_dirty_gc_count = 0;
|
||||
dev->bg_gcs = 0;
|
||||
dev->gc_block_finder = 0;
|
||||
dev->buffered_block = -1;
|
||||
dev->doing_buffered_block_rewrite = 0;
|
||||
dev->n_deleted_files = 0;
|
||||
dev->n_bg_deletions = 0;
|
||||
dev->n_unlinked_files = 0;
|
||||
dev->n_ecc_fixed = 0;
|
||||
dev->n_ecc_unfixed = 0;
|
||||
dev->n_tags_ecc_fixed = 0;
|
||||
dev->n_tags_ecc_unfixed = 0;
|
||||
dev->n_erase_failures = 0;
|
||||
dev->n_erased_blocks = 0;
|
||||
dev->gc_disable = 0;
|
||||
dev->has_pending_prioritised_gc = 1;
|
||||
/* Assume the worst for now, will get fixed on first GC */
|
||||
INIT_LIST_HEAD(&dev->dirty_dirs);
|
||||
dev->oldest_dirty_seq = 0;
|
||||
dev->oldest_dirty_block = 0;
|
||||
|
||||
/* Initialise temporary buffers and caches. */
|
||||
if (!yaffs_init_tmp_buffers(dev))
|
||||
init_failed = 1;
|
||||
|
||||
dev->cache = NULL;
|
||||
dev->gc_cleanup_list = NULL;
|
||||
|
||||
if (!init_failed && dev->param.n_caches > 0) {
|
||||
int i;
|
||||
void *buf;
|
||||
int cache_bytes =
|
||||
dev->param.n_caches * sizeof(struct yaffs_cache);
|
||||
|
||||
if (dev->param.n_caches > YAFFS_MAX_SHORT_OP_CACHES)
|
||||
dev->param.n_caches = YAFFS_MAX_SHORT_OP_CACHES;
|
||||
|
||||
dev->cache = kmalloc(cache_bytes, GFP_NOFS);
|
||||
|
||||
buf = (u8 *) dev->cache;
|
||||
|
||||
if (dev->cache)
|
||||
memset(dev->cache, 0, cache_bytes);
|
||||
|
||||
for (i = 0; i < dev->param.n_caches && buf; i++) {
|
||||
dev->cache[i].object = NULL;
|
||||
dev->cache[i].last_use = 0;
|
||||
dev->cache[i].dirty = 0;
|
||||
dev->cache[i].data = buf =
|
||||
kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS);
|
||||
}
|
||||
if (!buf)
|
||||
init_failed = 1;
|
||||
|
||||
dev->cache_last_use = 0;
|
||||
}
|
||||
|
||||
dev->cache_hits = 0;
|
||||
|
||||
if (!init_failed) {
|
||||
dev->gc_cleanup_list =
|
||||
kmalloc(dev->param.chunks_per_block * sizeof(u32),
|
||||
GFP_NOFS);
|
||||
if (!dev->gc_cleanup_list)
|
||||
init_failed = 1;
|
||||
}
|
||||
|
||||
if (dev->param.is_yaffs2)
|
||||
dev->param.use_header_file_size = 1;
|
||||
|
||||
if (!init_failed && !yaffs_init_blocks(dev))
|
||||
init_failed = 1;
|
||||
|
||||
yaffs_init_tnodes_and_objs(dev);
|
||||
|
||||
if (!init_failed && !yaffs_create_initial_dir(dev))
|
||||
init_failed = 1;
|
||||
|
||||
if (!init_failed && dev->param.is_yaffs2 &&
|
||||
!dev->param.disable_summary &&
|
||||
!yaffs_summary_init(dev))
|
||||
init_failed = 1;
|
||||
|
||||
if (!init_failed) {
|
||||
/* Now scan the flash. */
|
||||
if (dev->param.is_yaffs2) {
|
||||
if (yaffs2_checkpt_restore(dev)) {
|
||||
yaffs_check_obj_details_loaded(dev->root_dir);
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT |
|
||||
YAFFS_TRACE_MOUNT,
|
||||
"yaffs: restored from checkpoint"
|
||||
);
|
||||
} else {
|
||||
|
||||
/* Clean up the mess caused by an aborted
|
||||
* checkpoint load then scan backwards.
|
||||
*/
|
||||
yaffs_deinit_blocks(dev);
|
||||
|
||||
yaffs_deinit_tnodes_and_objs(dev);
|
||||
|
||||
dev->n_erased_blocks = 0;
|
||||
dev->n_free_chunks = 0;
|
||||
dev->alloc_block = -1;
|
||||
dev->alloc_page = -1;
|
||||
dev->n_deleted_files = 0;
|
||||
dev->n_unlinked_files = 0;
|
||||
dev->n_bg_deletions = 0;
|
||||
|
||||
if (!init_failed && !yaffs_init_blocks(dev))
|
||||
init_failed = 1;
|
||||
|
||||
yaffs_init_tnodes_and_objs(dev);
|
||||
|
||||
if (!init_failed
|
||||
&& !yaffs_create_initial_dir(dev))
|
||||
init_failed = 1;
|
||||
|
||||
if (!init_failed && !yaffs2_scan_backwards(dev))
|
||||
init_failed = 1;
|
||||
}
|
||||
} else if (!yaffs1_scan(dev)) {
|
||||
init_failed = 1;
|
||||
}
|
||||
|
||||
yaffs_strip_deleted_objs(dev);
|
||||
yaffs_fix_hanging_objs(dev);
|
||||
if (dev->param.empty_lost_n_found)
|
||||
yaffs_empty_l_n_f(dev);
|
||||
}
|
||||
|
||||
if (init_failed) {
|
||||
/* Clean up the mess */
|
||||
yaffs_trace(YAFFS_TRACE_TRACING,
|
||||
"yaffs: yaffs_guts_initialise() aborted.");
|
||||
|
||||
yaffs_deinitialise(dev);
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
|
||||
/* Zero out stats */
|
||||
dev->n_page_reads = 0;
|
||||
dev->n_page_writes = 0;
|
||||
dev->n_erasures = 0;
|
||||
dev->n_gc_copies = 0;
|
||||
dev->n_retried_writes = 0;
|
||||
|
||||
dev->n_retired_blocks = 0;
|
||||
|
||||
yaffs_verify_free_chunks(dev);
|
||||
yaffs_verify_blocks(dev);
|
||||
|
||||
/* Clean up any aborted checkpoint data */
|
||||
if (!dev->is_checkpointed && dev->blocks_in_checkpt > 0)
|
||||
yaffs2_checkpt_invalidate(dev);
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_TRACING,
|
||||
"yaffs: yaffs_guts_initialise() done.");
|
||||
return YAFFS_OK;
|
||||
}
|
||||
|
||||
void yaffs_deinitialise(struct yaffs_dev *dev)
|
||||
{
|
||||
if (dev->is_mounted) {
|
||||
int i;
|
||||
|
||||
yaffs_deinit_blocks(dev);
|
||||
yaffs_deinit_tnodes_and_objs(dev);
|
||||
yaffs_summary_deinit(dev);
|
||||
|
||||
if (dev->param.n_caches > 0 && dev->cache) {
|
||||
|
||||
for (i = 0; i < dev->param.n_caches; i++) {
|
||||
kfree(dev->cache[i].data);
|
||||
dev->cache[i].data = NULL;
|
||||
}
|
||||
|
||||
kfree(dev->cache);
|
||||
dev->cache = NULL;
|
||||
}
|
||||
|
||||
kfree(dev->gc_cleanup_list);
|
||||
|
||||
for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++)
|
||||
kfree(dev->temp_buffer[i].buffer);
|
||||
|
||||
dev->is_mounted = 0;
|
||||
|
||||
yaffs_deinit_nand(dev);
|
||||
}
|
||||
}
|
||||
|
||||
int yaffs_count_free_chunks(struct yaffs_dev *dev)
|
||||
{
|
||||
int n_free = 0;
|
||||
int b;
|
||||
struct yaffs_block_info *blk;
|
||||
|
||||
blk = dev->block_info;
|
||||
for (b = dev->internal_start_block; b <= dev->internal_end_block; b++) {
|
||||
switch (blk->block_state) {
|
||||
case YAFFS_BLOCK_STATE_EMPTY:
|
||||
case YAFFS_BLOCK_STATE_ALLOCATING:
|
||||
case YAFFS_BLOCK_STATE_COLLECTING:
|
||||
case YAFFS_BLOCK_STATE_FULL:
|
||||
n_free +=
|
||||
(dev->param.chunks_per_block - blk->pages_in_use +
|
||||
blk->soft_del_pages);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
blk++;
|
||||
}
|
||||
return n_free;
|
||||
}
|
||||
|
||||
int yaffs_get_n_free_chunks(struct yaffs_dev *dev)
|
||||
{
|
||||
/* This is what we report to the outside world */
|
||||
int n_free;
|
||||
int n_dirty_caches;
|
||||
int blocks_for_checkpt;
|
||||
int i;
|
||||
|
||||
n_free = dev->n_free_chunks;
|
||||
n_free += dev->n_deleted_files;
|
||||
|
||||
/* Now count and subtract the number of dirty chunks in the cache. */
|
||||
|
||||
for (n_dirty_caches = 0, i = 0; i < dev->param.n_caches; i++) {
|
||||
if (dev->cache[i].dirty)
|
||||
n_dirty_caches++;
|
||||
}
|
||||
|
||||
n_free -= n_dirty_caches;
|
||||
|
||||
n_free -=
|
||||
((dev->param.n_reserved_blocks + 1) * dev->param.chunks_per_block);
|
||||
|
||||
/* Now figure checkpoint space and report that... */
|
||||
blocks_for_checkpt = yaffs_calc_checkpt_blocks_required(dev);
|
||||
|
||||
n_free -= (blocks_for_checkpt * dev->param.chunks_per_block);
|
||||
|
||||
if (n_free < 0)
|
||||
n_free = 0;
|
||||
|
||||
return n_free;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Marshalling functions to get Y_LOFF_T file sizes into and out of
|
||||
* object headers.
|
||||
*/
|
||||
void yaffs_oh_size_load(struct yaffs_obj_hdr *oh, Y_LOFF_T fsize)
|
||||
{
|
||||
#ifdef CONFIG_YAFFS_OP
|
||||
oh->file_size_low = (fsize & 0xFFFFFFFF);
|
||||
oh->file_size_high = 0; //((fsize >> 32) & 0xFFFFFFFF);
|
||||
#else
|
||||
oh->file_size_low = (fsize & 0xFFFFFFFF);
|
||||
oh->file_size_high = ((fsize >> 32) & 0xFFFFFFFF);
|
||||
#endif
|
||||
}
|
||||
|
||||
Y_LOFF_T yaffs_oh_to_size(struct yaffs_obj_hdr *oh)
|
||||
{
|
||||
Y_LOFF_T retval;
|
||||
|
||||
if (sizeof(Y_LOFF_T) >= 8 && ~(oh->file_size_high))
|
||||
#ifdef CONFIG_YAFFS_OP
|
||||
retval = (((Y_LOFF_T) oh->file_size_low) & 0xFFFFFFFF);
|
||||
#else
|
||||
retval = (((Y_LOFF_T) oh->file_size_high) << 32) |
|
||||
(((Y_LOFF_T) oh->file_size_low) & 0xFFFFFFFF);
|
||||
#endif
|
||||
else
|
||||
retval = (Y_LOFF_T) oh->file_size_low;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
void yaffs_count_blocks_by_state(struct yaffs_dev *dev, int bs[10])
|
||||
{
|
||||
int i;
|
||||
struct yaffs_block_info *bi;
|
||||
int s;
|
||||
|
||||
for(i = 0; i < 10; i++)
|
||||
bs[i] = 0;
|
||||
|
||||
for(i = dev->internal_start_block; i <= dev->internal_end_block; i++) {
|
||||
bi = yaffs_get_block_info(dev, i);
|
||||
s = bi->block_state;
|
||||
if(s > YAFFS_BLOCK_STATE_DEAD || s < YAFFS_BLOCK_STATE_UNKNOWN)
|
||||
bs[0]++;
|
||||
else
|
||||
bs[s]++;
|
||||
}
|
||||
}
|
53
flight/pios/common/libraries/yaffs2/yaffs_hweight.c
Normal file
53
flight/pios/common/libraries/yaffs2/yaffs_hweight.c
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
/* These functions have been renamed to hweightxx to match the
|
||||
* equivaqlent functions in the Linux kernel.
|
||||
*/
|
||||
|
||||
#include "yaffs_hweight.h"
|
||||
|
||||
static const char yaffs_count_bits_table[256] = {
|
||||
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
|
||||
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
|
||||
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
|
||||
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
|
||||
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
|
||||
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
|
||||
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
|
||||
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
|
||||
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
|
||||
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
|
||||
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
|
||||
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
|
||||
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
|
||||
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
|
||||
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
|
||||
4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
|
||||
};
|
||||
|
||||
int yaffs_hweight8(u8 x)
|
||||
{
|
||||
int ret_val;
|
||||
ret_val = yaffs_count_bits_table[x];
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
int yaffs_hweight32(u32 x)
|
||||
{
|
||||
return yaffs_hweight8(x & 0xff) +
|
||||
yaffs_hweight8((x >> 8) & 0xff) +
|
||||
yaffs_hweight8((x >> 16) & 0xff) +
|
||||
yaffs_hweight8((x >> 24) & 0xff);
|
||||
}
|
||||
|
208
flight/pios/common/libraries/yaffs2/yaffs_nameval.c
Normal file
208
flight/pios/common/libraries/yaffs2/yaffs_nameval.c
Normal file
@ -0,0 +1,208 @@
|
||||
/*
|
||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This simple implementation of a name-value store assumes a small number of
|
||||
* values and fits into a small finite buffer.
|
||||
*
|
||||
* Each attribute is stored as a record:
|
||||
* sizeof(int) bytes record size.
|
||||
* yaffs_strnlen+1 bytes name null terminated.
|
||||
* nbytes value.
|
||||
* ----------
|
||||
* total size stored in record size
|
||||
*
|
||||
* This code has not been tested with unicode yet.
|
||||
*/
|
||||
|
||||
#include "yaffs_nameval.h"
|
||||
|
||||
#include "yportenv.h"
|
||||
|
||||
static int nval_find(const char *xb, int xb_size, const YCHAR *name,
|
||||
int *exist_size)
|
||||
{
|
||||
int pos = 0;
|
||||
int size;
|
||||
|
||||
memcpy(&size, xb, sizeof(int));
|
||||
while (size > 0 && (size < xb_size) && (pos + size < xb_size)) {
|
||||
if (!yaffs_strncmp((YCHAR *) (xb + pos + sizeof(int)),
|
||||
name, size)) {
|
||||
if (exist_size)
|
||||
*exist_size = size;
|
||||
return pos;
|
||||
}
|
||||
pos += size;
|
||||
if (pos < xb_size - (int)sizeof(int))
|
||||
memcpy(&size, xb + pos, sizeof(int));
|
||||
else
|
||||
size = 0;
|
||||
}
|
||||
if (exist_size)
|
||||
*exist_size = 0;
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
static int nval_used(const char *xb, int xb_size)
|
||||
{
|
||||
int pos = 0;
|
||||
int size;
|
||||
|
||||
memcpy(&size, xb + pos, sizeof(int));
|
||||
while (size > 0 && (size < xb_size) && (pos + size < xb_size)) {
|
||||
pos += size;
|
||||
if (pos < xb_size - (int)sizeof(int))
|
||||
memcpy(&size, xb + pos, sizeof(int));
|
||||
else
|
||||
size = 0;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
int nval_del(char *xb, int xb_size, const YCHAR *name)
|
||||
{
|
||||
int pos = nval_find(xb, xb_size, name, NULL);
|
||||
int size;
|
||||
|
||||
if (pos < 0 || pos >= xb_size)
|
||||
return -ENODATA;
|
||||
|
||||
/* Find size, shift rest over this record,
|
||||
* then zero out the rest of buffer */
|
||||
memcpy(&size, xb + pos, sizeof(int));
|
||||
memcpy(xb + pos, xb + pos + size, xb_size - (pos + size));
|
||||
memset(xb + (xb_size - size), 0, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nval_set(char *xb, int xb_size, const YCHAR *name, const char *buf,
|
||||
int bsize, int flags)
|
||||
{
|
||||
int pos;
|
||||
int namelen = yaffs_strnlen(name, xb_size);
|
||||
int reclen;
|
||||
int size_exist = 0;
|
||||
int space;
|
||||
int start;
|
||||
|
||||
pos = nval_find(xb, xb_size, name, &size_exist);
|
||||
|
||||
if (flags & XATTR_CREATE && pos >= 0)
|
||||
return -EEXIST;
|
||||
if (flags & XATTR_REPLACE && pos < 0)
|
||||
return -ENODATA;
|
||||
|
||||
start = nval_used(xb, xb_size);
|
||||
space = xb_size - start + size_exist;
|
||||
|
||||
reclen = (sizeof(int) + namelen + 1 + bsize);
|
||||
|
||||
if (reclen > space)
|
||||
return -ENOSPC;
|
||||
|
||||
if (pos >= 0) {
|
||||
nval_del(xb, xb_size, name);
|
||||
start = nval_used(xb, xb_size);
|
||||
}
|
||||
|
||||
pos = start;
|
||||
|
||||
memcpy(xb + pos, &reclen, sizeof(int));
|
||||
pos += sizeof(int);
|
||||
yaffs_strncpy((YCHAR *) (xb + pos), name, reclen);
|
||||
pos += (namelen + 1);
|
||||
memcpy(xb + pos, buf, bsize);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nval_get(const char *xb, int xb_size, const YCHAR * name, char *buf,
|
||||
int bsize)
|
||||
{
|
||||
int pos = nval_find(xb, xb_size, name, NULL);
|
||||
int size;
|
||||
|
||||
if (pos >= 0 && pos < xb_size) {
|
||||
|
||||
memcpy(&size, xb + pos, sizeof(int));
|
||||
pos += sizeof(int); /* advance past record length */
|
||||
size -= sizeof(int);
|
||||
|
||||
/* Advance over name string */
|
||||
while (xb[pos] && size > 0 && pos < xb_size) {
|
||||
pos++;
|
||||
size--;
|
||||
}
|
||||
/*Advance over NUL */
|
||||
pos++;
|
||||
size--;
|
||||
|
||||
/* If bsize is zero then this is a size query.
|
||||
* Return the size, but don't copy.
|
||||
*/
|
||||
if (!bsize)
|
||||
return size;
|
||||
|
||||
if (size <= bsize) {
|
||||
memcpy(buf, xb + pos, size);
|
||||
return size;
|
||||
}
|
||||
}
|
||||
if (pos >= 0)
|
||||
return -ERANGE;
|
||||
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
int nval_list(const char *xb, int xb_size, char *buf, int bsize)
|
||||
{
|
||||
int pos = 0;
|
||||
int size;
|
||||
int name_len;
|
||||
int ncopied = 0;
|
||||
int filled = 0;
|
||||
|
||||
memcpy(&size, xb + pos, sizeof(int));
|
||||
while (size > (int)sizeof(int) &&
|
||||
size <= xb_size &&
|
||||
(pos + size) < xb_size &&
|
||||
!filled) {
|
||||
pos += sizeof(int);
|
||||
size -= sizeof(int);
|
||||
name_len = yaffs_strnlen((YCHAR *) (xb + pos), size);
|
||||
if (ncopied + name_len + 1 < bsize) {
|
||||
memcpy(buf, xb + pos, name_len * sizeof(YCHAR));
|
||||
buf += name_len;
|
||||
*buf = '\0';
|
||||
buf++;
|
||||
if (sizeof(YCHAR) > 1) {
|
||||
*buf = '\0';
|
||||
buf++;
|
||||
}
|
||||
ncopied += (name_len + 1);
|
||||
} else {
|
||||
filled = 1;
|
||||
}
|
||||
pos += size;
|
||||
if (pos < xb_size - (int)sizeof(int))
|
||||
memcpy(&size, xb + pos, sizeof(int));
|
||||
else
|
||||
size = 0;
|
||||
}
|
||||
return ncopied;
|
||||
}
|
||||
|
||||
int nval_hasvalues(const char *xb, int xb_size)
|
||||
{
|
||||
return nval_used(xb, xb_size) > 0;
|
||||
}
|
122
flight/pios/common/libraries/yaffs2/yaffs_nand.c
Normal file
122
flight/pios/common/libraries/yaffs2/yaffs_nand.c
Normal file
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include "yaffs_nand.h"
|
||||
#include "yaffs_tagscompat.h"
|
||||
|
||||
#include "yaffs_getblockinfo.h"
|
||||
#include "yaffs_summary.h"
|
||||
|
||||
static int apply_chunk_offset(struct yaffs_dev *dev, int chunk)
|
||||
{
|
||||
return chunk - dev->chunk_offset;
|
||||
}
|
||||
|
||||
int yaffs_rd_chunk_tags_nand(struct yaffs_dev *dev, int nand_chunk,
|
||||
u8 *buffer, struct yaffs_ext_tags *tags)
|
||||
{
|
||||
int result;
|
||||
struct yaffs_ext_tags local_tags;
|
||||
int flash_chunk = apply_chunk_offset(dev, nand_chunk);
|
||||
|
||||
dev->n_page_reads++;
|
||||
|
||||
/* If there are no tags provided use local tags. */
|
||||
if (!tags)
|
||||
tags = &local_tags;
|
||||
|
||||
result = dev->tagger.read_chunk_tags_fn(dev, flash_chunk, buffer, tags);
|
||||
if (tags && tags->ecc_result > YAFFS_ECC_RESULT_NO_ERROR) {
|
||||
|
||||
struct yaffs_block_info *bi;
|
||||
bi = yaffs_get_block_info(dev,
|
||||
nand_chunk /
|
||||
dev->param.chunks_per_block);
|
||||
yaffs_handle_chunk_error(dev, bi);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int yaffs_wr_chunk_tags_nand(struct yaffs_dev *dev,
|
||||
int nand_chunk,
|
||||
const u8 *buffer, struct yaffs_ext_tags *tags)
|
||||
{
|
||||
int result;
|
||||
int flash_chunk = apply_chunk_offset(dev, nand_chunk);
|
||||
|
||||
dev->n_page_writes++;
|
||||
|
||||
if (!tags) {
|
||||
yaffs_trace(YAFFS_TRACE_ERROR, "Writing with no tags");
|
||||
BUG();
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
|
||||
tags->seq_number = dev->seq_number;
|
||||
tags->chunk_used = 1;
|
||||
yaffs_trace(YAFFS_TRACE_WRITE,
|
||||
"Writing chunk %d tags %d %d",
|
||||
nand_chunk, tags->obj_id, tags->chunk_id);
|
||||
|
||||
result = dev->tagger.write_chunk_tags_fn(dev, flash_chunk,
|
||||
buffer, tags);
|
||||
|
||||
yaffs_summary_add(dev, tags, nand_chunk);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int yaffs_mark_bad(struct yaffs_dev *dev, int block_no)
|
||||
{
|
||||
block_no -= dev->block_offset;
|
||||
dev->n_bad_markings++;
|
||||
|
||||
if (dev->param.disable_bad_block_marking)
|
||||
return YAFFS_OK;
|
||||
|
||||
return dev->tagger.mark_bad_fn(dev, block_no);
|
||||
}
|
||||
|
||||
|
||||
int yaffs_query_init_block_state(struct yaffs_dev *dev,
|
||||
int block_no,
|
||||
enum yaffs_block_state *state,
|
||||
u32 *seq_number)
|
||||
{
|
||||
block_no -= dev->block_offset;
|
||||
return dev->tagger.query_block_fn(dev, block_no, state, seq_number);
|
||||
}
|
||||
|
||||
int yaffs_erase_block(struct yaffs_dev *dev, int block_no)
|
||||
{
|
||||
int result;
|
||||
|
||||
block_no -= dev->block_offset;
|
||||
dev->n_erasures++;
|
||||
result = dev->drv.drv_erase_fn(dev, block_no);
|
||||
return result;
|
||||
}
|
||||
|
||||
int yaffs_init_nand(struct yaffs_dev *dev)
|
||||
{
|
||||
if (dev->drv.drv_initialise_fn)
|
||||
return dev->drv.drv_initialise_fn(dev);
|
||||
return YAFFS_OK;
|
||||
}
|
||||
|
||||
int yaffs_deinit_nand(struct yaffs_dev *dev)
|
||||
{
|
||||
if (dev->drv.drv_deinitialise_fn)
|
||||
return dev->drv.drv_deinitialise_fn(dev);
|
||||
return YAFFS_OK;
|
||||
}
|
197
flight/pios/common/libraries/yaffs2/yaffs_packedtags2.c
Normal file
197
flight/pios/common/libraries/yaffs2/yaffs_packedtags2.c
Normal file
@ -0,0 +1,197 @@
|
||||
/*
|
||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include "yaffs_packedtags2.h"
|
||||
#include "yportenv.h"
|
||||
#include "yaffs_trace.h"
|
||||
|
||||
/* This code packs a set of extended tags into a binary structure for
|
||||
* NAND storage
|
||||
*/
|
||||
|
||||
/* Some of the information is "extra" struff which can be packed in to
|
||||
* speed scanning
|
||||
* This is defined by having the EXTRA_HEADER_INFO_FLAG set.
|
||||
*/
|
||||
|
||||
/* Extra flags applied to chunk_id */
|
||||
|
||||
#define EXTRA_HEADER_INFO_FLAG 0x80000000
|
||||
#define EXTRA_SHRINK_FLAG 0x40000000
|
||||
#define EXTRA_SHADOWS_FLAG 0x20000000
|
||||
#define EXTRA_SPARE_FLAGS 0x10000000
|
||||
|
||||
#define ALL_EXTRA_FLAGS 0xf0000000
|
||||
|
||||
/* Also, the top 4 bits of the object Id are set to the object type. */
|
||||
#define EXTRA_OBJECT_TYPE_SHIFT (28)
|
||||
#define EXTRA_OBJECT_TYPE_MASK ((0x0f) << EXTRA_OBJECT_TYPE_SHIFT)
|
||||
|
||||
static void yaffs_dump_packed_tags2_tags_only(
|
||||
const struct yaffs_packed_tags2_tags_only *ptt)
|
||||
{
|
||||
yaffs_trace(YAFFS_TRACE_MTD,
|
||||
"packed tags obj %d chunk %d byte %d seq %d",
|
||||
ptt->obj_id, ptt->chunk_id, ptt->n_bytes, ptt->seq_number);
|
||||
}
|
||||
|
||||
static void yaffs_dump_packed_tags2(const struct yaffs_packed_tags2 *pt)
|
||||
{
|
||||
yaffs_dump_packed_tags2_tags_only(&pt->t);
|
||||
}
|
||||
|
||||
static void yaffs_dump_tags2(const struct yaffs_ext_tags *t)
|
||||
{
|
||||
yaffs_trace(YAFFS_TRACE_MTD,
|
||||
"ext.tags eccres %d blkbad %d chused %d obj %d chunk%d byte %d del %d ser %d seq %d",
|
||||
t->ecc_result, t->block_bad, t->chunk_used, t->obj_id,
|
||||
t->chunk_id, t->n_bytes, t->is_deleted, t->serial_number,
|
||||
t->seq_number);
|
||||
|
||||
}
|
||||
|
||||
static int yaffs_check_tags_extra_packable(const struct yaffs_ext_tags *t)
|
||||
{
|
||||
if (t->chunk_id != 0 || !t->extra_available)
|
||||
return 0;
|
||||
|
||||
/* Check if the file size is too long to store */
|
||||
if (t->extra_obj_type == YAFFS_OBJECT_TYPE_FILE &&
|
||||
(t->extra_file_size >> 31) != 0)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void yaffs_pack_tags2_tags_only(struct yaffs_packed_tags2_tags_only *ptt,
|
||||
const struct yaffs_ext_tags *t)
|
||||
{
|
||||
ptt->chunk_id = t->chunk_id;
|
||||
ptt->seq_number = t->seq_number;
|
||||
ptt->n_bytes = t->n_bytes;
|
||||
ptt->obj_id = t->obj_id;
|
||||
|
||||
/* Only store extra tags for object headers.
|
||||
* If it is a file then only store if the file size is short\
|
||||
* enough to fit.
|
||||
*/
|
||||
if (yaffs_check_tags_extra_packable(t)) {
|
||||
/* Store the extra header info instead */
|
||||
/* We save the parent object in the chunk_id */
|
||||
ptt->chunk_id = EXTRA_HEADER_INFO_FLAG | t->extra_parent_id;
|
||||
if (t->extra_is_shrink)
|
||||
ptt->chunk_id |= EXTRA_SHRINK_FLAG;
|
||||
if (t->extra_shadows)
|
||||
ptt->chunk_id |= EXTRA_SHADOWS_FLAG;
|
||||
|
||||
ptt->obj_id &= ~EXTRA_OBJECT_TYPE_MASK;
|
||||
ptt->obj_id |= (t->extra_obj_type << EXTRA_OBJECT_TYPE_SHIFT);
|
||||
|
||||
if (t->extra_obj_type == YAFFS_OBJECT_TYPE_HARDLINK)
|
||||
ptt->n_bytes = t->extra_equiv_id;
|
||||
else if (t->extra_obj_type == YAFFS_OBJECT_TYPE_FILE)
|
||||
ptt->n_bytes = (unsigned) t->extra_file_size;
|
||||
else
|
||||
ptt->n_bytes = 0;
|
||||
}
|
||||
|
||||
yaffs_dump_packed_tags2_tags_only(ptt);
|
||||
yaffs_dump_tags2(t);
|
||||
}
|
||||
|
||||
void yaffs_pack_tags2(struct yaffs_packed_tags2 *pt,
|
||||
const struct yaffs_ext_tags *t, int tags_ecc)
|
||||
{
|
||||
yaffs_pack_tags2_tags_only(&pt->t, t);
|
||||
|
||||
if (tags_ecc)
|
||||
yaffs_ecc_calc_other((unsigned char *)&pt->t,
|
||||
sizeof(struct yaffs_packed_tags2_tags_only),
|
||||
&pt->ecc);
|
||||
}
|
||||
|
||||
void yaffs_unpack_tags2_tags_only(struct yaffs_ext_tags *t,
|
||||
struct yaffs_packed_tags2_tags_only *ptt)
|
||||
{
|
||||
memset(t, 0, sizeof(struct yaffs_ext_tags));
|
||||
|
||||
if (ptt->seq_number == 0xffffffff)
|
||||
return;
|
||||
|
||||
t->block_bad = 0;
|
||||
t->chunk_used = 1;
|
||||
t->obj_id = ptt->obj_id;
|
||||
t->chunk_id = ptt->chunk_id;
|
||||
t->n_bytes = ptt->n_bytes;
|
||||
t->is_deleted = 0;
|
||||
t->serial_number = 0;
|
||||
t->seq_number = ptt->seq_number;
|
||||
|
||||
/* Do extra header info stuff */
|
||||
if (ptt->chunk_id & EXTRA_HEADER_INFO_FLAG) {
|
||||
t->chunk_id = 0;
|
||||
t->n_bytes = 0;
|
||||
|
||||
t->extra_available = 1;
|
||||
t->extra_parent_id = ptt->chunk_id & (~(ALL_EXTRA_FLAGS));
|
||||
t->extra_is_shrink = ptt->chunk_id & EXTRA_SHRINK_FLAG ? 1 : 0;
|
||||
t->extra_shadows = ptt->chunk_id & EXTRA_SHADOWS_FLAG ? 1 : 0;
|
||||
t->extra_obj_type = ptt->obj_id >> EXTRA_OBJECT_TYPE_SHIFT;
|
||||
t->obj_id &= ~EXTRA_OBJECT_TYPE_MASK;
|
||||
|
||||
if (t->extra_obj_type == YAFFS_OBJECT_TYPE_HARDLINK)
|
||||
t->extra_equiv_id = ptt->n_bytes;
|
||||
else
|
||||
t->extra_file_size = ptt->n_bytes;
|
||||
}
|
||||
yaffs_dump_packed_tags2_tags_only(ptt);
|
||||
yaffs_dump_tags2(t);
|
||||
}
|
||||
|
||||
void yaffs_unpack_tags2(struct yaffs_ext_tags *t, struct yaffs_packed_tags2 *pt,
|
||||
int tags_ecc)
|
||||
{
|
||||
enum yaffs_ecc_result ecc_result = YAFFS_ECC_RESULT_NO_ERROR;
|
||||
|
||||
if (pt->t.seq_number != 0xffffffff && tags_ecc) {
|
||||
/* Chunk is in use and we need to do ECC */
|
||||
|
||||
struct yaffs_ecc_other ecc;
|
||||
int result;
|
||||
yaffs_ecc_calc_other((unsigned char *)&pt->t,
|
||||
sizeof(struct yaffs_packed_tags2_tags_only),
|
||||
&ecc);
|
||||
result =
|
||||
yaffs_ecc_correct_other((unsigned char *)&pt->t,
|
||||
sizeof(struct yaffs_packed_tags2_tags_only),
|
||||
&pt->ecc, &ecc);
|
||||
switch (result) {
|
||||
case 0:
|
||||
ecc_result = YAFFS_ECC_RESULT_NO_ERROR;
|
||||
break;
|
||||
case 1:
|
||||
ecc_result = YAFFS_ECC_RESULT_FIXED;
|
||||
break;
|
||||
case -1:
|
||||
ecc_result = YAFFS_ECC_RESULT_UNFIXED;
|
||||
break;
|
||||
default:
|
||||
ecc_result = YAFFS_ECC_RESULT_UNKNOWN;
|
||||
}
|
||||
}
|
||||
yaffs_unpack_tags2_tags_only(t, &pt->t);
|
||||
|
||||
t->ecc_result = ecc_result;
|
||||
|
||||
yaffs_dump_packed_tags2(pt);
|
||||
yaffs_dump_tags2(t);
|
||||
}
|
163
flight/pios/common/libraries/yaffs2/yaffs_qsort.c
Normal file
163
flight/pios/common/libraries/yaffs2/yaffs_qsort.c
Normal file
@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Copyright (c) 1992, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* 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 of the University 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 REGENTS 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 REGENTS 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.
|
||||
*/
|
||||
|
||||
#include "yportenv.h"
|
||||
/* #include <linux/string.h> */
|
||||
|
||||
/*
|
||||
* Qsort routine from Bentley & McIlroy's "Engineering a Sort Function".
|
||||
*/
|
||||
#define swapcode(TYPE, parmi, parmj, n) do { \
|
||||
long i = (n) / sizeof(TYPE); \
|
||||
register TYPE *pi = (TYPE *) (parmi); \
|
||||
register TYPE *pj = (TYPE *) (parmj); \
|
||||
do { \
|
||||
register TYPE t = *pi; \
|
||||
*pi++ = *pj; \
|
||||
*pj++ = t; \
|
||||
} while (--i > 0); \
|
||||
} while (0)
|
||||
|
||||
#define SWAPINIT(a, es) swaptype = ((char *)a - (char *)0) % sizeof(long) || \
|
||||
es % sizeof(long) ? 2 : es == sizeof(long) ? 0 : 1;
|
||||
|
||||
static inline void
|
||||
swapfunc(char *a, char *b, int n, int swaptype)
|
||||
{
|
||||
if (swaptype <= 1)
|
||||
swapcode(long, a, b, n);
|
||||
else
|
||||
swapcode(char, a, b, n);
|
||||
}
|
||||
|
||||
#define yswap(a, b) do { \
|
||||
if (swaptype == 0) { \
|
||||
long t = *(long *)(a); \
|
||||
*(long *)(a) = *(long *)(b); \
|
||||
*(long *)(b) = t; \
|
||||
} else \
|
||||
swapfunc(a, b, es, swaptype); \
|
||||
} while (0)
|
||||
|
||||
#define vecswap(a, b, n) if ((n) > 0) swapfunc(a, b, n, swaptype)
|
||||
|
||||
static inline char *
|
||||
med3(char *a, char *b, char *c, int (*cmp)(const void *, const void *))
|
||||
{
|
||||
return cmp(a, b) < 0 ?
|
||||
(cmp(b, c) < 0 ? b : (cmp(a, c) < 0 ? c : a))
|
||||
: (cmp(b, c) > 0 ? b : (cmp(a, c) < 0 ? a : c));
|
||||
}
|
||||
|
||||
#ifndef min
|
||||
#define min(a, b) (((a) < (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
void
|
||||
yaffs_qsort(void *aa, size_t n, size_t es,
|
||||
int (*cmp)(const void *, const void *))
|
||||
{
|
||||
char *pa, *pb, *pc, *pd, *pl, *pm, *pn;
|
||||
int d, r, swaptype, swap_cnt;
|
||||
register char *a = aa;
|
||||
|
||||
loop: SWAPINIT(a, es);
|
||||
swap_cnt = 0;
|
||||
if (n < 7) {
|
||||
for (pm = (char *)a + es; pm < (char *) a + n * es; pm += es)
|
||||
for (pl = pm; pl > (char *) a && cmp(pl - es, pl) > 0;
|
||||
pl -= es)
|
||||
yswap(pl, pl - es);
|
||||
return;
|
||||
}
|
||||
pm = (char *)a + (n / 2) * es;
|
||||
if (n > 7) {
|
||||
pl = (char *)a;
|
||||
pn = (char *)a + (n - 1) * es;
|
||||
if (n > 40) {
|
||||
d = (n / 8) * es;
|
||||
pl = med3(pl, pl + d, pl + 2 * d, cmp);
|
||||
pm = med3(pm - d, pm, pm + d, cmp);
|
||||
pn = med3(pn - 2 * d, pn - d, pn, cmp);
|
||||
}
|
||||
pm = med3(pl, pm, pn, cmp);
|
||||
}
|
||||
yswap(a, pm);
|
||||
pa = pb = (char *)a + es;
|
||||
|
||||
pc = pd = (char *)a + (n - 1) * es;
|
||||
for (;;) {
|
||||
while (pb <= pc && (r = cmp(pb, a)) <= 0) {
|
||||
if (r == 0) {
|
||||
swap_cnt = 1;
|
||||
yswap(pa, pb);
|
||||
pa += es;
|
||||
}
|
||||
pb += es;
|
||||
}
|
||||
while (pb <= pc && (r = cmp(pc, a)) >= 0) {
|
||||
if (r == 0) {
|
||||
swap_cnt = 1;
|
||||
yswap(pc, pd);
|
||||
pd -= es;
|
||||
}
|
||||
pc -= es;
|
||||
}
|
||||
if (pb > pc)
|
||||
break;
|
||||
yswap(pb, pc);
|
||||
swap_cnt = 1;
|
||||
pb += es;
|
||||
pc -= es;
|
||||
}
|
||||
if (swap_cnt == 0) { /* Switch to insertion sort */
|
||||
for (pm = (char *) a + es; pm < (char *) a + n * es; pm += es)
|
||||
for (pl = pm; pl > (char *) a && cmp(pl - es, pl) > 0;
|
||||
pl -= es)
|
||||
yswap(pl, pl - es);
|
||||
return;
|
||||
}
|
||||
|
||||
pn = (char *)a + n * es;
|
||||
r = min(pa - (char *)a, pb - pa);
|
||||
vecswap(a, pb - r, r);
|
||||
r = min((long)(pd - pc), (long)(pn - pd - es));
|
||||
vecswap(pb, pn - r, r);
|
||||
r = pb - pa;
|
||||
if (r > (int)es)
|
||||
yaffs_qsort(a, r / es, es, cmp);
|
||||
r = pd - pc;
|
||||
if (r > (int)es) {
|
||||
/* Iterate rather than recurse to save stack space */
|
||||
a = pn - r;
|
||||
n = r / es;
|
||||
goto loop;
|
||||
}
|
||||
/* yaffs_qsort(pn - r, r / es, es, cmp);*/
|
||||
}
|
315
flight/pios/common/libraries/yaffs2/yaffs_summary.c
Normal file
315
flight/pios/common/libraries/yaffs2/yaffs_summary.c
Normal file
@ -0,0 +1,315 @@
|
||||
/*
|
||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
/* Summaries write the useful part of the tags for the chunks in a block into an
|
||||
* an array which is written to the last n chunks of the block.
|
||||
* Reading the summaries gives all the tags for the block in one read. Much
|
||||
* faster.
|
||||
*
|
||||
* Chunks holding summaries are marked with tags making it look like
|
||||
* they are part of a fake file.
|
||||
*
|
||||
* The summary could also be used during gc.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "yaffs_summary.h"
|
||||
#include "yaffs_packedtags2.h"
|
||||
#include "yaffs_nand.h"
|
||||
#include "yaffs_getblockinfo.h"
|
||||
#include "yaffs_bitmap.h"
|
||||
|
||||
/*
|
||||
* The summary is built up in an array of summary tags.
|
||||
* This gets written to the last one or two (maybe more) chunks in a block.
|
||||
* A summary header is written as the first part of each chunk of summary data.
|
||||
* The summary header must match or the summary is rejected.
|
||||
*/
|
||||
|
||||
/* Summary tags don't need the sequence number because that is redundant. */
|
||||
struct yaffs_summary_tags {
|
||||
unsigned obj_id;
|
||||
unsigned chunk_id;
|
||||
unsigned n_bytes;
|
||||
};
|
||||
|
||||
/* Summary header */
|
||||
struct yaffs_summary_header {
|
||||
unsigned version; /* Must match current version */
|
||||
unsigned block; /* Must be this block */
|
||||
unsigned seq; /* Must be this sequence number */
|
||||
unsigned sum; /* Just add up all the bytes in the tags */
|
||||
};
|
||||
|
||||
|
||||
static void yaffs_summary_clear(struct yaffs_dev *dev)
|
||||
{
|
||||
if (!dev->sum_tags)
|
||||
return;
|
||||
memset(dev->sum_tags, 0, dev->chunks_per_summary *
|
||||
sizeof(struct yaffs_summary_tags));
|
||||
}
|
||||
|
||||
|
||||
void yaffs_summary_deinit(struct yaffs_dev *dev)
|
||||
{
|
||||
kfree(dev->sum_tags);
|
||||
dev->sum_tags = NULL;
|
||||
kfree(dev->gc_sum_tags);
|
||||
dev->gc_sum_tags = NULL;
|
||||
dev->chunks_per_summary = 0;
|
||||
}
|
||||
|
||||
int yaffs_summary_init(struct yaffs_dev *dev)
|
||||
{
|
||||
int sum_bytes;
|
||||
int chunks_used; /* Number of chunks used by summary */
|
||||
int sum_tags_bytes;
|
||||
|
||||
sum_bytes = dev->param.chunks_per_block *
|
||||
sizeof(struct yaffs_summary_tags);
|
||||
|
||||
chunks_used = (sum_bytes + dev->data_bytes_per_chunk - 1)/
|
||||
(dev->data_bytes_per_chunk -
|
||||
sizeof(struct yaffs_summary_header));
|
||||
|
||||
dev->chunks_per_summary = dev->param.chunks_per_block - chunks_used;
|
||||
sum_tags_bytes = sizeof(struct yaffs_summary_tags) *
|
||||
dev->chunks_per_summary;
|
||||
dev->sum_tags = kmalloc(sum_tags_bytes, GFP_NOFS);
|
||||
dev->gc_sum_tags = kmalloc(sum_tags_bytes, GFP_NOFS);
|
||||
if (!dev->sum_tags || !dev->gc_sum_tags) {
|
||||
yaffs_summary_deinit(dev);
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
|
||||
yaffs_summary_clear(dev);
|
||||
|
||||
return YAFFS_OK;
|
||||
}
|
||||
|
||||
static unsigned yaffs_summary_sum(struct yaffs_dev *dev)
|
||||
{
|
||||
u8 *sum_buffer = (u8 *)dev->sum_tags;
|
||||
int i;
|
||||
unsigned sum = 0;
|
||||
|
||||
i = sizeof(struct yaffs_summary_tags) *
|
||||
dev->chunks_per_summary;
|
||||
while (i > 0) {
|
||||
sum += *sum_buffer;
|
||||
sum_buffer++;
|
||||
i--;
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
static int yaffs_summary_write(struct yaffs_dev *dev, int blk)
|
||||
{
|
||||
struct yaffs_ext_tags tags;
|
||||
u8 *buffer;
|
||||
u8 *sum_buffer = (u8 *)dev->sum_tags;
|
||||
int n_bytes;
|
||||
int chunk_in_nand;
|
||||
int chunk_in_block;
|
||||
int result;
|
||||
int this_tx;
|
||||
struct yaffs_summary_header hdr;
|
||||
int sum_bytes_per_chunk = dev->data_bytes_per_chunk - sizeof(hdr);
|
||||
struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk);
|
||||
|
||||
buffer = yaffs_get_temp_buffer(dev);
|
||||
n_bytes = sizeof(struct yaffs_summary_tags) *
|
||||
dev->chunks_per_summary;
|
||||
memset(&tags, 0, sizeof(struct yaffs_ext_tags));
|
||||
tags.obj_id = YAFFS_OBJECTID_SUMMARY;
|
||||
tags.chunk_id = 1;
|
||||
chunk_in_block = dev->chunks_per_summary;
|
||||
chunk_in_nand = dev->alloc_block * dev->param.chunks_per_block +
|
||||
dev->chunks_per_summary;
|
||||
hdr.version = YAFFS_SUMMARY_VERSION;
|
||||
hdr.block = blk;
|
||||
hdr.seq = bi->seq_number;
|
||||
hdr.sum = yaffs_summary_sum(dev);
|
||||
|
||||
do {
|
||||
this_tx = n_bytes;
|
||||
if (this_tx > sum_bytes_per_chunk)
|
||||
this_tx = sum_bytes_per_chunk;
|
||||
memcpy(buffer, &hdr, sizeof(hdr));
|
||||
memcpy(buffer + sizeof(hdr), sum_buffer, this_tx);
|
||||
tags.n_bytes = this_tx + sizeof(hdr);
|
||||
result = yaffs_wr_chunk_tags_nand(dev, chunk_in_nand,
|
||||
buffer, &tags);
|
||||
|
||||
if (result != YAFFS_OK)
|
||||
break;
|
||||
yaffs_set_chunk_bit(dev, blk, chunk_in_block);
|
||||
bi->pages_in_use++;
|
||||
dev->n_free_chunks--;
|
||||
|
||||
n_bytes -= this_tx;
|
||||
sum_buffer += this_tx;
|
||||
chunk_in_nand++;
|
||||
chunk_in_block++;
|
||||
tags.chunk_id++;
|
||||
} while (result == YAFFS_OK && n_bytes > 0);
|
||||
yaffs_release_temp_buffer(dev, buffer);
|
||||
|
||||
|
||||
if (result == YAFFS_OK)
|
||||
bi->has_summary = 1;
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int yaffs_summary_read(struct yaffs_dev *dev,
|
||||
struct yaffs_summary_tags *st,
|
||||
int blk)
|
||||
{
|
||||
struct yaffs_ext_tags tags;
|
||||
u8 *buffer;
|
||||
u8 *sum_buffer = (u8 *)st;
|
||||
int n_bytes;
|
||||
int chunk_id;
|
||||
int chunk_in_nand;
|
||||
int chunk_in_block;
|
||||
int result;
|
||||
int this_tx;
|
||||
struct yaffs_summary_header hdr;
|
||||
struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk);
|
||||
int sum_bytes_per_chunk = dev->data_bytes_per_chunk - sizeof(hdr);
|
||||
#ifndef CONFIG_YAFFS_OP
|
||||
int sum_tags_bytes;
|
||||
|
||||
sum_tags_bytes = sizeof(struct yaffs_summary_tags) *
|
||||
dev->chunks_per_summary;
|
||||
#endif //unused code
|
||||
|
||||
buffer = yaffs_get_temp_buffer(dev);
|
||||
n_bytes = sizeof(struct yaffs_summary_tags) * dev->chunks_per_summary;
|
||||
chunk_in_block = dev->chunks_per_summary;
|
||||
chunk_in_nand = blk * dev->param.chunks_per_block +
|
||||
dev->chunks_per_summary;
|
||||
chunk_id = 1;
|
||||
do {
|
||||
this_tx = n_bytes;
|
||||
if (this_tx > sum_bytes_per_chunk)
|
||||
this_tx = sum_bytes_per_chunk;
|
||||
result = yaffs_rd_chunk_tags_nand(dev, chunk_in_nand,
|
||||
buffer, &tags);
|
||||
|
||||
if ((int)tags.chunk_id != chunk_id ||
|
||||
tags.obj_id != YAFFS_OBJECTID_SUMMARY ||
|
||||
tags.chunk_used == 0 ||
|
||||
tags.ecc_result > YAFFS_ECC_RESULT_FIXED ||
|
||||
tags.n_bytes != (this_tx + sizeof(hdr)))
|
||||
result = YAFFS_FAIL;
|
||||
if (result != YAFFS_OK)
|
||||
break;
|
||||
|
||||
if (st == dev->sum_tags) {
|
||||
/* If we're scanning then update the block info */
|
||||
yaffs_set_chunk_bit(dev, blk, chunk_in_block);
|
||||
bi->pages_in_use++;
|
||||
}
|
||||
memcpy(&hdr, buffer, sizeof(hdr));
|
||||
memcpy(sum_buffer, buffer + sizeof(hdr), this_tx);
|
||||
n_bytes -= this_tx;
|
||||
sum_buffer += this_tx;
|
||||
chunk_in_nand++;
|
||||
chunk_in_block++;
|
||||
chunk_id++;
|
||||
} while (result == YAFFS_OK && n_bytes > 0);
|
||||
yaffs_release_temp_buffer(dev, buffer);
|
||||
|
||||
if (result == YAFFS_OK) {
|
||||
/* Verify header */
|
||||
if (hdr.version != YAFFS_SUMMARY_VERSION ||
|
||||
hdr.seq != bi->seq_number ||
|
||||
hdr.sum != yaffs_summary_sum(dev))
|
||||
result = YAFFS_FAIL;
|
||||
}
|
||||
|
||||
if (st == dev->sum_tags && result == YAFFS_OK)
|
||||
bi->has_summary = 1;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int yaffs_summary_add(struct yaffs_dev *dev,
|
||||
struct yaffs_ext_tags *tags,
|
||||
int chunk_in_nand)
|
||||
{
|
||||
struct yaffs_packed_tags2_tags_only tags_only;
|
||||
struct yaffs_summary_tags *sum_tags;
|
||||
int block_in_nand = chunk_in_nand / dev->param.chunks_per_block;
|
||||
int chunk_in_block = chunk_in_nand % dev->param.chunks_per_block;
|
||||
|
||||
if (!dev->sum_tags)
|
||||
return YAFFS_OK;
|
||||
|
||||
if (chunk_in_block >= 0 && chunk_in_block < dev->chunks_per_summary) {
|
||||
yaffs_pack_tags2_tags_only(&tags_only, tags);
|
||||
sum_tags = &dev->sum_tags[chunk_in_block];
|
||||
sum_tags->chunk_id = tags_only.chunk_id;
|
||||
sum_tags->n_bytes = tags_only.n_bytes;
|
||||
sum_tags->obj_id = tags_only.obj_id;
|
||||
|
||||
if (chunk_in_block == dev->chunks_per_summary - 1) {
|
||||
/* Time to write out the summary */
|
||||
yaffs_summary_write(dev, block_in_nand);
|
||||
yaffs_summary_clear(dev);
|
||||
yaffs_skip_rest_of_block(dev);
|
||||
}
|
||||
}
|
||||
return YAFFS_OK;
|
||||
}
|
||||
|
||||
int yaffs_summary_fetch(struct yaffs_dev *dev,
|
||||
struct yaffs_ext_tags *tags,
|
||||
int chunk_in_block)
|
||||
{
|
||||
struct yaffs_packed_tags2_tags_only tags_only;
|
||||
struct yaffs_summary_tags *sum_tags;
|
||||
if (chunk_in_block >= 0 && chunk_in_block < dev->chunks_per_summary) {
|
||||
sum_tags = &dev->sum_tags[chunk_in_block];
|
||||
tags_only.chunk_id = sum_tags->chunk_id;
|
||||
tags_only.n_bytes = sum_tags->n_bytes;
|
||||
tags_only.obj_id = sum_tags->obj_id;
|
||||
yaffs_unpack_tags2_tags_only(tags, &tags_only);
|
||||
return YAFFS_OK;
|
||||
}
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
|
||||
void yaffs_summary_gc(struct yaffs_dev *dev, int blk)
|
||||
{
|
||||
struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk);
|
||||
int i;
|
||||
|
||||
if (!bi->has_summary)
|
||||
return;
|
||||
|
||||
for (i = dev->chunks_per_summary;
|
||||
i < dev->param.chunks_per_block;
|
||||
i++) {
|
||||
if (yaffs_check_chunk_bit(dev, blk, i)) {
|
||||
yaffs_clear_chunk_bit(dev, blk, i);
|
||||
bi->pages_in_use--;
|
||||
dev->n_free_chunks++;
|
||||
}
|
||||
}
|
||||
}
|
381
flight/pios/common/libraries/yaffs2/yaffs_tagscompat.c
Normal file
381
flight/pios/common/libraries/yaffs2/yaffs_tagscompat.c
Normal file
@ -0,0 +1,381 @@
|
||||
/*
|
||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include "yaffs_guts.h"
|
||||
#include "yaffs_tagscompat.h"
|
||||
#include "yaffs_ecc.h"
|
||||
#include "yaffs_getblockinfo.h"
|
||||
#include "yaffs_trace.h"
|
||||
|
||||
static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk);
|
||||
|
||||
|
||||
/********** Tags ECC calculations *********/
|
||||
|
||||
|
||||
void yaffs_calc_tags_ecc(struct yaffs_tags *tags)
|
||||
{
|
||||
/* Calculate an ecc */
|
||||
unsigned char *b = ((union yaffs_tags_union *)tags)->as_bytes;
|
||||
unsigned i, j;
|
||||
unsigned ecc = 0;
|
||||
unsigned bit = 0;
|
||||
|
||||
tags->ecc = 0;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
for (j = 1; j & 0xff; j <<= 1) {
|
||||
bit++;
|
||||
if (b[i] & j)
|
||||
ecc ^= bit;
|
||||
}
|
||||
}
|
||||
tags->ecc = ecc;
|
||||
}
|
||||
|
||||
int yaffs_check_tags_ecc(struct yaffs_tags *tags)
|
||||
{
|
||||
unsigned ecc = tags->ecc;
|
||||
|
||||
yaffs_calc_tags_ecc(tags);
|
||||
|
||||
ecc ^= tags->ecc;
|
||||
|
||||
if (ecc && ecc <= 64) {
|
||||
/* TODO: Handle the failure better. Retire? */
|
||||
unsigned char *b = ((union yaffs_tags_union *)tags)->as_bytes;
|
||||
|
||||
ecc--;
|
||||
|
||||
b[ecc / 8] ^= (1 << (ecc & 7));
|
||||
|
||||
/* Now recvalc the ecc */
|
||||
yaffs_calc_tags_ecc(tags);
|
||||
|
||||
return 1; /* recovered error */
|
||||
} else if (ecc) {
|
||||
/* Wierd ecc failure value */
|
||||
/* TODO Need to do somethiong here */
|
||||
return -1; /* unrecovered error */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/********** Tags **********/
|
||||
|
||||
static void yaffs_load_tags_to_spare(struct yaffs_spare *spare_ptr,
|
||||
struct yaffs_tags *tags_ptr)
|
||||
{
|
||||
union yaffs_tags_union *tu = (union yaffs_tags_union *)tags_ptr;
|
||||
|
||||
yaffs_calc_tags_ecc(tags_ptr);
|
||||
|
||||
spare_ptr->tb0 = tu->as_bytes[0];
|
||||
spare_ptr->tb1 = tu->as_bytes[1];
|
||||
spare_ptr->tb2 = tu->as_bytes[2];
|
||||
spare_ptr->tb3 = tu->as_bytes[3];
|
||||
spare_ptr->tb4 = tu->as_bytes[4];
|
||||
spare_ptr->tb5 = tu->as_bytes[5];
|
||||
spare_ptr->tb6 = tu->as_bytes[6];
|
||||
spare_ptr->tb7 = tu->as_bytes[7];
|
||||
}
|
||||
|
||||
static void yaffs_get_tags_from_spare(struct yaffs_dev *dev,
|
||||
struct yaffs_spare *spare_ptr,
|
||||
struct yaffs_tags *tags_ptr)
|
||||
{
|
||||
union yaffs_tags_union *tu = (union yaffs_tags_union *)tags_ptr;
|
||||
int result;
|
||||
|
||||
tu->as_bytes[0] = spare_ptr->tb0;
|
||||
tu->as_bytes[1] = spare_ptr->tb1;
|
||||
tu->as_bytes[2] = spare_ptr->tb2;
|
||||
tu->as_bytes[3] = spare_ptr->tb3;
|
||||
tu->as_bytes[4] = spare_ptr->tb4;
|
||||
tu->as_bytes[5] = spare_ptr->tb5;
|
||||
tu->as_bytes[6] = spare_ptr->tb6;
|
||||
tu->as_bytes[7] = spare_ptr->tb7;
|
||||
|
||||
result = yaffs_check_tags_ecc(tags_ptr);
|
||||
if (result > 0)
|
||||
dev->n_tags_ecc_fixed++;
|
||||
else if (result < 0)
|
||||
dev->n_tags_ecc_unfixed++;
|
||||
}
|
||||
|
||||
static void yaffs_spare_init(struct yaffs_spare *spare)
|
||||
{
|
||||
memset(spare, 0xff, sizeof(struct yaffs_spare));
|
||||
}
|
||||
|
||||
static int yaffs_wr_nand(struct yaffs_dev *dev,
|
||||
int nand_chunk, const u8 *data,
|
||||
struct yaffs_spare *spare)
|
||||
{
|
||||
int data_size = dev->data_bytes_per_chunk;
|
||||
|
||||
return dev->drv.drv_write_chunk_fn(dev, nand_chunk,
|
||||
data, data_size,
|
||||
(u8 *) spare, sizeof(*spare));
|
||||
}
|
||||
|
||||
static int yaffs_rd_chunk_nand(struct yaffs_dev *dev,
|
||||
int nand_chunk,
|
||||
u8 *data,
|
||||
struct yaffs_spare *spare,
|
||||
enum yaffs_ecc_result *ecc_result,
|
||||
int correct_errors)
|
||||
{
|
||||
int ret_val;
|
||||
struct yaffs_spare local_spare;
|
||||
int data_size;
|
||||
int spare_size;
|
||||
int ecc_result1, ecc_result2;
|
||||
u8 calc_ecc[3];
|
||||
|
||||
if (!spare) {
|
||||
/* If we don't have a real spare, then we use a local one. */
|
||||
/* Need this for the calculation of the ecc */
|
||||
spare = &local_spare;
|
||||
}
|
||||
data_size = dev->data_bytes_per_chunk;
|
||||
spare_size = sizeof(struct yaffs_spare);
|
||||
|
||||
if (dev->param.use_nand_ecc)
|
||||
return dev->drv.drv_read_chunk_fn(dev, nand_chunk,
|
||||
data, data_size,
|
||||
(u8 *) spare, spare_size,
|
||||
ecc_result);
|
||||
|
||||
|
||||
/* Handle the ECC at this level. */
|
||||
|
||||
ret_val = dev->drv.drv_read_chunk_fn(dev, nand_chunk,
|
||||
data, data_size,
|
||||
(u8 *)spare, spare_size,
|
||||
NULL);
|
||||
if (!data || !correct_errors)
|
||||
return ret_val;
|
||||
|
||||
/* Do ECC correction if needed. */
|
||||
yaffs_ecc_calc(data, calc_ecc);
|
||||
ecc_result1 = yaffs_ecc_correct(data, spare->ecc1, calc_ecc);
|
||||
yaffs_ecc_calc(&data[256], calc_ecc);
|
||||
ecc_result2 = yaffs_ecc_correct(&data[256], spare->ecc2, calc_ecc);
|
||||
|
||||
if (ecc_result1 > 0) {
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"**>>yaffs ecc error fix performed on chunk %d:0",
|
||||
nand_chunk);
|
||||
dev->n_ecc_fixed++;
|
||||
} else if (ecc_result1 < 0) {
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"**>>yaffs ecc error unfixed on chunk %d:0",
|
||||
nand_chunk);
|
||||
dev->n_ecc_unfixed++;
|
||||
}
|
||||
|
||||
if (ecc_result2 > 0) {
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"**>>yaffs ecc error fix performed on chunk %d:1",
|
||||
nand_chunk);
|
||||
dev->n_ecc_fixed++;
|
||||
} else if (ecc_result2 < 0) {
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"**>>yaffs ecc error unfixed on chunk %d:1",
|
||||
nand_chunk);
|
||||
dev->n_ecc_unfixed++;
|
||||
}
|
||||
|
||||
if (ecc_result1 || ecc_result2) {
|
||||
/* We had a data problem on this page */
|
||||
yaffs_handle_rd_data_error(dev, nand_chunk);
|
||||
}
|
||||
|
||||
if (ecc_result1 < 0 || ecc_result2 < 0)
|
||||
*ecc_result = YAFFS_ECC_RESULT_UNFIXED;
|
||||
else if (ecc_result1 > 0 || ecc_result2 > 0)
|
||||
*ecc_result = YAFFS_ECC_RESULT_FIXED;
|
||||
else
|
||||
*ecc_result = YAFFS_ECC_RESULT_NO_ERROR;
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
/*
|
||||
* Functions for robustisizing
|
||||
*/
|
||||
|
||||
static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk)
|
||||
{
|
||||
int flash_block = nand_chunk / dev->param.chunks_per_block;
|
||||
|
||||
/* Mark the block for retirement */
|
||||
yaffs_get_block_info(dev, flash_block + dev->block_offset)->
|
||||
needs_retiring = 1;
|
||||
yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,
|
||||
"**>>Block %d marked for retirement",
|
||||
flash_block);
|
||||
|
||||
/* TODO:
|
||||
* Just do a garbage collection on the affected block
|
||||
* then retire the block
|
||||
* NB recursion
|
||||
*/
|
||||
}
|
||||
|
||||
static int yaffs_tags_compat_wr(struct yaffs_dev *dev,
|
||||
int nand_chunk,
|
||||
const u8 *data, const struct yaffs_ext_tags *ext_tags)
|
||||
{
|
||||
struct yaffs_spare spare;
|
||||
struct yaffs_tags tags;
|
||||
|
||||
yaffs_spare_init(&spare);
|
||||
|
||||
if (ext_tags->is_deleted)
|
||||
spare.page_status = 0;
|
||||
else {
|
||||
tags.obj_id = ext_tags->obj_id;
|
||||
tags.chunk_id = ext_tags->chunk_id;
|
||||
|
||||
tags.n_bytes_lsb = ext_tags->n_bytes & (1024 - 1);
|
||||
|
||||
if (dev->data_bytes_per_chunk >= 1024)
|
||||
tags.n_bytes_msb = (ext_tags->n_bytes >> 10) & 3;
|
||||
else
|
||||
tags.n_bytes_msb = 3;
|
||||
|
||||
tags.serial_number = ext_tags->serial_number;
|
||||
|
||||
if (!dev->param.use_nand_ecc && data) {
|
||||
yaffs_ecc_calc(data, spare.ecc1);
|
||||
yaffs_ecc_calc(&data[256], spare.ecc2);
|
||||
}
|
||||
|
||||
yaffs_load_tags_to_spare(&spare, &tags);
|
||||
}
|
||||
return yaffs_wr_nand(dev, nand_chunk, data, &spare);
|
||||
}
|
||||
|
||||
static int yaffs_tags_compat_rd(struct yaffs_dev *dev,
|
||||
int nand_chunk,
|
||||
u8 *data, struct yaffs_ext_tags *ext_tags)
|
||||
{
|
||||
struct yaffs_spare spare;
|
||||
struct yaffs_tags tags;
|
||||
enum yaffs_ecc_result ecc_result = YAFFS_ECC_RESULT_UNKNOWN;
|
||||
static struct yaffs_spare spare_ff;
|
||||
static int init;
|
||||
int deleted;
|
||||
|
||||
if (!init) {
|
||||
memset(&spare_ff, 0xff, sizeof(spare_ff));
|
||||
init = 1;
|
||||
}
|
||||
|
||||
if (!yaffs_rd_chunk_nand(dev, nand_chunk,
|
||||
data, &spare, &ecc_result, 1))
|
||||
return YAFFS_FAIL;
|
||||
|
||||
/* ext_tags may be NULL */
|
||||
if (!ext_tags)
|
||||
return YAFFS_OK;
|
||||
|
||||
deleted = (hweight8(spare.page_status) < 7) ? 1 : 0;
|
||||
|
||||
ext_tags->is_deleted = deleted;
|
||||
ext_tags->ecc_result = ecc_result;
|
||||
ext_tags->block_bad = 0; /* We're reading it */
|
||||
/* therefore it is not a bad block */
|
||||
ext_tags->chunk_used =
|
||||
memcmp(&spare_ff, &spare, sizeof(spare_ff)) ? 1 : 0;
|
||||
|
||||
if (ext_tags->chunk_used) {
|
||||
yaffs_get_tags_from_spare(dev, &spare, &tags);
|
||||
ext_tags->obj_id = tags.obj_id;
|
||||
ext_tags->chunk_id = tags.chunk_id;
|
||||
ext_tags->n_bytes = tags.n_bytes_lsb;
|
||||
|
||||
if (dev->data_bytes_per_chunk >= 1024)
|
||||
ext_tags->n_bytes |=
|
||||
(((unsigned)tags.n_bytes_msb) << 10);
|
||||
|
||||
ext_tags->serial_number = tags.serial_number;
|
||||
}
|
||||
|
||||
return YAFFS_OK;
|
||||
}
|
||||
|
||||
static int yaffs_tags_compat_mark_bad(struct yaffs_dev *dev, int flash_block)
|
||||
{
|
||||
struct yaffs_spare spare;
|
||||
|
||||
memset(&spare, 0xff, sizeof(struct yaffs_spare));
|
||||
|
||||
spare.block_status = 'Y';
|
||||
|
||||
yaffs_wr_nand(dev, flash_block * dev->param.chunks_per_block, NULL,
|
||||
&spare);
|
||||
yaffs_wr_nand(dev, flash_block * dev->param.chunks_per_block + 1,
|
||||
NULL, &spare);
|
||||
|
||||
return YAFFS_OK;
|
||||
}
|
||||
|
||||
static int yaffs_tags_compat_query_block(struct yaffs_dev *dev,
|
||||
int block_no,
|
||||
enum yaffs_block_state *state,
|
||||
u32 *seq_number)
|
||||
{
|
||||
struct yaffs_spare spare0, spare1;
|
||||
static struct yaffs_spare spare_ff;
|
||||
static int init;
|
||||
enum yaffs_ecc_result dummy;
|
||||
|
||||
if (!init) {
|
||||
memset(&spare_ff, 0xff, sizeof(spare_ff));
|
||||
init = 1;
|
||||
}
|
||||
|
||||
*seq_number = 0;
|
||||
|
||||
/* Look for bad block markers in the first two chunks */
|
||||
yaffs_rd_chunk_nand(dev, block_no * dev->param.chunks_per_block,
|
||||
NULL, &spare0, &dummy, 0);
|
||||
yaffs_rd_chunk_nand(dev, block_no * dev->param.chunks_per_block + 1,
|
||||
NULL, &spare1, &dummy, 0);
|
||||
|
||||
if (hweight8(spare0.block_status & spare1.block_status) < 7)
|
||||
*state = YAFFS_BLOCK_STATE_DEAD;
|
||||
else if (memcmp(&spare_ff, &spare0, sizeof(spare_ff)) == 0)
|
||||
*state = YAFFS_BLOCK_STATE_EMPTY;
|
||||
else
|
||||
*state = YAFFS_BLOCK_STATE_NEEDS_SCAN;
|
||||
|
||||
return YAFFS_OK;
|
||||
}
|
||||
|
||||
void yaffs_tags_compat_install(struct yaffs_dev *dev)
|
||||
{
|
||||
if(dev->param.is_yaffs2)
|
||||
return;
|
||||
if(!dev->tagger.write_chunk_tags_fn)
|
||||
dev->tagger.write_chunk_tags_fn = yaffs_tags_compat_wr;
|
||||
if(!dev->tagger.read_chunk_tags_fn)
|
||||
dev->tagger.read_chunk_tags_fn = yaffs_tags_compat_rd;
|
||||
if(!dev->tagger.query_block_fn)
|
||||
dev->tagger.query_block_fn = yaffs_tags_compat_query_block;
|
||||
if(!dev->tagger.mark_bad_fn)
|
||||
dev->tagger.mark_bad_fn = yaffs_tags_compat_mark_bad;
|
||||
}
|
199
flight/pios/common/libraries/yaffs2/yaffs_tagsmarshall.c
Normal file
199
flight/pios/common/libraries/yaffs2/yaffs_tagsmarshall.c
Normal file
@ -0,0 +1,199 @@
|
||||
/*
|
||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include "yaffs_guts.h"
|
||||
#include "yaffs_trace.h"
|
||||
#include "yaffs_packedtags2.h"
|
||||
|
||||
static int yaffs_tags_marshall_write(struct yaffs_dev *dev,
|
||||
int nand_chunk, const u8 *data,
|
||||
const struct yaffs_ext_tags *tags)
|
||||
{
|
||||
struct yaffs_packed_tags2 pt;
|
||||
int retval;
|
||||
|
||||
int packed_tags_size =
|
||||
dev->param.no_tags_ecc ? sizeof(pt.t) : sizeof(pt);
|
||||
void *packed_tags_ptr =
|
||||
dev->param.no_tags_ecc ? (void *)&pt.t : (void *)&pt;
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_MTD,
|
||||
"yaffs_tags_marshall_write chunk %d data %p tags %p",
|
||||
nand_chunk, data, tags);
|
||||
|
||||
/* For yaffs2 writing there must be both data and tags.
|
||||
* If we're using inband tags, then the tags are stuffed into
|
||||
* the end of the data buffer.
|
||||
*/
|
||||
if (!data || !tags)
|
||||
BUG();
|
||||
else if (dev->param.inband_tags) {
|
||||
struct yaffs_packed_tags2_tags_only *pt2tp;
|
||||
pt2tp =
|
||||
(struct yaffs_packed_tags2_tags_only *)(data +
|
||||
dev->
|
||||
data_bytes_per_chunk);
|
||||
yaffs_pack_tags2_tags_only(pt2tp, tags);
|
||||
} else {
|
||||
yaffs_pack_tags2(&pt, tags, !dev->param.no_tags_ecc);
|
||||
}
|
||||
|
||||
retval = dev->drv.drv_write_chunk_fn(dev, nand_chunk,
|
||||
data, dev->param.total_bytes_per_chunk,
|
||||
(dev->param.inband_tags) ? NULL : packed_tags_ptr,
|
||||
(dev->param.inband_tags) ? 0 : packed_tags_size);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int yaffs_tags_marshall_read(struct yaffs_dev *dev,
|
||||
int nand_chunk, u8 *data,
|
||||
struct yaffs_ext_tags *tags)
|
||||
{
|
||||
//int retval = 0;
|
||||
int local_data = 0;
|
||||
u8 spare_buffer[100];
|
||||
enum yaffs_ecc_result ecc_result;
|
||||
|
||||
struct yaffs_packed_tags2 pt;
|
||||
|
||||
int packed_tags_size =
|
||||
dev->param.no_tags_ecc ? sizeof(pt.t) : sizeof(pt);
|
||||
void *packed_tags_ptr =
|
||||
dev->param.no_tags_ecc ? (void *)&pt.t : (void *)&pt;
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_MTD,
|
||||
"yaffs_tags_marshall_read chunk %d data %p tags %p",
|
||||
nand_chunk, data, tags);
|
||||
|
||||
if (dev->param.inband_tags) {
|
||||
if (!data) {
|
||||
local_data = 1;
|
||||
data = yaffs_get_temp_buffer(dev);
|
||||
}
|
||||
}
|
||||
|
||||
if (dev->param.inband_tags || (data && !tags))
|
||||
dev->drv.drv_read_chunk_fn(dev, nand_chunk,
|
||||
data, dev->param.total_bytes_per_chunk,
|
||||
NULL, 0,
|
||||
&ecc_result); //TODO check retval
|
||||
else if (tags)
|
||||
dev->drv.drv_read_chunk_fn(dev, nand_chunk,
|
||||
data, dev->param.total_bytes_per_chunk,
|
||||
spare_buffer, packed_tags_size,
|
||||
&ecc_result); //TODO check retval
|
||||
else
|
||||
BUG();
|
||||
|
||||
|
||||
if (dev->param.inband_tags) {
|
||||
if (tags) {
|
||||
struct yaffs_packed_tags2_tags_only *pt2tp;
|
||||
pt2tp =
|
||||
(struct yaffs_packed_tags2_tags_only *)
|
||||
&data[dev->data_bytes_per_chunk];
|
||||
yaffs_unpack_tags2_tags_only(tags, pt2tp);
|
||||
}
|
||||
} else if (tags) {
|
||||
memcpy(packed_tags_ptr, spare_buffer, packed_tags_size);
|
||||
yaffs_unpack_tags2(tags, &pt, !dev->param.no_tags_ecc);
|
||||
}
|
||||
|
||||
if (local_data)
|
||||
yaffs_release_temp_buffer(dev, data);
|
||||
|
||||
if (tags && ecc_result == YAFFS_ECC_RESULT_UNFIXED) {
|
||||
tags->ecc_result = YAFFS_ECC_RESULT_UNFIXED;
|
||||
dev->n_ecc_unfixed++;
|
||||
}
|
||||
|
||||
if (tags && ecc_result == YAFFS_ECC_RESULT_FIXED) {
|
||||
if (tags->ecc_result <= YAFFS_ECC_RESULT_NO_ERROR)
|
||||
tags->ecc_result = YAFFS_ECC_RESULT_FIXED;
|
||||
dev->n_ecc_fixed++;
|
||||
}
|
||||
|
||||
if (ecc_result < YAFFS_ECC_RESULT_UNFIXED)
|
||||
return YAFFS_OK;
|
||||
else
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
|
||||
static int yaffs_tags_marshall_query_block(struct yaffs_dev *dev, int block_no,
|
||||
enum yaffs_block_state *state,
|
||||
u32 *seq_number)
|
||||
{
|
||||
int retval;
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_MTD, "yaffs_tags_marshall_query_block %d",
|
||||
block_no);
|
||||
|
||||
retval = dev->drv.drv_check_bad_fn(dev, block_no);
|
||||
|
||||
if (retval== YAFFS_FAIL) {
|
||||
yaffs_trace(YAFFS_TRACE_MTD, "block is bad");
|
||||
|
||||
*state = YAFFS_BLOCK_STATE_DEAD;
|
||||
*seq_number = 0;
|
||||
} else {
|
||||
struct yaffs_ext_tags t;
|
||||
|
||||
yaffs_tags_marshall_read(dev,
|
||||
block_no * dev->param.chunks_per_block,
|
||||
NULL, &t);
|
||||
|
||||
if (t.chunk_used) {
|
||||
*seq_number = t.seq_number;
|
||||
*state = YAFFS_BLOCK_STATE_NEEDS_SCAN;
|
||||
} else {
|
||||
*seq_number = 0;
|
||||
*state = YAFFS_BLOCK_STATE_EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_MTD,
|
||||
"block query returns seq %d state %d",
|
||||
*seq_number, *state);
|
||||
|
||||
if (retval == 0)
|
||||
return YAFFS_OK;
|
||||
else
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
|
||||
static int yaffs_tags_marshall_mark_bad(struct yaffs_dev *dev, int block_no)
|
||||
{
|
||||
return dev->drv.drv_mark_bad_fn(dev, block_no);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void yaffs_tags_marshall_install(struct yaffs_dev *dev)
|
||||
{
|
||||
if (!dev->param.is_yaffs2)
|
||||
return;
|
||||
|
||||
if (!dev->tagger.write_chunk_tags_fn)
|
||||
dev->tagger.write_chunk_tags_fn = yaffs_tags_marshall_write;
|
||||
|
||||
if (!dev->tagger.read_chunk_tags_fn)
|
||||
dev->tagger.read_chunk_tags_fn = yaffs_tags_marshall_read;
|
||||
|
||||
if (!dev->tagger.query_block_fn)
|
||||
dev->tagger.query_block_fn = yaffs_tags_marshall_query_block;
|
||||
|
||||
if (!dev->tagger.mark_bad_fn)
|
||||
dev->tagger.mark_bad_fn = yaffs_tags_marshall_mark_bad;
|
||||
|
||||
}
|
529
flight/pios/common/libraries/yaffs2/yaffs_verify.c
Normal file
529
flight/pios/common/libraries/yaffs2/yaffs_verify.c
Normal file
@ -0,0 +1,529 @@
|
||||
/*
|
||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include "yaffs_verify.h"
|
||||
#include "yaffs_trace.h"
|
||||
#include "yaffs_bitmap.h"
|
||||
#include "yaffs_getblockinfo.h"
|
||||
#include "yaffs_nand.h"
|
||||
|
||||
int yaffs_skip_verification(struct yaffs_dev *dev)
|
||||
{
|
||||
(void) dev;
|
||||
return !(yaffs_trace_mask &
|
||||
(YAFFS_TRACE_VERIFY | YAFFS_TRACE_VERIFY_FULL));
|
||||
}
|
||||
|
||||
static int yaffs_skip_full_verification(struct yaffs_dev *dev)
|
||||
{
|
||||
(void) dev;
|
||||
return !(yaffs_trace_mask & (YAFFS_TRACE_VERIFY_FULL));
|
||||
}
|
||||
|
||||
static int yaffs_skip_nand_verification(struct yaffs_dev *dev)
|
||||
{
|
||||
(void) dev;
|
||||
return !(yaffs_trace_mask & (YAFFS_TRACE_VERIFY_NAND));
|
||||
}
|
||||
|
||||
static const char * const block_state_name[] = {
|
||||
"Unknown",
|
||||
"Needs scan",
|
||||
"Scanning",
|
||||
"Empty",
|
||||
"Allocating",
|
||||
"Full",
|
||||
"Dirty",
|
||||
"Checkpoint",
|
||||
"Collecting",
|
||||
"Dead"
|
||||
};
|
||||
|
||||
void yaffs_verify_blk(struct yaffs_dev *dev, struct yaffs_block_info *bi, int n)
|
||||
{
|
||||
int actually_used;
|
||||
int in_use;
|
||||
|
||||
if (yaffs_skip_verification(dev))
|
||||
return;
|
||||
|
||||
/* Report illegal runtime states */
|
||||
if (bi->block_state >= YAFFS_NUMBER_OF_BLOCK_STATES)
|
||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
||||
"Block %d has undefined state %d",
|
||||
n, bi->block_state);
|
||||
|
||||
switch (bi->block_state) {
|
||||
case YAFFS_BLOCK_STATE_UNKNOWN:
|
||||
case YAFFS_BLOCK_STATE_SCANNING:
|
||||
case YAFFS_BLOCK_STATE_NEEDS_SCAN:
|
||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
||||
"Block %d has bad run-state %s",
|
||||
n, block_state_name[bi->block_state]);
|
||||
}
|
||||
|
||||
/* Check pages in use and soft deletions are legal */
|
||||
|
||||
actually_used = bi->pages_in_use - bi->soft_del_pages;
|
||||
|
||||
if (bi->pages_in_use < 0 ||
|
||||
bi->pages_in_use > dev->param.chunks_per_block ||
|
||||
bi->soft_del_pages < 0 ||
|
||||
bi->soft_del_pages > dev->param.chunks_per_block ||
|
||||
actually_used < 0 || actually_used > dev->param.chunks_per_block)
|
||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
||||
"Block %d has illegal values pages_in_used %d soft_del_pages %d",
|
||||
n, bi->pages_in_use, bi->soft_del_pages);
|
||||
|
||||
/* Check chunk bitmap legal */
|
||||
in_use = yaffs_count_chunk_bits(dev, n);
|
||||
if (in_use != bi->pages_in_use)
|
||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
||||
"Block %d has inconsistent values pages_in_use %d counted chunk bits %d",
|
||||
n, bi->pages_in_use, in_use);
|
||||
}
|
||||
|
||||
void yaffs_verify_collected_blk(struct yaffs_dev *dev,
|
||||
struct yaffs_block_info *bi, int n)
|
||||
{
|
||||
yaffs_verify_blk(dev, bi, n);
|
||||
|
||||
/* After collection the block should be in the erased state */
|
||||
|
||||
if (bi->block_state != YAFFS_BLOCK_STATE_COLLECTING &&
|
||||
bi->block_state != YAFFS_BLOCK_STATE_EMPTY) {
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"Block %d is in state %d after gc, should be erased",
|
||||
n, bi->block_state);
|
||||
}
|
||||
}
|
||||
|
||||
void yaffs_verify_blocks(struct yaffs_dev *dev)
|
||||
{
|
||||
int i;
|
||||
int state_count[YAFFS_NUMBER_OF_BLOCK_STATES];
|
||||
int illegal_states = 0;
|
||||
|
||||
if (yaffs_skip_verification(dev))
|
||||
return;
|
||||
|
||||
memset(state_count, 0, sizeof(state_count));
|
||||
|
||||
for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) {
|
||||
struct yaffs_block_info *bi = yaffs_get_block_info(dev, i);
|
||||
yaffs_verify_blk(dev, bi, i);
|
||||
|
||||
if (bi->block_state < YAFFS_NUMBER_OF_BLOCK_STATES)
|
||||
state_count[bi->block_state]++;
|
||||
else
|
||||
illegal_states++;
|
||||
}
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_VERIFY, "Block summary");
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
||||
"%d blocks have illegal states",
|
||||
illegal_states);
|
||||
if (state_count[YAFFS_BLOCK_STATE_ALLOCATING] > 1)
|
||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
||||
"Too many allocating blocks");
|
||||
|
||||
for (i = 0; i < YAFFS_NUMBER_OF_BLOCK_STATES; i++)
|
||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
||||
"%s %d blocks",
|
||||
block_state_name[i], state_count[i]);
|
||||
|
||||
if (dev->blocks_in_checkpt != state_count[YAFFS_BLOCK_STATE_CHECKPOINT])
|
||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
||||
"Checkpoint block count wrong dev %d count %d",
|
||||
dev->blocks_in_checkpt,
|
||||
state_count[YAFFS_BLOCK_STATE_CHECKPOINT]);
|
||||
|
||||
if (dev->n_erased_blocks != state_count[YAFFS_BLOCK_STATE_EMPTY])
|
||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
||||
"Erased block count wrong dev %d count %d",
|
||||
dev->n_erased_blocks,
|
||||
state_count[YAFFS_BLOCK_STATE_EMPTY]);
|
||||
|
||||
if (state_count[YAFFS_BLOCK_STATE_COLLECTING] > 1)
|
||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
||||
"Too many collecting blocks %d (max is 1)",
|
||||
state_count[YAFFS_BLOCK_STATE_COLLECTING]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Verify the object header. oh must be valid, but obj and tags may be NULL in
|
||||
* which case those tests will not be performed.
|
||||
*/
|
||||
void yaffs_verify_oh(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh,
|
||||
struct yaffs_ext_tags *tags, int parent_check)
|
||||
{
|
||||
if (obj && yaffs_skip_verification(obj->my_dev))
|
||||
return;
|
||||
|
||||
if (!(tags && obj && oh)) {
|
||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
||||
"Verifying object header tags %p obj %p oh %p",
|
||||
tags, obj, oh);
|
||||
return;
|
||||
}
|
||||
|
||||
if (oh->type <= YAFFS_OBJECT_TYPE_UNKNOWN ||
|
||||
oh->type > YAFFS_OBJECT_TYPE_MAX)
|
||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
||||
"Obj %d header type is illegal value 0x%x",
|
||||
tags->obj_id, oh->type);
|
||||
|
||||
if (tags->obj_id != obj->obj_id)
|
||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
||||
"Obj %d header mismatch obj_id %d",
|
||||
tags->obj_id, obj->obj_id);
|
||||
|
||||
/*
|
||||
* Check that the object's parent ids match if parent_check requested.
|
||||
*
|
||||
* Tests do not apply to the root object.
|
||||
*/
|
||||
|
||||
if (parent_check && tags->obj_id > 1 && !obj->parent)
|
||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
||||
"Obj %d header mismatch parent_id %d obj->parent is NULL",
|
||||
tags->obj_id, oh->parent_obj_id);
|
||||
|
||||
if (parent_check && obj->parent &&
|
||||
oh->parent_obj_id != (s32)obj->parent->obj_id &&
|
||||
(oh->parent_obj_id != YAFFS_OBJECTID_UNLINKED ||
|
||||
obj->parent->obj_id != YAFFS_OBJECTID_DELETED))
|
||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
||||
"Obj %d header mismatch parent_id %d parent_obj_id %d",
|
||||
tags->obj_id, oh->parent_obj_id,
|
||||
obj->parent->obj_id);
|
||||
|
||||
if (tags->obj_id > 1 && oh->name[0] == 0) /* Null name */
|
||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
||||
"Obj %d header name is NULL",
|
||||
obj->obj_id);
|
||||
|
||||
if (tags->obj_id > 1 && ((u8) (oh->name[0])) == 0xff) /* Junk name */
|
||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
||||
"Obj %d header name is 0xff",
|
||||
obj->obj_id);
|
||||
}
|
||||
|
||||
void yaffs_verify_file(struct yaffs_obj *obj)
|
||||
{
|
||||
u32 x;
|
||||
int required_depth;
|
||||
//int actual_depth;
|
||||
int last_chunk;
|
||||
u32 offset_in_chunk;
|
||||
u32 the_chunk;
|
||||
|
||||
u32 i;
|
||||
struct yaffs_dev *dev;
|
||||
struct yaffs_ext_tags tags;
|
||||
struct yaffs_tnode *tn;
|
||||
u32 obj_id;
|
||||
|
||||
if (!obj)
|
||||
return;
|
||||
|
||||
if (yaffs_skip_verification(obj->my_dev))
|
||||
return;
|
||||
|
||||
dev = obj->my_dev;
|
||||
obj_id = obj->obj_id;
|
||||
|
||||
|
||||
/* Check file size is consistent with tnode depth */
|
||||
yaffs_addr_to_chunk(dev, obj->variant.file_variant.file_size,
|
||||
&last_chunk, &offset_in_chunk);
|
||||
last_chunk++;
|
||||
x = last_chunk >> YAFFS_TNODES_LEVEL0_BITS;
|
||||
required_depth = 0;
|
||||
while (x > 0) {
|
||||
x >>= YAFFS_TNODES_INTERNAL_BITS;
|
||||
required_depth++;
|
||||
}
|
||||
|
||||
// actual_depth = obj->variant.file_variant.top_level; // unused
|
||||
|
||||
/* Check that the chunks in the tnode tree are all correct.
|
||||
* We do this by scanning through the tnode tree and
|
||||
* checking the tags for every chunk match.
|
||||
*/
|
||||
|
||||
if (yaffs_skip_nand_verification(dev))
|
||||
return;
|
||||
|
||||
for (i = 1; i <= (u32)last_chunk; i++) {
|
||||
tn = yaffs_find_tnode_0(dev, &obj->variant.file_variant, i);
|
||||
|
||||
if (!tn)
|
||||
continue;
|
||||
|
||||
the_chunk = yaffs_get_group_base(dev, tn, i);
|
||||
if (the_chunk > 0) {
|
||||
yaffs_rd_chunk_tags_nand(dev, the_chunk, NULL,
|
||||
&tags);
|
||||
if (tags.obj_id != obj_id || tags.chunk_id != i)
|
||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
||||
"Object %d chunk_id %d NAND mismatch chunk %d tags (%d:%d)",
|
||||
obj_id, i, the_chunk,
|
||||
tags.obj_id, tags.chunk_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void yaffs_verify_link(struct yaffs_obj *obj)
|
||||
{
|
||||
if (obj && yaffs_skip_verification(obj->my_dev))
|
||||
return;
|
||||
|
||||
/* Verify sane equivalent object */
|
||||
}
|
||||
|
||||
void yaffs_verify_symlink(struct yaffs_obj *obj)
|
||||
{
|
||||
if (obj && yaffs_skip_verification(obj->my_dev))
|
||||
return;
|
||||
|
||||
/* Verify symlink string */
|
||||
}
|
||||
|
||||
void yaffs_verify_special(struct yaffs_obj *obj)
|
||||
{
|
||||
if (obj && yaffs_skip_verification(obj->my_dev))
|
||||
return;
|
||||
}
|
||||
|
||||
void yaffs_verify_obj(struct yaffs_obj *obj)
|
||||
{
|
||||
struct yaffs_dev *dev;
|
||||
u32 chunk_min;
|
||||
u32 chunk_max;
|
||||
u32 chunk_id_ok;
|
||||
u32 chunk_in_range;
|
||||
u32 chunk_wrongly_deleted;
|
||||
u32 chunk_valid;
|
||||
|
||||
if (!obj)
|
||||
return;
|
||||
|
||||
if (obj->being_created)
|
||||
return;
|
||||
|
||||
dev = obj->my_dev;
|
||||
|
||||
if (yaffs_skip_verification(dev))
|
||||
return;
|
||||
|
||||
/* Check sane object header chunk */
|
||||
|
||||
chunk_min = dev->internal_start_block * dev->param.chunks_per_block;
|
||||
chunk_max =
|
||||
(dev->internal_end_block + 1) * dev->param.chunks_per_block - 1;
|
||||
|
||||
chunk_in_range = (((unsigned)(obj->hdr_chunk)) >= chunk_min &&
|
||||
((unsigned)(obj->hdr_chunk)) <= chunk_max);
|
||||
chunk_id_ok = chunk_in_range || (obj->hdr_chunk == 0);
|
||||
chunk_valid = chunk_in_range &&
|
||||
yaffs_check_chunk_bit(dev,
|
||||
obj->hdr_chunk / dev->param.chunks_per_block,
|
||||
obj->hdr_chunk % dev->param.chunks_per_block);
|
||||
chunk_wrongly_deleted = chunk_in_range && !chunk_valid;
|
||||
|
||||
if (!obj->fake && (!chunk_id_ok || chunk_wrongly_deleted))
|
||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
||||
"Obj %d has chunk_id %d %s %s",
|
||||
obj->obj_id, obj->hdr_chunk,
|
||||
chunk_id_ok ? "" : ",out of range",
|
||||
chunk_wrongly_deleted ? ",marked as deleted" : "");
|
||||
|
||||
if (chunk_valid && !yaffs_skip_nand_verification(dev)) {
|
||||
struct yaffs_ext_tags tags;
|
||||
struct yaffs_obj_hdr *oh;
|
||||
u8 *buffer = yaffs_get_temp_buffer(dev);
|
||||
|
||||
oh = (struct yaffs_obj_hdr *)buffer;
|
||||
|
||||
yaffs_rd_chunk_tags_nand(dev, obj->hdr_chunk, buffer, &tags);
|
||||
|
||||
yaffs_verify_oh(obj, oh, &tags, 1);
|
||||
|
||||
yaffs_release_temp_buffer(dev, buffer);
|
||||
}
|
||||
|
||||
/* Verify it has a parent */
|
||||
if (obj && !obj->fake && (!obj->parent || obj->parent->my_dev != dev)) {
|
||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
||||
"Obj %d has parent pointer %p which does not look like an object",
|
||||
obj->obj_id, obj->parent);
|
||||
}
|
||||
|
||||
/* Verify parent is a directory */
|
||||
if (obj->parent &&
|
||||
obj->parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) {
|
||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
||||
"Obj %d's parent is not a directory (type %d)",
|
||||
obj->obj_id, obj->parent->variant_type);
|
||||
}
|
||||
|
||||
switch (obj->variant_type) {
|
||||
case YAFFS_OBJECT_TYPE_FILE:
|
||||
yaffs_verify_file(obj);
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_SYMLINK:
|
||||
yaffs_verify_symlink(obj);
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_DIRECTORY:
|
||||
yaffs_verify_dir(obj);
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_HARDLINK:
|
||||
yaffs_verify_link(obj);
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_SPECIAL:
|
||||
yaffs_verify_special(obj);
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_UNKNOWN:
|
||||
default:
|
||||
yaffs_trace(YAFFS_TRACE_VERIFY,
|
||||
"Obj %d has illegaltype %d",
|
||||
obj->obj_id, obj->variant_type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void yaffs_verify_objects(struct yaffs_dev *dev)
|
||||
{
|
||||
struct yaffs_obj *obj;
|
||||
int i;
|
||||
struct list_head *lh;
|
||||
|
||||
if (yaffs_skip_verification(dev))
|
||||
return;
|
||||
|
||||
/* Iterate through the objects in each hash entry */
|
||||
|
||||
for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) {
|
||||
list_for_each(lh, &dev->obj_bucket[i].list) {
|
||||
obj = list_entry(lh, struct yaffs_obj, hash_link);
|
||||
yaffs_verify_obj(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void yaffs_verify_obj_in_dir(struct yaffs_obj *obj)
|
||||
{
|
||||
struct list_head *lh;
|
||||
struct yaffs_obj *list_obj;
|
||||
int count = 0;
|
||||
|
||||
if (!obj) {
|
||||
yaffs_trace(YAFFS_TRACE_ALWAYS, "No object to verify");
|
||||
BUG();
|
||||
return;
|
||||
}
|
||||
|
||||
if (yaffs_skip_verification(obj->my_dev))
|
||||
return;
|
||||
|
||||
if (!obj->parent) {
|
||||
yaffs_trace(YAFFS_TRACE_ALWAYS, "Object does not have parent");
|
||||
BUG();
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj->parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) {
|
||||
yaffs_trace(YAFFS_TRACE_ALWAYS, "Parent is not directory");
|
||||
BUG();
|
||||
}
|
||||
|
||||
/* Iterate through the objects in each hash entry */
|
||||
|
||||
list_for_each(lh, &obj->parent->variant.dir_variant.children) {
|
||||
list_obj = list_entry(lh, struct yaffs_obj, siblings);
|
||||
yaffs_verify_obj(list_obj);
|
||||
if (obj == list_obj)
|
||||
count++;
|
||||
}
|
||||
|
||||
if (count != 1) {
|
||||
yaffs_trace(YAFFS_TRACE_ALWAYS,
|
||||
"Object in directory %d times",
|
||||
count);
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
|
||||
void yaffs_verify_dir(struct yaffs_obj *directory)
|
||||
{
|
||||
struct list_head *lh;
|
||||
struct yaffs_obj *list_obj;
|
||||
|
||||
if (!directory) {
|
||||
BUG();
|
||||
return;
|
||||
}
|
||||
|
||||
if (yaffs_skip_full_verification(directory->my_dev))
|
||||
return;
|
||||
|
||||
if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) {
|
||||
yaffs_trace(YAFFS_TRACE_ALWAYS,
|
||||
"Directory has wrong type: %d",
|
||||
directory->variant_type);
|
||||
BUG();
|
||||
}
|
||||
|
||||
/* Iterate through the objects in each hash entry */
|
||||
|
||||
list_for_each(lh, &directory->variant.dir_variant.children) {
|
||||
list_obj = list_entry(lh, struct yaffs_obj, siblings);
|
||||
if (list_obj->parent != directory) {
|
||||
yaffs_trace(YAFFS_TRACE_ALWAYS,
|
||||
"Object in directory list has wrong parent %p",
|
||||
list_obj->parent);
|
||||
BUG();
|
||||
}
|
||||
yaffs_verify_obj_in_dir(list_obj);
|
||||
}
|
||||
}
|
||||
|
||||
static int yaffs_free_verification_failures;
|
||||
|
||||
void yaffs_verify_free_chunks(struct yaffs_dev *dev)
|
||||
{
|
||||
int counted;
|
||||
int difference;
|
||||
|
||||
if (yaffs_skip_verification(dev))
|
||||
return;
|
||||
|
||||
counted = yaffs_count_free_chunks(dev);
|
||||
|
||||
difference = dev->n_free_chunks - counted;
|
||||
|
||||
if (difference) {
|
||||
yaffs_trace(YAFFS_TRACE_ALWAYS,
|
||||
"Freechunks verification failure %d %d %d",
|
||||
dev->n_free_chunks, counted, difference);
|
||||
yaffs_free_verification_failures++;
|
||||
}
|
||||
}
|
||||
|
||||
int yaffs_verify_file_sane(struct yaffs_obj *in)
|
||||
{
|
||||
(void) in;
|
||||
return YAFFS_OK;
|
||||
}
|
428
flight/pios/common/libraries/yaffs2/yaffs_yaffs1.c
Normal file
428
flight/pios/common/libraries/yaffs2/yaffs_yaffs1.c
Normal file
@ -0,0 +1,428 @@
|
||||
/*
|
||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include "yaffs_yaffs1.h"
|
||||
#include "yportenv.h"
|
||||
#include "yaffs_trace.h"
|
||||
#include "yaffs_bitmap.h"
|
||||
#include "yaffs_getblockinfo.h"
|
||||
#include "yaffs_nand.h"
|
||||
#include "yaffs_attribs.h"
|
||||
|
||||
int yaffs1_scan(struct yaffs_dev *dev)
|
||||
{
|
||||
struct yaffs_ext_tags tags;
|
||||
int blk;
|
||||
//int result;
|
||||
int chunk;
|
||||
int c;
|
||||
int deleted;
|
||||
enum yaffs_block_state state;
|
||||
LIST_HEAD(hard_list);
|
||||
struct yaffs_block_info *bi;
|
||||
u32 seq_number;
|
||||
struct yaffs_obj_hdr *oh;
|
||||
struct yaffs_obj *in;
|
||||
struct yaffs_obj *parent;
|
||||
int alloc_failed = 0;
|
||||
struct yaffs_shadow_fixer *shadow_fixers = NULL;
|
||||
u8 *chunk_data;
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_SCAN,
|
||||
"yaffs1_scan starts intstartblk %d intendblk %d...",
|
||||
dev->internal_start_block, dev->internal_end_block);
|
||||
|
||||
chunk_data = yaffs_get_temp_buffer(dev);
|
||||
|
||||
dev->seq_number = YAFFS_LOWEST_SEQUENCE_NUMBER;
|
||||
|
||||
/* Scan all the blocks to determine their state */
|
||||
bi = dev->block_info;
|
||||
for (blk = dev->internal_start_block; blk <= dev->internal_end_block;
|
||||
blk++) {
|
||||
yaffs_clear_chunk_bits(dev, blk);
|
||||
bi->pages_in_use = 0;
|
||||
bi->soft_del_pages = 0;
|
||||
|
||||
yaffs_query_init_block_state(dev, blk, &state, &seq_number);
|
||||
|
||||
bi->block_state = state;
|
||||
bi->seq_number = seq_number;
|
||||
|
||||
if (bi->seq_number == YAFFS_SEQUENCE_BAD_BLOCK)
|
||||
bi->block_state = state = YAFFS_BLOCK_STATE_DEAD;
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_SCAN_DEBUG,
|
||||
"Block scanning block %d state %d seq %d",
|
||||
blk, state, seq_number);
|
||||
|
||||
if (state == YAFFS_BLOCK_STATE_DEAD) {
|
||||
yaffs_trace(YAFFS_TRACE_BAD_BLOCKS,
|
||||
"block %d is bad", blk);
|
||||
} else if (state == YAFFS_BLOCK_STATE_EMPTY) {
|
||||
yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "Block empty ");
|
||||
dev->n_erased_blocks++;
|
||||
dev->n_free_chunks += dev->param.chunks_per_block;
|
||||
}
|
||||
bi++;
|
||||
}
|
||||
|
||||
/* For each block.... */
|
||||
for (blk = dev->internal_start_block;
|
||||
!alloc_failed && blk <= dev->internal_end_block; blk++) {
|
||||
|
||||
cond_resched();
|
||||
|
||||
bi = yaffs_get_block_info(dev, blk);
|
||||
state = bi->block_state;
|
||||
|
||||
deleted = 0;
|
||||
|
||||
/* For each chunk in each block that needs scanning.... */
|
||||
for (c = 0;
|
||||
!alloc_failed && c < dev->param.chunks_per_block &&
|
||||
state == YAFFS_BLOCK_STATE_NEEDS_SCAN; c++) {
|
||||
/* Read the tags and decide what to do */
|
||||
chunk = blk * dev->param.chunks_per_block + c;
|
||||
|
||||
if (!yaffs_rd_chunk_tags_nand(dev, chunk, NULL, &tags))
|
||||
|
||||
{
|
||||
yaffs_trace(YAFFS_TRACE_ERROR, "yaffs1_scan: unhandled error from rd_chunk_tags_nand");
|
||||
}
|
||||
|
||||
/* Let's have a good look at this chunk... */
|
||||
|
||||
if (tags.ecc_result == YAFFS_ECC_RESULT_UNFIXED ||
|
||||
tags.is_deleted) {
|
||||
/* YAFFS1 only...
|
||||
* A deleted chunk
|
||||
*/
|
||||
deleted++;
|
||||
dev->n_free_chunks++;
|
||||
} else if (!tags.chunk_used) {
|
||||
/* An unassigned chunk in the block
|
||||
* This means that either the block is empty or
|
||||
* this is the one being allocated from
|
||||
*/
|
||||
|
||||
if (c == 0) {
|
||||
/* We're looking at the first chunk in
|
||||
*the block so the block is unused */
|
||||
state = YAFFS_BLOCK_STATE_EMPTY;
|
||||
dev->n_erased_blocks++;
|
||||
} else {
|
||||
/* this is the block being allocated */
|
||||
yaffs_trace(YAFFS_TRACE_SCAN,
|
||||
" Allocating from %d %d",
|
||||
blk, c);
|
||||
state = YAFFS_BLOCK_STATE_ALLOCATING;
|
||||
dev->alloc_block = blk;
|
||||
dev->alloc_page = c;
|
||||
dev->alloc_block_finder = blk;
|
||||
|
||||
}
|
||||
|
||||
dev->n_free_chunks +=
|
||||
(dev->param.chunks_per_block - c);
|
||||
} else if (tags.chunk_id > 0) {
|
||||
/* chunk_id > 0 so it is a data chunk... */
|
||||
unsigned int endpos;
|
||||
|
||||
yaffs_set_chunk_bit(dev, blk, c);
|
||||
bi->pages_in_use++;
|
||||
|
||||
in = yaffs_find_or_create_by_number(dev,
|
||||
tags.obj_id,
|
||||
YAFFS_OBJECT_TYPE_FILE);
|
||||
/* PutChunkIntoFile checks for a clash
|
||||
* (two data chunks with the same chunk_id).
|
||||
*/
|
||||
|
||||
if (!in)
|
||||
alloc_failed = 1;
|
||||
|
||||
if (in) {
|
||||
if (!yaffs_put_chunk_in_file
|
||||
(in, tags.chunk_id, chunk, 1))
|
||||
alloc_failed = 1;
|
||||
}
|
||||
|
||||
endpos =
|
||||
(tags.chunk_id - 1) *
|
||||
dev->data_bytes_per_chunk +
|
||||
tags.n_bytes;
|
||||
if (in &&
|
||||
in->variant_type ==
|
||||
YAFFS_OBJECT_TYPE_FILE &&
|
||||
in->variant.file_variant.scanned_size <
|
||||
(s32)endpos) {
|
||||
in->variant.file_variant.scanned_size =
|
||||
endpos;
|
||||
if (!dev->param.use_header_file_size) {
|
||||
in->variant.
|
||||
file_variant.file_size =
|
||||
in->variant.
|
||||
file_variant.scanned_size;
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
/* chunk_id == 0, so it is an ObjectHeader.
|
||||
* Make the object
|
||||
*/
|
||||
yaffs_set_chunk_bit(dev, blk, c);
|
||||
bi->pages_in_use++;
|
||||
|
||||
if (!yaffs_rd_chunk_tags_nand(dev, chunk, chunk_data, NULL))
|
||||
|
||||
{
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"yaffs1_scan: unhandled error from rd_chunk_tags_nand");
|
||||
}
|
||||
|
||||
oh = (struct yaffs_obj_hdr *)chunk_data;
|
||||
|
||||
in = yaffs_find_by_number(dev, tags.obj_id);
|
||||
if (in && in->variant_type != oh->type) {
|
||||
/* This should not happen, but somehow
|
||||
* Wev'e ended up with an obj_id that
|
||||
* has been reused but not yet deleted,
|
||||
* and worse still it has changed type.
|
||||
* Delete the old object.
|
||||
*/
|
||||
|
||||
yaffs_del_obj(in);
|
||||
in = NULL;
|
||||
}
|
||||
|
||||
in = yaffs_find_or_create_by_number(dev,
|
||||
tags.obj_id,
|
||||
oh->type);
|
||||
|
||||
if (!in)
|
||||
alloc_failed = 1;
|
||||
|
||||
if (in && oh->shadows_obj > 0) {
|
||||
|
||||
struct yaffs_shadow_fixer *fixer;
|
||||
fixer =
|
||||
kmalloc(sizeof
|
||||
(struct yaffs_shadow_fixer),
|
||||
GFP_NOFS);
|
||||
if (fixer) {
|
||||
fixer->next = shadow_fixers;
|
||||
shadow_fixers = fixer;
|
||||
fixer->obj_id = tags.obj_id;
|
||||
fixer->shadowed_id =
|
||||
oh->shadows_obj;
|
||||
yaffs_trace(YAFFS_TRACE_SCAN,
|
||||
" Shadow fixer: %d shadows %d",
|
||||
fixer->obj_id,
|
||||
fixer->shadowed_id);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (in && in->valid) {
|
||||
/* We have already filled this one.
|
||||
* We have a duplicate and need to
|
||||
* resolve it. */
|
||||
|
||||
unsigned existing_serial = in->serial;
|
||||
unsigned new_serial =
|
||||
tags.serial_number;
|
||||
|
||||
if (((existing_serial + 1) & 3) ==
|
||||
new_serial) {
|
||||
/* Use new one - destroy the
|
||||
* exisiting one */
|
||||
yaffs_chunk_del(dev,
|
||||
in->hdr_chunk,
|
||||
1, __LINE__);
|
||||
in->valid = 0;
|
||||
} else {
|
||||
/* Use existing - destroy
|
||||
* this one. */
|
||||
yaffs_chunk_del(dev, chunk, 1,
|
||||
__LINE__);
|
||||
}
|
||||
}
|
||||
|
||||
if (in && !in->valid &&
|
||||
(tags.obj_id == YAFFS_OBJECTID_ROOT ||
|
||||
tags.obj_id ==
|
||||
YAFFS_OBJECTID_LOSTNFOUND)) {
|
||||
/* We only load some info, don't fiddle
|
||||
* with directory structure */
|
||||
in->valid = 1;
|
||||
in->variant_type = oh->type;
|
||||
|
||||
in->yst_mode = oh->yst_mode;
|
||||
yaffs_load_attribs(in, oh);
|
||||
in->hdr_chunk = chunk;
|
||||
in->serial = tags.serial_number;
|
||||
|
||||
} else if (in && !in->valid) {
|
||||
/* we need to load this info */
|
||||
|
||||
in->valid = 1;
|
||||
in->variant_type = oh->type;
|
||||
|
||||
in->yst_mode = oh->yst_mode;
|
||||
yaffs_load_attribs(in, oh);
|
||||
in->hdr_chunk = chunk;
|
||||
in->serial = tags.serial_number;
|
||||
|
||||
yaffs_set_obj_name_from_oh(in, oh);
|
||||
in->dirty = 0;
|
||||
|
||||
/* directory stuff...
|
||||
* hook up to parent
|
||||
*/
|
||||
|
||||
parent =
|
||||
yaffs_find_or_create_by_number
|
||||
(dev, oh->parent_obj_id,
|
||||
YAFFS_OBJECT_TYPE_DIRECTORY);
|
||||
if (!parent)
|
||||
alloc_failed = 1;
|
||||
if (parent && parent->variant_type ==
|
||||
YAFFS_OBJECT_TYPE_UNKNOWN) {
|
||||
/* Set up as a directory */
|
||||
parent->variant_type =
|
||||
YAFFS_OBJECT_TYPE_DIRECTORY;
|
||||
INIT_LIST_HEAD(&parent->
|
||||
variant.dir_variant.
|
||||
children);
|
||||
} else if (!parent ||
|
||||
parent->variant_type !=
|
||||
YAFFS_OBJECT_TYPE_DIRECTORY) {
|
||||
/* Hoosterman, a problem....
|
||||
* We're trying to use a
|
||||
* non-directory as a directory
|
||||
*/
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found."
|
||||
);
|
||||
parent = dev->lost_n_found;
|
||||
}
|
||||
|
||||
yaffs_add_obj_to_dir(parent, in);
|
||||
|
||||
switch (in->variant_type) {
|
||||
case YAFFS_OBJECT_TYPE_UNKNOWN:
|
||||
/* Todo got a problem */
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_FILE:
|
||||
if (dev->param.
|
||||
use_header_file_size)
|
||||
in->variant.
|
||||
file_variant.file_size
|
||||
= yaffs_oh_to_size(oh);
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_HARDLINK:
|
||||
in->variant.
|
||||
hardlink_variant.equiv_id =
|
||||
oh->equiv_id;
|
||||
list_add(&in->hard_links,
|
||||
&hard_list);
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_DIRECTORY:
|
||||
/* Do nothing */
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_SPECIAL:
|
||||
/* Do nothing */
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_SYMLINK:
|
||||
in->variant.symlink_variant.
|
||||
alias =
|
||||
yaffs_clone_str(oh->alias);
|
||||
if (!in->variant.
|
||||
symlink_variant.alias)
|
||||
alloc_failed = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (state == YAFFS_BLOCK_STATE_NEEDS_SCAN) {
|
||||
/* If we got this far while scanning,
|
||||
* then the block is fully allocated. */
|
||||
state = YAFFS_BLOCK_STATE_FULL;
|
||||
}
|
||||
|
||||
if (state == YAFFS_BLOCK_STATE_ALLOCATING) {
|
||||
/* If the block was partially allocated then
|
||||
* treat it as fully allocated. */
|
||||
state = YAFFS_BLOCK_STATE_FULL;
|
||||
dev->alloc_block = -1;
|
||||
}
|
||||
|
||||
bi->block_state = state;
|
||||
|
||||
/* Now let's see if it was dirty */
|
||||
if (bi->pages_in_use == 0 &&
|
||||
!bi->has_shrink_hdr &&
|
||||
bi->block_state == YAFFS_BLOCK_STATE_FULL)
|
||||
yaffs_block_became_dirty(dev, blk);
|
||||
}
|
||||
|
||||
/* Ok, we've done all the scanning.
|
||||
* Fix up the hard link chains.
|
||||
* We should now have scanned all the objects, now it's time to add
|
||||
* these hardlinks.
|
||||
*/
|
||||
|
||||
yaffs_link_fixup(dev, &hard_list);
|
||||
|
||||
/*
|
||||
* Fix up any shadowed objects.
|
||||
* There should not be more than one of these.
|
||||
*/
|
||||
{
|
||||
struct yaffs_shadow_fixer *fixer;
|
||||
struct yaffs_obj *obj;
|
||||
|
||||
while (shadow_fixers) {
|
||||
fixer = shadow_fixers;
|
||||
shadow_fixers = fixer->next;
|
||||
/* Complete the rename transaction by deleting the
|
||||
* shadowed object then setting the object header
|
||||
to unshadowed.
|
||||
*/
|
||||
obj = yaffs_find_by_number(dev, fixer->shadowed_id);
|
||||
if (obj)
|
||||
yaffs_del_obj(obj);
|
||||
|
||||
obj = yaffs_find_by_number(dev, fixer->obj_id);
|
||||
|
||||
if (obj)
|
||||
yaffs_update_oh(obj, NULL, 1, 0, 0, NULL);
|
||||
|
||||
kfree(fixer);
|
||||
}
|
||||
}
|
||||
|
||||
yaffs_release_temp_buffer(dev, chunk_data);
|
||||
|
||||
if (alloc_failed)
|
||||
return YAFFS_FAIL;
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_SCAN, "yaffs1_scan ends");
|
||||
|
||||
return YAFFS_OK;
|
||||
}
|
1546
flight/pios/common/libraries/yaffs2/yaffs_yaffs2.c
Normal file
1546
flight/pios/common/libraries/yaffs2/yaffs_yaffs2.c
Normal file
@ -0,0 +1,1546 @@
|
||||
/*
|
||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include "yaffs_guts.h"
|
||||
#include "yaffs_trace.h"
|
||||
#include "yaffs_yaffs2.h"
|
||||
#include "yaffs_checkptrw.h"
|
||||
#include "yaffs_bitmap.h"
|
||||
#include "yaffs_nand.h"
|
||||
#include "yaffs_getblockinfo.h"
|
||||
#include "yaffs_verify.h"
|
||||
#include "yaffs_attribs.h"
|
||||
#include "yaffs_summary.h"
|
||||
|
||||
/*
|
||||
* Checkpoints are really no benefit on very small partitions.
|
||||
*
|
||||
* To save space on small partitions don't bother with checkpoints unless
|
||||
* the partition is at least this big.
|
||||
*/
|
||||
#define YAFFS_CHECKPOINT_MIN_BLOCKS 60
|
||||
#define YAFFS_SMALL_HOLE_THRESHOLD 4
|
||||
|
||||
/*
|
||||
* Oldest Dirty Sequence Number handling.
|
||||
*/
|
||||
|
||||
/* yaffs_calc_oldest_dirty_seq()
|
||||
* yaffs2_find_oldest_dirty_seq()
|
||||
* Calculate the oldest dirty sequence number if we don't know it.
|
||||
*/
|
||||
void yaffs_calc_oldest_dirty_seq(struct yaffs_dev *dev)
|
||||
{
|
||||
int i;
|
||||
unsigned seq;
|
||||
unsigned block_no = 0;
|
||||
struct yaffs_block_info *b;
|
||||
|
||||
if (!dev->param.is_yaffs2)
|
||||
return;
|
||||
|
||||
/* Find the oldest dirty sequence number. */
|
||||
seq = dev->seq_number + 1;
|
||||
b = dev->block_info;
|
||||
for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) {
|
||||
if (b->block_state == YAFFS_BLOCK_STATE_FULL &&
|
||||
(b->pages_in_use - b->soft_del_pages) <
|
||||
dev->param.chunks_per_block &&
|
||||
b->seq_number < seq) {
|
||||
seq = b->seq_number;
|
||||
block_no = i;
|
||||
}
|
||||
b++;
|
||||
}
|
||||
|
||||
if (block_no) {
|
||||
dev->oldest_dirty_seq = seq;
|
||||
dev->oldest_dirty_block = block_no;
|
||||
}
|
||||
}
|
||||
|
||||
void yaffs2_find_oldest_dirty_seq(struct yaffs_dev *dev)
|
||||
{
|
||||
if (!dev->param.is_yaffs2)
|
||||
return;
|
||||
|
||||
if (!dev->oldest_dirty_seq)
|
||||
yaffs_calc_oldest_dirty_seq(dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* yaffs_clear_oldest_dirty_seq()
|
||||
* Called when a block is erased or marked bad. (ie. when its seq_number
|
||||
* becomes invalid). If the value matches the oldest then we clear
|
||||
* dev->oldest_dirty_seq to force its recomputation.
|
||||
*/
|
||||
void yaffs2_clear_oldest_dirty_seq(struct yaffs_dev *dev,
|
||||
struct yaffs_block_info *bi)
|
||||
{
|
||||
|
||||
if (!dev->param.is_yaffs2)
|
||||
return;
|
||||
|
||||
if (!bi || bi->seq_number == dev->oldest_dirty_seq) {
|
||||
dev->oldest_dirty_seq = 0;
|
||||
dev->oldest_dirty_block = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* yaffs2_update_oldest_dirty_seq()
|
||||
* Update the oldest dirty sequence number whenever we dirty a block.
|
||||
* Only do this if the oldest_dirty_seq is actually being tracked.
|
||||
*/
|
||||
void yaffs2_update_oldest_dirty_seq(struct yaffs_dev *dev, unsigned block_no,
|
||||
struct yaffs_block_info *bi)
|
||||
{
|
||||
if (!dev->param.is_yaffs2)
|
||||
return;
|
||||
|
||||
if (dev->oldest_dirty_seq) {
|
||||
if (dev->oldest_dirty_seq > bi->seq_number) {
|
||||
dev->oldest_dirty_seq = bi->seq_number;
|
||||
dev->oldest_dirty_block = block_no;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int yaffs_block_ok_for_gc(struct yaffs_dev *dev, struct yaffs_block_info *bi)
|
||||
{
|
||||
|
||||
if (!dev->param.is_yaffs2)
|
||||
return 1; /* disqualification only applies to yaffs2. */
|
||||
|
||||
if (!bi->has_shrink_hdr)
|
||||
return 1; /* can gc */
|
||||
|
||||
yaffs2_find_oldest_dirty_seq(dev);
|
||||
|
||||
/* Can't do gc of this block if there are any blocks older than this
|
||||
* one that have discarded pages.
|
||||
*/
|
||||
return (bi->seq_number <= dev->oldest_dirty_seq);
|
||||
}
|
||||
|
||||
/*
|
||||
* yaffs2_find_refresh_block()
|
||||
* periodically finds the oldest full block by sequence number for refreshing.
|
||||
* Only for yaffs2.
|
||||
*/
|
||||
u32 yaffs2_find_refresh_block(struct yaffs_dev *dev)
|
||||
{
|
||||
u32 b;
|
||||
u32 oldest = 0;
|
||||
u32 oldest_seq = 0;
|
||||
struct yaffs_block_info *bi;
|
||||
|
||||
if (!dev->param.is_yaffs2)
|
||||
return oldest;
|
||||
|
||||
/*
|
||||
* If refresh period < 10 then refreshing is disabled.
|
||||
*/
|
||||
if (dev->param.refresh_period < 10)
|
||||
return oldest;
|
||||
|
||||
/*
|
||||
* Fix broken values.
|
||||
*/
|
||||
if (dev->refresh_skip > dev->param.refresh_period)
|
||||
dev->refresh_skip = dev->param.refresh_period;
|
||||
|
||||
if (dev->refresh_skip > 0)
|
||||
return oldest;
|
||||
|
||||
/*
|
||||
* Refresh skip is now zero.
|
||||
* We'll do a refresh this time around....
|
||||
* Update the refresh skip and find the oldest block.
|
||||
*/
|
||||
dev->refresh_skip = dev->param.refresh_period;
|
||||
dev->refresh_count++;
|
||||
bi = dev->block_info;
|
||||
for (b = dev->internal_start_block; b <= (u32)dev->internal_end_block; b++) {
|
||||
|
||||
if (bi->block_state == YAFFS_BLOCK_STATE_FULL) {
|
||||
|
||||
if (oldest < 1 || bi->seq_number < oldest_seq) {
|
||||
oldest = b;
|
||||
oldest_seq = bi->seq_number;
|
||||
}
|
||||
}
|
||||
bi++;
|
||||
}
|
||||
|
||||
if (oldest > 0) {
|
||||
yaffs_trace(YAFFS_TRACE_GC,
|
||||
"GC refresh count %d selected block %d with seq_number %d",
|
||||
dev->refresh_count, oldest, oldest_seq);
|
||||
}
|
||||
|
||||
return oldest;
|
||||
}
|
||||
|
||||
int yaffs2_checkpt_required(struct yaffs_dev *dev)
|
||||
{
|
||||
int nblocks;
|
||||
|
||||
if (!dev->param.is_yaffs2)
|
||||
return 0;
|
||||
|
||||
nblocks = dev->internal_end_block - dev->internal_start_block + 1;
|
||||
|
||||
return !dev->param.skip_checkpt_wr &&
|
||||
!dev->read_only && (nblocks >= YAFFS_CHECKPOINT_MIN_BLOCKS);
|
||||
}
|
||||
|
||||
int yaffs_calc_checkpt_blocks_required(struct yaffs_dev *dev)
|
||||
{
|
||||
int retval;
|
||||
int n_bytes = 0;
|
||||
int n_blocks;
|
||||
int dev_blocks;
|
||||
|
||||
if (!dev->param.is_yaffs2)
|
||||
return 0;
|
||||
|
||||
if (!dev->checkpoint_blocks_required && yaffs2_checkpt_required(dev)) {
|
||||
/* Not a valid value so recalculate */
|
||||
dev_blocks = dev->param.end_block - dev->param.start_block + 1;
|
||||
n_bytes += sizeof(struct yaffs_checkpt_validity);
|
||||
n_bytes += sizeof(struct yaffs_checkpt_dev);
|
||||
n_bytes += dev_blocks * sizeof(struct yaffs_block_info);
|
||||
n_bytes += dev_blocks * dev->chunk_bit_stride;
|
||||
n_bytes +=
|
||||
(sizeof(struct yaffs_checkpt_obj) + sizeof(u32)) *
|
||||
dev->n_obj;
|
||||
n_bytes += (dev->tnode_size + sizeof(u32)) * dev->n_tnodes;
|
||||
n_bytes += sizeof(struct yaffs_checkpt_validity);
|
||||
n_bytes += sizeof(u32); /* checksum */
|
||||
|
||||
/* Round up and add 2 blocks to allow for some bad blocks,
|
||||
* so add 3 */
|
||||
|
||||
n_blocks =
|
||||
(n_bytes /
|
||||
(dev->data_bytes_per_chunk *
|
||||
dev->param.chunks_per_block)) + 3;
|
||||
|
||||
dev->checkpoint_blocks_required = n_blocks;
|
||||
}
|
||||
|
||||
retval = dev->checkpoint_blocks_required - dev->blocks_in_checkpt;
|
||||
if (retval < 0)
|
||||
retval = 0;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*--------------------- Checkpointing --------------------*/
|
||||
|
||||
static int yaffs2_wr_checkpt_validity_marker(struct yaffs_dev *dev, int head)
|
||||
{
|
||||
struct yaffs_checkpt_validity cp;
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
|
||||
cp.struct_type = sizeof(cp);
|
||||
cp.magic = YAFFS_MAGIC;
|
||||
cp.version = YAFFS_CHECKPOINT_VERSION;
|
||||
cp.head = (head) ? 1 : 0;
|
||||
|
||||
return (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)) ? 1 : 0;
|
||||
}
|
||||
|
||||
static int yaffs2_rd_checkpt_validity_marker(struct yaffs_dev *dev, int head)
|
||||
{
|
||||
struct yaffs_checkpt_validity cp;
|
||||
int ok;
|
||||
|
||||
ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == (int)sizeof(cp));
|
||||
|
||||
if (ok)
|
||||
ok = (cp.struct_type == sizeof(cp)) &&
|
||||
(cp.magic == YAFFS_MAGIC) &&
|
||||
(cp.version == YAFFS_CHECKPOINT_VERSION) &&
|
||||
(cp.head == ((head) ? 1 : 0));
|
||||
return ok ? 1 : 0;
|
||||
}
|
||||
|
||||
static void yaffs2_dev_to_checkpt_dev(struct yaffs_checkpt_dev *cp,
|
||||
struct yaffs_dev *dev)
|
||||
{
|
||||
cp->n_erased_blocks = dev->n_erased_blocks;
|
||||
cp->alloc_block = dev->alloc_block;
|
||||
cp->alloc_page = dev->alloc_page;
|
||||
cp->n_free_chunks = dev->n_free_chunks;
|
||||
|
||||
cp->n_deleted_files = dev->n_deleted_files;
|
||||
cp->n_unlinked_files = dev->n_unlinked_files;
|
||||
cp->n_bg_deletions = dev->n_bg_deletions;
|
||||
cp->seq_number = dev->seq_number;
|
||||
|
||||
}
|
||||
|
||||
static void yaffs_checkpt_dev_to_dev(struct yaffs_dev *dev,
|
||||
struct yaffs_checkpt_dev *cp)
|
||||
{
|
||||
dev->n_erased_blocks = cp->n_erased_blocks;
|
||||
dev->alloc_block = cp->alloc_block;
|
||||
dev->alloc_page = cp->alloc_page;
|
||||
dev->n_free_chunks = cp->n_free_chunks;
|
||||
|
||||
dev->n_deleted_files = cp->n_deleted_files;
|
||||
dev->n_unlinked_files = cp->n_unlinked_files;
|
||||
dev->n_bg_deletions = cp->n_bg_deletions;
|
||||
dev->seq_number = cp->seq_number;
|
||||
}
|
||||
|
||||
static int yaffs2_wr_checkpt_dev(struct yaffs_dev *dev)
|
||||
{
|
||||
struct yaffs_checkpt_dev cp;
|
||||
u32 n_bytes;
|
||||
u32 n_blocks = dev->internal_end_block - dev->internal_start_block + 1;
|
||||
int ok;
|
||||
|
||||
/* Write device runtime values */
|
||||
yaffs2_dev_to_checkpt_dev(&cp, dev);
|
||||
cp.struct_type = sizeof(cp);
|
||||
|
||||
ok = (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp));
|
||||
if (!ok)
|
||||
return 0;
|
||||
|
||||
/* Write block info */
|
||||
n_bytes = n_blocks * sizeof(struct yaffs_block_info);
|
||||
ok = (yaffs2_checkpt_wr(dev, dev->block_info, n_bytes) == (int)n_bytes);
|
||||
if (!ok)
|
||||
return 0;
|
||||
|
||||
/* Write chunk bits */
|
||||
n_bytes = n_blocks * dev->chunk_bit_stride;
|
||||
ok = (yaffs2_checkpt_wr(dev, dev->chunk_bits, n_bytes) == (int)n_bytes);
|
||||
|
||||
return ok ? 1 : 0;
|
||||
}
|
||||
|
||||
static int yaffs2_rd_checkpt_dev(struct yaffs_dev *dev)
|
||||
{
|
||||
struct yaffs_checkpt_dev cp;
|
||||
u32 n_bytes;
|
||||
u32 n_blocks =
|
||||
(dev->internal_end_block - dev->internal_start_block + 1);
|
||||
int ok;
|
||||
|
||||
ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == (int)sizeof(cp));
|
||||
if (!ok)
|
||||
return 0;
|
||||
|
||||
if (cp.struct_type != sizeof(cp))
|
||||
return 0;
|
||||
|
||||
yaffs_checkpt_dev_to_dev(dev, &cp);
|
||||
|
||||
n_bytes = n_blocks * sizeof(struct yaffs_block_info);
|
||||
|
||||
ok = (yaffs2_checkpt_rd(dev, dev->block_info, n_bytes) == (int)n_bytes);
|
||||
|
||||
if (!ok)
|
||||
return 0;
|
||||
|
||||
n_bytes = n_blocks * dev->chunk_bit_stride;
|
||||
|
||||
ok = (yaffs2_checkpt_rd(dev, dev->chunk_bits, n_bytes) == (int)n_bytes);
|
||||
|
||||
return ok ? 1 : 0;
|
||||
}
|
||||
|
||||
static void yaffs2_obj_checkpt_obj(struct yaffs_checkpt_obj *cp,
|
||||
struct yaffs_obj *obj)
|
||||
{
|
||||
cp->obj_id = obj->obj_id;
|
||||
cp->parent_id = (obj->parent) ? obj->parent->obj_id : 0;
|
||||
cp->hdr_chunk = obj->hdr_chunk;
|
||||
cp->variant_type = obj->variant_type;
|
||||
cp->deleted = obj->deleted;
|
||||
cp->soft_del = obj->soft_del;
|
||||
cp->unlinked = obj->unlinked;
|
||||
cp->fake = obj->fake;
|
||||
cp->rename_allowed = obj->rename_allowed;
|
||||
cp->unlink_allowed = obj->unlink_allowed;
|
||||
cp->serial = obj->serial;
|
||||
cp->n_data_chunks = obj->n_data_chunks;
|
||||
|
||||
if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE)
|
||||
cp->size_or_equiv_obj = obj->variant.file_variant.file_size;
|
||||
else if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK)
|
||||
cp->size_or_equiv_obj = obj->variant.hardlink_variant.equiv_id;
|
||||
}
|
||||
|
||||
static int yaffs2_checkpt_obj_to_obj(struct yaffs_obj *obj,
|
||||
struct yaffs_checkpt_obj *cp)
|
||||
{
|
||||
struct yaffs_obj *parent;
|
||||
|
||||
if (obj->variant_type != cp->variant_type) {
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"Checkpoint read object %d type %d chunk %d does not match existing object type %d",
|
||||
cp->obj_id, cp->variant_type, cp->hdr_chunk,
|
||||
obj->variant_type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
obj->obj_id = cp->obj_id;
|
||||
|
||||
if (cp->parent_id)
|
||||
parent = yaffs_find_or_create_by_number(obj->my_dev,
|
||||
cp->parent_id,
|
||||
YAFFS_OBJECT_TYPE_DIRECTORY);
|
||||
else
|
||||
parent = NULL;
|
||||
|
||||
if (parent) {
|
||||
if (parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) {
|
||||
yaffs_trace(YAFFS_TRACE_ALWAYS,
|
||||
"Checkpoint read object %d parent %d type %d chunk %d Parent type, %d, not directory",
|
||||
cp->obj_id, cp->parent_id,
|
||||
cp->variant_type, cp->hdr_chunk,
|
||||
parent->variant_type);
|
||||
return 0;
|
||||
}
|
||||
yaffs_add_obj_to_dir(parent, obj);
|
||||
}
|
||||
|
||||
obj->hdr_chunk = cp->hdr_chunk;
|
||||
obj->variant_type = cp->variant_type;
|
||||
obj->deleted = cp->deleted;
|
||||
obj->soft_del = cp->soft_del;
|
||||
obj->unlinked = cp->unlinked;
|
||||
obj->fake = cp->fake;
|
||||
obj->rename_allowed = cp->rename_allowed;
|
||||
obj->unlink_allowed = cp->unlink_allowed;
|
||||
obj->serial = cp->serial;
|
||||
obj->n_data_chunks = cp->n_data_chunks;
|
||||
|
||||
if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE)
|
||||
obj->variant.file_variant.file_size = cp->size_or_equiv_obj;
|
||||
else if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK)
|
||||
obj->variant.hardlink_variant.equiv_id = cp->size_or_equiv_obj;
|
||||
|
||||
if (obj->hdr_chunk > 0)
|
||||
obj->lazy_loaded = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int yaffs2_checkpt_tnode_worker(struct yaffs_obj *in,
|
||||
struct yaffs_tnode *tn, u32 level,
|
||||
int chunk_offset)
|
||||
{
|
||||
int i;
|
||||
struct yaffs_dev *dev = in->my_dev;
|
||||
int ok = 1;
|
||||
u32 base_offset;
|
||||
|
||||
if (!tn)
|
||||
return 1;
|
||||
|
||||
if (level > 0) {
|
||||
for (i = 0; i < YAFFS_NTNODES_INTERNAL && ok; i++) {
|
||||
if (!tn->internal[i])
|
||||
continue;
|
||||
ok = yaffs2_checkpt_tnode_worker(in,
|
||||
tn->internal[i],
|
||||
level - 1,
|
||||
(chunk_offset <<
|
||||
YAFFS_TNODES_INTERNAL_BITS) + i);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
/* Level 0 tnode */
|
||||
base_offset = chunk_offset << YAFFS_TNODES_LEVEL0_BITS;
|
||||
ok = (yaffs2_checkpt_wr(dev, &base_offset, sizeof(base_offset)) ==
|
||||
sizeof(base_offset));
|
||||
if (ok)
|
||||
ok = (yaffs2_checkpt_wr(dev, tn, dev->tnode_size) ==
|
||||
(int)dev->tnode_size);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
static int yaffs2_wr_checkpt_tnodes(struct yaffs_obj *obj)
|
||||
{
|
||||
u32 end_marker = ~0;
|
||||
int ok = 1;
|
||||
|
||||
if (obj->variant_type != YAFFS_OBJECT_TYPE_FILE)
|
||||
return ok;
|
||||
|
||||
ok = yaffs2_checkpt_tnode_worker(obj,
|
||||
obj->variant.file_variant.top,
|
||||
obj->variant.file_variant.
|
||||
top_level, 0);
|
||||
if (ok)
|
||||
ok = (yaffs2_checkpt_wr(obj->my_dev, &end_marker,
|
||||
sizeof(end_marker)) == sizeof(end_marker));
|
||||
|
||||
return ok ? 1 : 0;
|
||||
}
|
||||
|
||||
static int yaffs2_rd_checkpt_tnodes(struct yaffs_obj *obj)
|
||||
{
|
||||
u32 base_chunk;
|
||||
int ok = 1;
|
||||
struct yaffs_dev *dev = obj->my_dev;
|
||||
struct yaffs_file_var *file_stuct_ptr = &obj->variant.file_variant;
|
||||
struct yaffs_tnode *tn;
|
||||
int nread = 0;
|
||||
|
||||
ok = (yaffs2_checkpt_rd(dev, &base_chunk, sizeof(base_chunk)) ==
|
||||
(int)sizeof(base_chunk));
|
||||
|
||||
while (ok && (~base_chunk)) {
|
||||
nread++;
|
||||
/* Read level 0 tnode */
|
||||
|
||||
tn = yaffs_get_tnode(dev);
|
||||
if (tn)
|
||||
ok = (yaffs2_checkpt_rd(dev, tn, dev->tnode_size) ==
|
||||
(int)dev->tnode_size);
|
||||
else
|
||||
ok = 0;
|
||||
|
||||
if (tn && ok)
|
||||
ok = yaffs_add_find_tnode_0(dev,
|
||||
file_stuct_ptr,
|
||||
base_chunk, tn) ? 1 : 0;
|
||||
|
||||
if (ok)
|
||||
ok = (yaffs2_checkpt_rd
|
||||
(dev, &base_chunk,
|
||||
sizeof(base_chunk)) == (int)sizeof(base_chunk));
|
||||
}
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"Checkpoint read tnodes %d records, last %d. ok %d",
|
||||
nread, base_chunk, ok);
|
||||
|
||||
return ok ? 1 : 0;
|
||||
}
|
||||
|
||||
static int yaffs2_wr_checkpt_objs(struct yaffs_dev *dev)
|
||||
{
|
||||
struct yaffs_obj *obj;
|
||||
struct yaffs_checkpt_obj cp;
|
||||
int i;
|
||||
int ok = 1;
|
||||
struct list_head *lh;
|
||||
|
||||
/* Iterate through the objects in each hash entry,
|
||||
* dumping them to the checkpointing stream.
|
||||
*/
|
||||
|
||||
for (i = 0; ok && i < YAFFS_NOBJECT_BUCKETS; i++) {
|
||||
list_for_each(lh, &dev->obj_bucket[i].list) {
|
||||
obj = list_entry(lh, struct yaffs_obj, hash_link);
|
||||
if (!obj->defered_free) {
|
||||
yaffs2_obj_checkpt_obj(&cp, obj);
|
||||
cp.struct_type = sizeof(cp);
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"Checkpoint write object %d parent %d type %d chunk %d obj addr %p",
|
||||
cp.obj_id, cp.parent_id,
|
||||
cp.variant_type, cp.hdr_chunk, obj);
|
||||
|
||||
ok = (yaffs2_checkpt_wr(dev, &cp,
|
||||
sizeof(cp)) == sizeof(cp));
|
||||
|
||||
if (ok &&
|
||||
obj->variant_type ==
|
||||
YAFFS_OBJECT_TYPE_FILE)
|
||||
ok = yaffs2_wr_checkpt_tnodes(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Dump end of list */
|
||||
memset(&cp, 0xff, sizeof(struct yaffs_checkpt_obj));
|
||||
cp.struct_type = sizeof(cp);
|
||||
|
||||
if (ok)
|
||||
ok = (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp));
|
||||
|
||||
return ok ? 1 : 0;
|
||||
}
|
||||
|
||||
static int yaffs2_rd_checkpt_objs(struct yaffs_dev *dev)
|
||||
{
|
||||
struct yaffs_obj *obj;
|
||||
struct yaffs_checkpt_obj cp;
|
||||
int ok = 1;
|
||||
int done = 0;
|
||||
LIST_HEAD(hard_list);
|
||||
|
||||
|
||||
while (ok && !done) {
|
||||
ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == (int)sizeof(cp));
|
||||
if (cp.struct_type != sizeof(cp)) {
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"struct size %d instead of %d ok %d",
|
||||
cp.struct_type, (int)sizeof(cp), ok);
|
||||
ok = 0;
|
||||
}
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"Checkpoint read object %d parent %d type %d chunk %d ",
|
||||
cp.obj_id, cp.parent_id, cp.variant_type,
|
||||
cp.hdr_chunk);
|
||||
|
||||
if (ok && cp.obj_id == (u32)~0) {
|
||||
done = 1;
|
||||
} else if (ok) {
|
||||
obj =
|
||||
yaffs_find_or_create_by_number(dev, cp.obj_id,
|
||||
cp.variant_type);
|
||||
if (obj) {
|
||||
ok = yaffs2_checkpt_obj_to_obj(obj, &cp);
|
||||
if (!ok)
|
||||
break;
|
||||
if (obj->variant_type ==
|
||||
YAFFS_OBJECT_TYPE_FILE) {
|
||||
ok = yaffs2_rd_checkpt_tnodes(obj);
|
||||
} else if (obj->variant_type ==
|
||||
YAFFS_OBJECT_TYPE_HARDLINK) {
|
||||
list_add(&obj->hard_links, &hard_list);
|
||||
}
|
||||
} else {
|
||||
ok = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ok)
|
||||
yaffs_link_fixup(dev, &hard_list);
|
||||
|
||||
return ok ? 1 : 0;
|
||||
}
|
||||
|
||||
static int yaffs2_wr_checkpt_sum(struct yaffs_dev *dev)
|
||||
{
|
||||
u32 checkpt_sum;
|
||||
int ok;
|
||||
|
||||
yaffs2_get_checkpt_sum(dev, &checkpt_sum);
|
||||
|
||||
ok = (yaffs2_checkpt_wr(dev, &checkpt_sum, sizeof(checkpt_sum)) ==
|
||||
sizeof(checkpt_sum));
|
||||
|
||||
if (!ok)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int yaffs2_rd_checkpt_sum(struct yaffs_dev *dev)
|
||||
{
|
||||
u32 checkpt_sum0;
|
||||
u32 checkpt_sum1;
|
||||
int ok;
|
||||
|
||||
yaffs2_get_checkpt_sum(dev, &checkpt_sum0);
|
||||
|
||||
ok = (yaffs2_checkpt_rd(dev, &checkpt_sum1, sizeof(checkpt_sum1)) ==
|
||||
(int)sizeof(checkpt_sum1));
|
||||
|
||||
if (!ok)
|
||||
return 0;
|
||||
|
||||
if (checkpt_sum0 != checkpt_sum1)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int yaffs2_wr_checkpt_data(struct yaffs_dev *dev)
|
||||
{
|
||||
int ok = 1;
|
||||
|
||||
if (!yaffs2_checkpt_required(dev)) {
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"skipping checkpoint write");
|
||||
ok = 0;
|
||||
}
|
||||
|
||||
if (ok)
|
||||
ok = yaffs2_checkpt_open(dev, 1);
|
||||
|
||||
if (ok) {
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"write checkpoint validity");
|
||||
ok = yaffs2_wr_checkpt_validity_marker(dev, 1);
|
||||
}
|
||||
if (ok) {
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"write checkpoint device");
|
||||
ok = yaffs2_wr_checkpt_dev(dev);
|
||||
}
|
||||
if (ok) {
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"write checkpoint objects");
|
||||
ok = yaffs2_wr_checkpt_objs(dev);
|
||||
}
|
||||
if (ok) {
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"write checkpoint validity");
|
||||
ok = yaffs2_wr_checkpt_validity_marker(dev, 0);
|
||||
}
|
||||
|
||||
if (ok)
|
||||
ok = yaffs2_wr_checkpt_sum(dev);
|
||||
|
||||
if (!yaffs_checkpt_close(dev))
|
||||
ok = 0;
|
||||
|
||||
if (ok)
|
||||
dev->is_checkpointed = 1;
|
||||
else
|
||||
dev->is_checkpointed = 0;
|
||||
|
||||
return dev->is_checkpointed;
|
||||
}
|
||||
|
||||
static int yaffs2_rd_checkpt_data(struct yaffs_dev *dev)
|
||||
{
|
||||
int ok = 1;
|
||||
|
||||
if (!dev->param.is_yaffs2)
|
||||
ok = 0;
|
||||
|
||||
if (ok && dev->param.skip_checkpt_rd) {
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"skipping checkpoint read");
|
||||
ok = 0;
|
||||
}
|
||||
|
||||
if (ok)
|
||||
ok = yaffs2_checkpt_open(dev, 0); /* open for read */
|
||||
|
||||
if (ok) {
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"read checkpoint validity");
|
||||
ok = yaffs2_rd_checkpt_validity_marker(dev, 1);
|
||||
}
|
||||
if (ok) {
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"read checkpoint device");
|
||||
ok = yaffs2_rd_checkpt_dev(dev);
|
||||
}
|
||||
if (ok) {
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"read checkpoint objects");
|
||||
ok = yaffs2_rd_checkpt_objs(dev);
|
||||
}
|
||||
if (ok) {
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"read checkpoint validity");
|
||||
ok = yaffs2_rd_checkpt_validity_marker(dev, 0);
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
ok = yaffs2_rd_checkpt_sum(dev);
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"read checkpoint checksum %d", ok);
|
||||
}
|
||||
|
||||
if (!yaffs_checkpt_close(dev))
|
||||
ok = 0;
|
||||
|
||||
if (ok)
|
||||
dev->is_checkpointed = 1;
|
||||
else
|
||||
dev->is_checkpointed = 0;
|
||||
|
||||
return ok ? 1 : 0;
|
||||
}
|
||||
|
||||
void yaffs2_checkpt_invalidate(struct yaffs_dev *dev)
|
||||
{
|
||||
if (dev->is_checkpointed || dev->blocks_in_checkpt > 0) {
|
||||
dev->is_checkpointed = 0;
|
||||
yaffs2_checkpt_invalidate_stream(dev);
|
||||
}
|
||||
if (dev->param.sb_dirty_fn)
|
||||
dev->param.sb_dirty_fn(dev);
|
||||
}
|
||||
|
||||
int yaffs_checkpoint_save(struct yaffs_dev *dev)
|
||||
{
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"save entry: is_checkpointed %d",
|
||||
dev->is_checkpointed);
|
||||
|
||||
yaffs_verify_objects(dev);
|
||||
yaffs_verify_blocks(dev);
|
||||
yaffs_verify_free_chunks(dev);
|
||||
|
||||
if (!dev->is_checkpointed) {
|
||||
yaffs2_checkpt_invalidate(dev);
|
||||
yaffs2_wr_checkpt_data(dev);
|
||||
}
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT | YAFFS_TRACE_MOUNT,
|
||||
"save exit: is_checkpointed %d",
|
||||
dev->is_checkpointed);
|
||||
|
||||
return dev->is_checkpointed;
|
||||
}
|
||||
|
||||
int yaffs2_checkpt_restore(struct yaffs_dev *dev)
|
||||
{
|
||||
int retval;
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"restore entry: is_checkpointed %d",
|
||||
dev->is_checkpointed);
|
||||
|
||||
retval = yaffs2_rd_checkpt_data(dev);
|
||||
|
||||
if (dev->is_checkpointed) {
|
||||
yaffs_verify_objects(dev);
|
||||
yaffs_verify_blocks(dev);
|
||||
yaffs_verify_free_chunks(dev);
|
||||
}
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_CHECKPOINT,
|
||||
"restore exit: is_checkpointed %d",
|
||||
dev->is_checkpointed);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
int yaffs2_handle_hole(struct yaffs_obj *obj, Y_LOFF_T new_size)
|
||||
{
|
||||
/* if new_size > old_file_size.
|
||||
* We're going to be writing a hole.
|
||||
* If the hole is small then write zeros otherwise write a start
|
||||
* of hole marker.
|
||||
*/
|
||||
Y_LOFF_T old_file_size;
|
||||
Y_LOFF_T increase;
|
||||
int small_hole;
|
||||
int result = YAFFS_OK;
|
||||
struct yaffs_dev *dev = NULL;
|
||||
u8 *local_buffer = NULL;
|
||||
int small_increase_ok = 0;
|
||||
|
||||
if (!obj)
|
||||
return YAFFS_FAIL;
|
||||
|
||||
if (obj->variant_type != YAFFS_OBJECT_TYPE_FILE)
|
||||
return YAFFS_FAIL;
|
||||
|
||||
dev = obj->my_dev;
|
||||
|
||||
/* Bail out if not yaffs2 mode */
|
||||
if (!dev->param.is_yaffs2)
|
||||
return YAFFS_OK;
|
||||
|
||||
old_file_size = obj->variant.file_variant.file_size;
|
||||
|
||||
if (new_size <= old_file_size)
|
||||
return YAFFS_OK;
|
||||
|
||||
increase = new_size - old_file_size;
|
||||
|
||||
if (increase < YAFFS_SMALL_HOLE_THRESHOLD * dev->data_bytes_per_chunk &&
|
||||
yaffs_check_alloc_available(dev, YAFFS_SMALL_HOLE_THRESHOLD + 1))
|
||||
small_hole = 1;
|
||||
else
|
||||
small_hole = 0;
|
||||
|
||||
if (small_hole)
|
||||
local_buffer = yaffs_get_temp_buffer(dev);
|
||||
|
||||
if (local_buffer) {
|
||||
/* fill hole with zero bytes */
|
||||
Y_LOFF_T pos = old_file_size;
|
||||
int this_write;
|
||||
int written;
|
||||
memset(local_buffer, 0, dev->data_bytes_per_chunk);
|
||||
small_increase_ok = 1;
|
||||
|
||||
while (increase > 0 && small_increase_ok) {
|
||||
this_write = increase;
|
||||
if (this_write > dev->data_bytes_per_chunk)
|
||||
this_write = dev->data_bytes_per_chunk;
|
||||
written =
|
||||
yaffs_do_file_wr(obj, local_buffer, pos, this_write,
|
||||
0);
|
||||
if (written == this_write) {
|
||||
pos += this_write;
|
||||
increase -= this_write;
|
||||
} else {
|
||||
small_increase_ok = 0;
|
||||
}
|
||||
}
|
||||
|
||||
yaffs_release_temp_buffer(dev, local_buffer);
|
||||
|
||||
/* If out of space then reverse any chunks we've added */
|
||||
if (!small_increase_ok)
|
||||
yaffs_resize_file_down(obj, old_file_size);
|
||||
}
|
||||
|
||||
if (!small_increase_ok &&
|
||||
obj->parent &&
|
||||
obj->parent->obj_id != YAFFS_OBJECTID_UNLINKED &&
|
||||
obj->parent->obj_id != YAFFS_OBJECTID_DELETED) {
|
||||
/* Write a hole start header with the old file size */
|
||||
yaffs_update_oh(obj, NULL, 0, 1, 0, NULL);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
struct yaffs_block_index {
|
||||
int seq;
|
||||
int block;
|
||||
};
|
||||
|
||||
static int yaffs2_ybicmp(const void *a, const void *b)
|
||||
{
|
||||
int aseq = ((struct yaffs_block_index *)a)->seq;
|
||||
int bseq = ((struct yaffs_block_index *)b)->seq;
|
||||
int ablock = ((struct yaffs_block_index *)a)->block;
|
||||
int bblock = ((struct yaffs_block_index *)b)->block;
|
||||
|
||||
if (aseq == bseq)
|
||||
return ablock - bblock;
|
||||
|
||||
return aseq - bseq;
|
||||
}
|
||||
|
||||
static inline int yaffs2_scan_chunk(struct yaffs_dev *dev,
|
||||
struct yaffs_block_info *bi,
|
||||
int blk, int chunk_in_block,
|
||||
int *found_chunks,
|
||||
u8 *chunk_data,
|
||||
struct list_head *hard_list,
|
||||
int summary_available)
|
||||
{
|
||||
struct yaffs_obj_hdr *oh;
|
||||
struct yaffs_obj *in;
|
||||
struct yaffs_obj *parent;
|
||||
int equiv_id;
|
||||
Y_LOFF_T file_size;
|
||||
int is_shrink;
|
||||
int is_unlinked;
|
||||
struct yaffs_ext_tags tags;
|
||||
//int result;
|
||||
int alloc_failed = 0;
|
||||
int chunk = blk * dev->param.chunks_per_block + chunk_in_block;
|
||||
struct yaffs_file_var *file_var;
|
||||
struct yaffs_hardlink_var *hl_var;
|
||||
struct yaffs_symlink_var *sl_var;
|
||||
|
||||
if (summary_available) {
|
||||
if (!yaffs_summary_fetch(dev, &tags, chunk_in_block))
|
||||
|
||||
{
|
||||
yaffs_trace(YAFFS_TRACE_ERROR, "yaffs2_scan_chunk: unhandled error from yaffs_summary_fetch");
|
||||
}
|
||||
tags.seq_number = bi->seq_number;
|
||||
}
|
||||
|
||||
if (!summary_available || tags.obj_id == 0) {
|
||||
if (!yaffs_rd_chunk_tags_nand(dev, chunk, NULL, &tags))
|
||||
|
||||
{
|
||||
yaffs_trace(YAFFS_TRACE_ERROR, "yaffs2_scan_chunk: unhandled error from rd_chunk_tags_nand");
|
||||
}
|
||||
dev->tags_used++;
|
||||
} else {
|
||||
dev->summary_used++;
|
||||
}
|
||||
|
||||
/* Let's have a good look at this chunk... */
|
||||
|
||||
if (!tags.chunk_used) {
|
||||
/* An unassigned chunk in the block.
|
||||
* If there are used chunks after this one, then
|
||||
* it is a chunk that was skipped due to failing
|
||||
* the erased check. Just skip it so that it can
|
||||
* be deleted.
|
||||
* But, more typically, We get here when this is
|
||||
* an unallocated chunk and his means that
|
||||
* either the block is empty or this is the one
|
||||
* being allocated from
|
||||
*/
|
||||
|
||||
if (*found_chunks) {
|
||||
/* This is a chunk that was skipped due
|
||||
* to failing the erased check */
|
||||
} else if (chunk_in_block == 0) {
|
||||
/* We're looking at the first chunk in
|
||||
* the block so the block is unused */
|
||||
bi->block_state = YAFFS_BLOCK_STATE_EMPTY;
|
||||
dev->n_erased_blocks++;
|
||||
} else {
|
||||
if (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN ||
|
||||
bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING) {
|
||||
if (dev->seq_number == bi->seq_number) {
|
||||
/* Allocating from this block*/
|
||||
yaffs_trace(YAFFS_TRACE_SCAN,
|
||||
" Allocating from %d %d",
|
||||
blk, chunk_in_block);
|
||||
|
||||
bi->block_state =
|
||||
YAFFS_BLOCK_STATE_ALLOCATING;
|
||||
dev->alloc_block = blk;
|
||||
dev->alloc_page = chunk_in_block;
|
||||
dev->alloc_block_finder = blk;
|
||||
} else {
|
||||
/* This is a partially written block
|
||||
* that is not the current
|
||||
* allocation block.
|
||||
*/
|
||||
yaffs_trace(YAFFS_TRACE_SCAN,
|
||||
"Partially written block %d detected. gc will fix this.",
|
||||
blk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dev->n_free_chunks++;
|
||||
|
||||
} else if (tags.ecc_result ==
|
||||
YAFFS_ECC_RESULT_UNFIXED) {
|
||||
yaffs_trace(YAFFS_TRACE_SCAN,
|
||||
" Unfixed ECC in chunk(%d:%d), chunk ignored",
|
||||
blk, chunk_in_block);
|
||||
dev->n_free_chunks++;
|
||||
} else if (tags.obj_id > YAFFS_MAX_OBJECT_ID ||
|
||||
tags.chunk_id > YAFFS_MAX_CHUNK_ID ||
|
||||
tags.obj_id == YAFFS_OBJECTID_SUMMARY ||
|
||||
(tags.chunk_id > 0 &&
|
||||
tags.n_bytes > (u32)dev->data_bytes_per_chunk) ||
|
||||
tags.seq_number != bi->seq_number) {
|
||||
yaffs_trace(YAFFS_TRACE_SCAN,
|
||||
"Chunk (%d:%d) with bad tags:obj = %d, chunk_id = %d, n_bytes = %d, ignored",
|
||||
blk, chunk_in_block, tags.obj_id,
|
||||
tags.chunk_id, tags.n_bytes);
|
||||
dev->n_free_chunks++;
|
||||
} else if (tags.chunk_id > 0) {
|
||||
/* chunk_id > 0 so it is a data chunk... */
|
||||
Y_LOFF_T endpos;
|
||||
Y_LOFF_T chunk_base = (tags.chunk_id - 1) *
|
||||
dev->data_bytes_per_chunk;
|
||||
|
||||
*found_chunks = 1;
|
||||
|
||||
yaffs_set_chunk_bit(dev, blk, chunk_in_block);
|
||||
bi->pages_in_use++;
|
||||
|
||||
in = yaffs_find_or_create_by_number(dev,
|
||||
tags.obj_id,
|
||||
YAFFS_OBJECT_TYPE_FILE);
|
||||
if (!in)
|
||||
/* Out of memory */
|
||||
alloc_failed = 1;
|
||||
|
||||
if (in &&
|
||||
in->variant_type == YAFFS_OBJECT_TYPE_FILE &&
|
||||
chunk_base < in->variant.file_variant.shrink_size) {
|
||||
/* This has not been invalidated by
|
||||
* a resize */
|
||||
if (!yaffs_put_chunk_in_file(in, tags.chunk_id,
|
||||
chunk, -1))
|
||||
alloc_failed = 1;
|
||||
|
||||
/* File size is calculated by looking at
|
||||
* the data chunks if we have not
|
||||
* seen an object header yet.
|
||||
* Stop this practice once we find an
|
||||
* object header.
|
||||
*/
|
||||
endpos = chunk_base + tags.n_bytes;
|
||||
|
||||
if (!in->valid &&
|
||||
in->variant.file_variant.scanned_size < endpos) {
|
||||
in->variant.file_variant.
|
||||
scanned_size = endpos;
|
||||
in->variant.file_variant.
|
||||
file_size = endpos;
|
||||
}
|
||||
} else if (in) {
|
||||
/* This chunk has been invalidated by a
|
||||
* resize, or a past file deletion
|
||||
* so delete the chunk*/
|
||||
yaffs_chunk_del(dev, chunk, 1, __LINE__);
|
||||
}
|
||||
} else {
|
||||
/* chunk_id == 0, so it is an ObjectHeader.
|
||||
* Thus, we read in the object header and make
|
||||
* the object
|
||||
*/
|
||||
*found_chunks = 1;
|
||||
|
||||
yaffs_set_chunk_bit(dev, blk, chunk_in_block);
|
||||
bi->pages_in_use++;
|
||||
|
||||
oh = NULL;
|
||||
in = NULL;
|
||||
|
||||
if (tags.extra_available) {
|
||||
in = yaffs_find_or_create_by_number(dev,
|
||||
tags.obj_id,
|
||||
tags.extra_obj_type);
|
||||
if (!in)
|
||||
alloc_failed = 1;
|
||||
}
|
||||
|
||||
if (!in ||
|
||||
(!in->valid && dev->param.disable_lazy_load) ||
|
||||
tags.extra_shadows ||
|
||||
(!in->valid && (tags.obj_id == YAFFS_OBJECTID_ROOT ||
|
||||
tags.obj_id == YAFFS_OBJECTID_LOSTNFOUND))) {
|
||||
|
||||
/* If we don't have valid info then we
|
||||
* need to read the chunk
|
||||
* TODO In future we can probably defer
|
||||
* reading the chunk and living with
|
||||
* invalid data until needed.
|
||||
*/
|
||||
|
||||
if (!yaffs_rd_chunk_tags_nand(dev,
|
||||
chunk,
|
||||
chunk_data,
|
||||
NULL))
|
||||
|
||||
{
|
||||
yaffs_trace(YAFFS_TRACE_ERROR, "yaffs2_scan_chunk: unhandled error from rd_chunk_tags_nand");
|
||||
}
|
||||
|
||||
oh = (struct yaffs_obj_hdr *)chunk_data;
|
||||
|
||||
if (dev->param.inband_tags) {
|
||||
/* Fix up the header if they got
|
||||
* corrupted by inband tags */
|
||||
oh->shadows_obj =
|
||||
oh->inband_shadowed_obj_id;
|
||||
oh->is_shrink =
|
||||
oh->inband_is_shrink;
|
||||
}
|
||||
|
||||
if (!in) {
|
||||
in = yaffs_find_or_create_by_number(dev,
|
||||
tags.obj_id, oh->type);
|
||||
if (!in)
|
||||
alloc_failed = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!in) {
|
||||
/* TODO Hoosterman we have a problem! */
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"yaffs tragedy: Could not make object for object %d at chunk %d during scan",
|
||||
tags.obj_id, chunk);
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
|
||||
if (in->valid) {
|
||||
/* We have already filled this one.
|
||||
* We have a duplicate that will be
|
||||
* discarded, but we first have to suck
|
||||
* out resize info if it is a file.
|
||||
*/
|
||||
if ((in->variant_type == YAFFS_OBJECT_TYPE_FILE) &&
|
||||
((oh && oh->type == YAFFS_OBJECT_TYPE_FILE) ||
|
||||
(tags.extra_available &&
|
||||
tags.extra_obj_type == YAFFS_OBJECT_TYPE_FILE)
|
||||
)) {
|
||||
Y_LOFF_T this_size = (oh) ?
|
||||
yaffs_oh_to_size(oh) :
|
||||
tags.extra_file_size;
|
||||
u32 parent_obj_id = (oh) ?
|
||||
(u32)oh->parent_obj_id :
|
||||
tags.extra_parent_id;
|
||||
|
||||
is_shrink = (oh) ?
|
||||
oh->is_shrink :
|
||||
tags.extra_is_shrink;
|
||||
|
||||
/* If it is deleted (unlinked
|
||||
* at start also means deleted)
|
||||
* we treat the file size as
|
||||
* being zeroed at this point.
|
||||
*/
|
||||
if (parent_obj_id == YAFFS_OBJECTID_DELETED ||
|
||||
parent_obj_id == YAFFS_OBJECTID_UNLINKED) {
|
||||
this_size = 0;
|
||||
is_shrink = 1;
|
||||
}
|
||||
|
||||
if (is_shrink &&
|
||||
in->variant.file_variant.shrink_size >
|
||||
this_size)
|
||||
in->variant.file_variant.shrink_size =
|
||||
this_size;
|
||||
|
||||
if (is_shrink)
|
||||
bi->has_shrink_hdr = 1;
|
||||
}
|
||||
/* Use existing - destroy this one. */
|
||||
yaffs_chunk_del(dev, chunk, 1, __LINE__);
|
||||
}
|
||||
|
||||
if (!in->valid && in->variant_type !=
|
||||
(oh ? oh->type : tags.extra_obj_type)) {
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"yaffs tragedy: Bad type, %d != %d, for object %d at chunk %d during scan",
|
||||
oh ? oh->type : tags.extra_obj_type,
|
||||
in->variant_type, tags.obj_id,
|
||||
chunk);
|
||||
in = yaffs_retype_obj(in, oh ? oh->type : tags.extra_obj_type);
|
||||
}
|
||||
|
||||
if (!in->valid &&
|
||||
(tags.obj_id == YAFFS_OBJECTID_ROOT ||
|
||||
tags.obj_id == YAFFS_OBJECTID_LOSTNFOUND)) {
|
||||
/* We only load some info, don't fiddle
|
||||
* with directory structure */
|
||||
in->valid = 1;
|
||||
|
||||
if (oh) {
|
||||
in->yst_mode = oh->yst_mode;
|
||||
yaffs_load_attribs(in, oh);
|
||||
in->lazy_loaded = 0;
|
||||
} else {
|
||||
in->lazy_loaded = 1;
|
||||
}
|
||||
in->hdr_chunk = chunk;
|
||||
|
||||
} else if (!in->valid) {
|
||||
/* we need to load this info */
|
||||
in->valid = 1;
|
||||
in->hdr_chunk = chunk;
|
||||
if (oh) {
|
||||
in->variant_type = oh->type;
|
||||
in->yst_mode = oh->yst_mode;
|
||||
yaffs_load_attribs(in, oh);
|
||||
|
||||
if (oh->shadows_obj > 0)
|
||||
yaffs_handle_shadowed_obj(dev,
|
||||
oh->shadows_obj, 1);
|
||||
|
||||
yaffs_set_obj_name_from_oh(in, oh);
|
||||
parent = yaffs_find_or_create_by_number(dev,
|
||||
oh->parent_obj_id,
|
||||
YAFFS_OBJECT_TYPE_DIRECTORY);
|
||||
file_size = yaffs_oh_to_size(oh);
|
||||
is_shrink = oh->is_shrink;
|
||||
equiv_id = oh->equiv_id;
|
||||
} else {
|
||||
in->variant_type = tags.extra_obj_type;
|
||||
parent = yaffs_find_or_create_by_number(dev,
|
||||
tags.extra_parent_id,
|
||||
YAFFS_OBJECT_TYPE_DIRECTORY);
|
||||
file_size = tags.extra_file_size;
|
||||
is_shrink = tags.extra_is_shrink;
|
||||
equiv_id = tags.extra_equiv_id;
|
||||
in->lazy_loaded = 1;
|
||||
}
|
||||
in->dirty = 0;
|
||||
|
||||
if (!parent)
|
||||
alloc_failed = 1;
|
||||
|
||||
/* directory stuff...
|
||||
* hook up to parent
|
||||
*/
|
||||
|
||||
if (parent &&
|
||||
parent->variant_type == YAFFS_OBJECT_TYPE_UNKNOWN) {
|
||||
/* Set up as a directory */
|
||||
parent->variant_type =
|
||||
YAFFS_OBJECT_TYPE_DIRECTORY;
|
||||
INIT_LIST_HEAD(&parent->
|
||||
variant.dir_variant.children);
|
||||
} else if (!parent ||
|
||||
parent->variant_type !=
|
||||
YAFFS_OBJECT_TYPE_DIRECTORY) {
|
||||
/* Hoosterman, another problem....
|
||||
* Trying to use a non-directory as a directory
|
||||
*/
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||
"yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found."
|
||||
);
|
||||
parent = dev->lost_n_found;
|
||||
}
|
||||
yaffs_add_obj_to_dir(parent, in);
|
||||
|
||||
is_unlinked = (parent == dev->del_dir) ||
|
||||
(parent == dev->unlinked_dir);
|
||||
|
||||
if (is_shrink)
|
||||
/* Mark the block */
|
||||
bi->has_shrink_hdr = 1;
|
||||
|
||||
/* Note re hardlinks.
|
||||
* Since we might scan a hardlink before its equivalent
|
||||
* object is scanned we put them all in a list.
|
||||
* After scanning is complete, we should have all the
|
||||
* objects, so we run through this list and fix up all
|
||||
* the chains.
|
||||
*/
|
||||
|
||||
switch (in->variant_type) {
|
||||
case YAFFS_OBJECT_TYPE_UNKNOWN:
|
||||
/* Todo got a problem */
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_FILE:
|
||||
file_var = &in->variant.file_variant;
|
||||
if (file_var->scanned_size < file_size) {
|
||||
/* This covers the case where the file
|
||||
* size is greater than the data held.
|
||||
* This will happen if the file is
|
||||
* resized to be larger than its
|
||||
* current data extents.
|
||||
*/
|
||||
file_var->file_size = file_size;
|
||||
file_var->scanned_size = file_size;
|
||||
}
|
||||
|
||||
if (file_var->shrink_size > file_size)
|
||||
file_var->shrink_size = file_size;
|
||||
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_HARDLINK:
|
||||
hl_var = &in->variant.hardlink_variant;
|
||||
if (!is_unlinked) {
|
||||
hl_var->equiv_id = equiv_id;
|
||||
list_add(&in->hard_links, hard_list);
|
||||
}
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_DIRECTORY:
|
||||
/* Do nothing */
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_SPECIAL:
|
||||
/* Do nothing */
|
||||
break;
|
||||
case YAFFS_OBJECT_TYPE_SYMLINK:
|
||||
sl_var = &in->variant.symlink_variant;
|
||||
if (oh) {
|
||||
sl_var->alias =
|
||||
yaffs_clone_str(oh->alias);
|
||||
if (!sl_var->alias)
|
||||
alloc_failed = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return alloc_failed ? YAFFS_FAIL : YAFFS_OK;
|
||||
}
|
||||
|
||||
int yaffs2_scan_backwards(struct yaffs_dev *dev)
|
||||
{
|
||||
int blk;
|
||||
int block_iter;
|
||||
int start_iter;
|
||||
int end_iter;
|
||||
int n_to_scan = 0;
|
||||
enum yaffs_block_state state;
|
||||
int c;
|
||||
//int deleted;
|
||||
LIST_HEAD(hard_list);
|
||||
struct yaffs_block_info *bi;
|
||||
u32 seq_number;
|
||||
int n_blocks = dev->internal_end_block - dev->internal_start_block + 1;
|
||||
u8 *chunk_data;
|
||||
int found_chunks;
|
||||
int alloc_failed = 0;
|
||||
struct yaffs_block_index *block_index = NULL;
|
||||
int alt_block_index = 0;
|
||||
int summary_available;
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_SCAN,
|
||||
"yaffs2_scan_backwards starts intstartblk %d intendblk %d...",
|
||||
dev->internal_start_block, dev->internal_end_block);
|
||||
|
||||
dev->seq_number = YAFFS_LOWEST_SEQUENCE_NUMBER;
|
||||
|
||||
block_index =
|
||||
kmalloc(n_blocks * sizeof(struct yaffs_block_index), GFP_NOFS);
|
||||
|
||||
if (!block_index) {
|
||||
block_index =
|
||||
vmalloc(n_blocks * sizeof(struct yaffs_block_index));
|
||||
alt_block_index = 1;
|
||||
}
|
||||
|
||||
if (!block_index) {
|
||||
yaffs_trace(YAFFS_TRACE_SCAN,
|
||||
"yaffs2_scan_backwards() could not allocate block index!"
|
||||
);
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
|
||||
dev->blocks_in_checkpt = 0;
|
||||
|
||||
chunk_data = yaffs_get_temp_buffer(dev);
|
||||
|
||||
/* Scan all the blocks to determine their state */
|
||||
bi = dev->block_info;
|
||||
for (blk = dev->internal_start_block; blk <= dev->internal_end_block;
|
||||
blk++) {
|
||||
yaffs_clear_chunk_bits(dev, blk);
|
||||
bi->pages_in_use = 0;
|
||||
bi->soft_del_pages = 0;
|
||||
|
||||
yaffs_query_init_block_state(dev, blk, &state, &seq_number);
|
||||
|
||||
bi->block_state = state;
|
||||
bi->seq_number = seq_number;
|
||||
|
||||
if (bi->seq_number == YAFFS_SEQUENCE_CHECKPOINT_DATA)
|
||||
bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT;
|
||||
if (bi->seq_number == YAFFS_SEQUENCE_BAD_BLOCK)
|
||||
bi->block_state = YAFFS_BLOCK_STATE_DEAD;
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_SCAN_DEBUG,
|
||||
"Block scanning block %d state %d seq %d",
|
||||
blk, bi->block_state, seq_number);
|
||||
|
||||
if (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT) {
|
||||
dev->blocks_in_checkpt++;
|
||||
|
||||
} else if (bi->block_state == YAFFS_BLOCK_STATE_DEAD) {
|
||||
yaffs_trace(YAFFS_TRACE_BAD_BLOCKS,
|
||||
"block %d is bad", blk);
|
||||
} else if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) {
|
||||
yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "Block empty ");
|
||||
dev->n_erased_blocks++;
|
||||
dev->n_free_chunks += dev->param.chunks_per_block;
|
||||
} else if (bi->block_state ==
|
||||
YAFFS_BLOCK_STATE_NEEDS_SCAN) {
|
||||
/* Determine the highest sequence number */
|
||||
if (seq_number >= YAFFS_LOWEST_SEQUENCE_NUMBER &&
|
||||
seq_number < YAFFS_HIGHEST_SEQUENCE_NUMBER) {
|
||||
block_index[n_to_scan].seq = seq_number;
|
||||
block_index[n_to_scan].block = blk;
|
||||
n_to_scan++;
|
||||
if (seq_number >= dev->seq_number)
|
||||
dev->seq_number = seq_number;
|
||||
} else {
|
||||
/* TODO: Nasty sequence number! */
|
||||
yaffs_trace(YAFFS_TRACE_SCAN,
|
||||
"Block scanning block %d has bad sequence number %d",
|
||||
blk, seq_number);
|
||||
}
|
||||
}
|
||||
bi++;
|
||||
}
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_ALWAYS, "%d blocks to be sorted...", n_to_scan);
|
||||
|
||||
cond_resched();
|
||||
|
||||
/* Sort the blocks by sequence number */
|
||||
sort(block_index, n_to_scan, sizeof(struct yaffs_block_index),
|
||||
yaffs2_ybicmp, NULL);
|
||||
|
||||
cond_resched();
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_SCAN, "...done");
|
||||
|
||||
/* Now scan the blocks looking at the data. */
|
||||
start_iter = 0;
|
||||
end_iter = n_to_scan - 1;
|
||||
yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "%d blocks to scan", n_to_scan);
|
||||
|
||||
/* For each block.... backwards */
|
||||
for (block_iter = end_iter;
|
||||
!alloc_failed && block_iter >= start_iter;
|
||||
block_iter--) {
|
||||
/* Cooperative multitasking! This loop can run for so
|
||||
long that watchdog timers expire. */
|
||||
cond_resched();
|
||||
|
||||
/* get the block to scan in the correct order */
|
||||
blk = block_index[block_iter].block;
|
||||
bi = yaffs_get_block_info(dev, blk);
|
||||
//deleted = 0;
|
||||
|
||||
summary_available = yaffs_summary_read(dev, dev->sum_tags, blk);
|
||||
|
||||
/* For each chunk in each block that needs scanning.... */
|
||||
found_chunks = 0;
|
||||
if (summary_available)
|
||||
c = dev->chunks_per_summary - 1;
|
||||
else
|
||||
c = dev->param.chunks_per_block - 1;
|
||||
|
||||
for (/* c is already initialised */;
|
||||
!alloc_failed && c >= 0 &&
|
||||
(bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN ||
|
||||
bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING);
|
||||
c--) {
|
||||
/* Scan backwards...
|
||||
* Read the tags and decide what to do
|
||||
*/
|
||||
if (yaffs2_scan_chunk(dev, bi, blk, c,
|
||||
&found_chunks, chunk_data,
|
||||
&hard_list, summary_available) ==
|
||||
YAFFS_FAIL)
|
||||
alloc_failed = 1;
|
||||
}
|
||||
|
||||
if (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN) {
|
||||
/* If we got this far while scanning, then the block
|
||||
* is fully allocated. */
|
||||
bi->block_state = YAFFS_BLOCK_STATE_FULL;
|
||||
}
|
||||
|
||||
/* Now let's see if it was dirty */
|
||||
if (bi->pages_in_use == 0 &&
|
||||
!bi->has_shrink_hdr &&
|
||||
bi->block_state == YAFFS_BLOCK_STATE_FULL) {
|
||||
yaffs_block_became_dirty(dev, blk);
|
||||
}
|
||||
}
|
||||
|
||||
yaffs_skip_rest_of_block(dev);
|
||||
|
||||
if (alt_block_index)
|
||||
vfree(block_index);
|
||||
else
|
||||
kfree(block_index);
|
||||
|
||||
/* Ok, we've done all the scanning.
|
||||
* Fix up the hard link chains.
|
||||
* We have scanned all the objects, now it's time to add these
|
||||
* hardlinks.
|
||||
*/
|
||||
yaffs_link_fixup(dev, &hard_list);
|
||||
|
||||
yaffs_release_temp_buffer(dev, chunk_data);
|
||||
|
||||
if (alloc_failed)
|
||||
return YAFFS_FAIL;
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_SCAN, "yaffs2_scan_backwards ends");
|
||||
|
||||
return YAFFS_OK;
|
||||
}
|
3867
flight/pios/common/libraries/yaffs2/yaffsfs.c
Normal file
3867
flight/pios/common/libraries/yaffs2/yaffsfs.c
Normal file
@ -0,0 +1,3867 @@
|
||||
/*
|
||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include "yaffsfs.h"
|
||||
#include "yaffs_guts.h"
|
||||
#include "yaffscfg.h"
|
||||
#include "yportenv.h"
|
||||
#include "yaffs_trace.h"
|
||||
|
||||
#include "string.h"
|
||||
|
||||
#define YAFFSFS_MAX_SYMLINK_DEREFERENCES 5
|
||||
|
||||
#ifndef NULL
|
||||
#define NULL ((void *)0)
|
||||
#endif
|
||||
|
||||
#define ROOT_DIR(dev) (((dev) && (dev)->is_mounted) ? (dev)->root_dir : NULL)
|
||||
|
||||
/* YAFFSFS_RW_SIZE must be a power of 2 */
|
||||
#define YAFFSFS_RW_SHIFT (13)
|
||||
#define YAFFSFS_RW_SIZE (1<<YAFFSFS_RW_SHIFT)
|
||||
|
||||
/* Some forward references */
|
||||
static struct yaffs_obj *yaffsfs_FindObject(struct yaffs_obj *relativeDirectory,
|
||||
const YCHAR *path,
|
||||
int symDepth, int getEquiv,
|
||||
struct yaffs_obj **dirOut,
|
||||
int *notDir, int *loop);
|
||||
|
||||
static void yaffsfs_RemoveObjectCallback(struct yaffs_obj *obj);
|
||||
static yaffs_DIR *yaffsfs_opendir_reldir_no_lock(
|
||||
struct yaffs_obj *reldir, const YCHAR *dirname);
|
||||
static int yaffsfs_closedir_no_lock(yaffs_DIR *dirent);
|
||||
|
||||
unsigned int yaffs_wr_attempts;
|
||||
|
||||
/*
|
||||
* Handle management.
|
||||
* There are open inodes in struct yaffsfs_Inode.
|
||||
* There are open file descriptors in yaffsfs_FileDes.
|
||||
* There are open handles in yaffsfs_FileDes.
|
||||
*
|
||||
* Things are structured this way to be like the Linux VFS model
|
||||
* so that interactions with the yaffs guts calls are similar.
|
||||
* That means more common code paths and less special code.
|
||||
* That means better testing etc.
|
||||
*
|
||||
* We have 3 layers because:
|
||||
* A handle is different than an fd because you can use dup()
|
||||
* to create a new handle that accesses the *same* fd. The two
|
||||
* handles will use the same offset (part of the fd). We only close
|
||||
* down the fd when there are no more handles accessing it.
|
||||
*
|
||||
* More than one fd can currently access one file, but each fd
|
||||
* has its own permsiions and offset.
|
||||
*/
|
||||
|
||||
struct yaffsfs_Inode {
|
||||
int count; /* Number of handles accessing this inode */
|
||||
struct yaffs_obj *iObj;
|
||||
};
|
||||
|
||||
|
||||
struct yaffsfs_DirSearchContext {
|
||||
struct yaffs_dirent de; /* directory entry */
|
||||
YCHAR name[NAME_MAX + 1]; /* name of directory being searched */
|
||||
struct yaffs_obj *dirObj; /* ptr to directory being searched */
|
||||
struct yaffs_obj *nextReturn; /* obj returned by next readddir */
|
||||
struct list_head others;
|
||||
s32 offset:20;
|
||||
u8 inUse:1;
|
||||
};
|
||||
|
||||
struct yaffsfs_FileDes {
|
||||
u8 isDir:1; /* This s a directory */
|
||||
u8 reading:1;
|
||||
u8 writing:1;
|
||||
u8 append:1;
|
||||
u8 shareRead:1;
|
||||
u8 shareWrite:1;
|
||||
s32 inodeId:12; /* Index to corresponding yaffsfs_Inode */
|
||||
s32 handleCount:10; /* Number of handles for this fd */
|
||||
union {
|
||||
Y_LOFF_T position; /* current position in file */
|
||||
yaffs_DIR *dir;
|
||||
} v;
|
||||
};
|
||||
|
||||
struct yaffsfs_Handle {
|
||||
short int fdId;
|
||||
short int useCount;
|
||||
};
|
||||
|
||||
|
||||
static struct yaffsfs_DirSearchContext yaffsfs_dsc[YAFFSFS_N_DSC];
|
||||
static struct yaffsfs_Inode yaffsfs_inode[YAFFSFS_N_HANDLES];
|
||||
static struct yaffsfs_FileDes yaffsfs_fd[YAFFSFS_N_HANDLES];
|
||||
static struct yaffsfs_Handle yaffsfs_handle[YAFFSFS_N_HANDLES];
|
||||
|
||||
static int yaffsfs_handlesInitialised;
|
||||
|
||||
unsigned yaffs_set_trace(unsigned tm)
|
||||
{
|
||||
yaffs_trace_mask = tm;
|
||||
return yaffs_trace_mask;
|
||||
}
|
||||
|
||||
unsigned yaffs_get_trace(void)
|
||||
{
|
||||
return yaffs_trace_mask;
|
||||
}
|
||||
|
||||
/*
|
||||
* yaffsfs_InitHandle
|
||||
* Inilitalise handle management on start-up.
|
||||
*/
|
||||
|
||||
static void yaffsfs_InitHandles(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (yaffsfs_handlesInitialised)
|
||||
return;
|
||||
|
||||
yaffsfs_handlesInitialised = 1;
|
||||
|
||||
memset(yaffsfs_inode, 0, sizeof(yaffsfs_inode));
|
||||
memset(yaffsfs_fd, 0, sizeof(yaffsfs_fd));
|
||||
memset(yaffsfs_handle, 0, sizeof(yaffsfs_handle));
|
||||
memset(yaffsfs_dsc, 0, sizeof(yaffsfs_dsc));
|
||||
|
||||
for (i = 0; i < YAFFSFS_N_HANDLES; i++)
|
||||
yaffsfs_fd[i].inodeId = -1;
|
||||
for (i = 0; i < YAFFSFS_N_HANDLES; i++)
|
||||
yaffsfs_handle[i].fdId = -1;
|
||||
}
|
||||
|
||||
static struct yaffsfs_Handle *yaffsfs_HandleToPointer(int h)
|
||||
{
|
||||
if (h >= 0 && h < YAFFSFS_N_HANDLES)
|
||||
return &yaffsfs_handle[h];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct yaffsfs_FileDes *yaffsfs_HandleToFileDes(int handle)
|
||||
{
|
||||
struct yaffsfs_Handle *h = yaffsfs_HandleToPointer(handle);
|
||||
|
||||
if (h && h->useCount > 0 && h->fdId >= 0 && h->fdId < YAFFSFS_N_HANDLES)
|
||||
return &yaffsfs_fd[h->fdId];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct yaffsfs_Inode *yaffsfs_HandleToInode(int handle)
|
||||
{
|
||||
struct yaffsfs_FileDes *fd = yaffsfs_HandleToFileDes(handle);
|
||||
|
||||
if (fd && fd->handleCount > 0 &&
|
||||
fd->inodeId >= 0 && fd->inodeId < YAFFSFS_N_HANDLES)
|
||||
return &yaffsfs_inode[fd->inodeId];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct yaffs_obj *yaffsfs_HandleToObject(int handle)
|
||||
{
|
||||
struct yaffsfs_Inode *in = yaffsfs_HandleToInode(handle);
|
||||
|
||||
if (in)
|
||||
return in->iObj;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* yaffsfs_FindInodeIdForObject
|
||||
* Find the inode entry for an object, if it exists.
|
||||
*/
|
||||
|
||||
static int yaffsfs_FindInodeIdForObject(struct yaffs_obj *obj)
|
||||
{
|
||||
int i;
|
||||
int ret = -1;
|
||||
|
||||
if (obj)
|
||||
obj = yaffs_get_equivalent_obj(obj);
|
||||
|
||||
/* Look for it in open inode table */
|
||||
for (i = 0; i < YAFFSFS_N_HANDLES && ret < 0; i++) {
|
||||
if (yaffsfs_inode[i].iObj == obj)
|
||||
ret = i;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* yaffsfs_GetInodeIdForObject
|
||||
* Grab an inode entry when opening a new inode.
|
||||
*/
|
||||
static int yaffsfs_GetInodeIdForObject(struct yaffs_obj *obj)
|
||||
{
|
||||
int i;
|
||||
int ret;
|
||||
struct yaffsfs_Inode *in = NULL;
|
||||
|
||||
if (obj)
|
||||
obj = yaffs_get_equivalent_obj(obj);
|
||||
|
||||
ret = yaffsfs_FindInodeIdForObject(obj);
|
||||
|
||||
for (i = 0; i < YAFFSFS_N_HANDLES && ret < 0; i++) {
|
||||
if (!yaffsfs_inode[i].iObj)
|
||||
ret = i;
|
||||
}
|
||||
|
||||
if (ret >= 0) {
|
||||
in = &yaffsfs_inode[ret];
|
||||
if (!in->iObj)
|
||||
in->count = 0;
|
||||
in->iObj = obj;
|
||||
in->count++;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int yaffsfs_CountHandles(struct yaffs_obj *obj)
|
||||
{
|
||||
int i = yaffsfs_FindInodeIdForObject(obj);
|
||||
|
||||
if (i >= 0)
|
||||
return yaffsfs_inode[i].count;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void yaffsfs_ReleaseInode(struct yaffsfs_Inode *in)
|
||||
{
|
||||
struct yaffs_obj *obj;
|
||||
|
||||
obj = in->iObj;
|
||||
obj->my_inode = NULL;
|
||||
in->iObj = NULL;
|
||||
|
||||
if (obj->unlinked)
|
||||
yaffs_del_obj(obj);
|
||||
}
|
||||
|
||||
static void yaffsfs_PutInode(int inodeId)
|
||||
{
|
||||
if (inodeId >= 0 && inodeId < YAFFSFS_N_HANDLES) {
|
||||
struct yaffsfs_Inode *in = &yaffsfs_inode[inodeId];
|
||||
in->count--;
|
||||
if (in->count <= 0) {
|
||||
yaffsfs_ReleaseInode(in);
|
||||
in->count = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int yaffsfs_NewHandle(struct yaffsfs_Handle **hptr)
|
||||
{
|
||||
int i;
|
||||
struct yaffsfs_Handle *h;
|
||||
|
||||
for (i = 0; i < YAFFSFS_N_HANDLES; i++) {
|
||||
h = &yaffsfs_handle[i];
|
||||
if (h->useCount < 1) {
|
||||
memset(h, 0, sizeof(struct yaffsfs_Handle));
|
||||
h->fdId = -1;
|
||||
h->useCount = 1;
|
||||
if (hptr)
|
||||
*hptr = h;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int yaffsfs_NewHandleAndFileDes(void)
|
||||
{
|
||||
int i;
|
||||
struct yaffsfs_FileDes *fd;
|
||||
struct yaffsfs_Handle *h = NULL;
|
||||
int handle = yaffsfs_NewHandle(&h);
|
||||
|
||||
if (handle < 0)
|
||||
return -1;
|
||||
|
||||
for (i = 0; i < YAFFSFS_N_HANDLES; i++) {
|
||||
fd = &yaffsfs_fd[i];
|
||||
if (fd->handleCount < 1) {
|
||||
memset(fd, 0, sizeof(struct yaffsfs_FileDes));
|
||||
fd->inodeId = -1;
|
||||
fd->handleCount = 1;
|
||||
h->fdId = i;
|
||||
return handle;
|
||||
}
|
||||
}
|
||||
|
||||
/* Dump the handle because we could not get a fd */
|
||||
h->useCount = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* yaffs_get_handle
|
||||
* Increase use of handle when reading/writing a file
|
||||
* Also gets the file descriptor.
|
||||
*/
|
||||
|
||||
static int yaffsfs_GetHandle(int handle)
|
||||
{
|
||||
struct yaffsfs_Handle *h = yaffsfs_HandleToPointer(handle);
|
||||
|
||||
if (h && h->useCount > 0) {
|
||||
h->useCount++;
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* yaffs_put_handle
|
||||
* Let go of a handle when closing a file or aborting an open or
|
||||
* ending a read or write.
|
||||
*/
|
||||
|
||||
static int yaffsfs_PutFileDes(int fdId)
|
||||
{
|
||||
struct yaffsfs_FileDes *fd;
|
||||
|
||||
if (fdId >= 0 && fdId < YAFFSFS_N_HANDLES) {
|
||||
fd = &yaffsfs_fd[fdId];
|
||||
fd->handleCount--;
|
||||
if (fd->handleCount < 1) {
|
||||
if (fd->isDir)
|
||||
yaffsfs_closedir_no_lock(fd->v.dir);
|
||||
if (fd->inodeId >= 0) {
|
||||
yaffsfs_PutInode(fd->inodeId);
|
||||
fd->inodeId = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int yaffsfs_PutHandle(int handle)
|
||||
{
|
||||
struct yaffsfs_Handle *h = yaffsfs_HandleToPointer(handle);
|
||||
|
||||
if (h && h->useCount > 0) {
|
||||
h->useCount--;
|
||||
if (h->useCount < 1) {
|
||||
yaffsfs_PutFileDes(h->fdId);
|
||||
h->fdId = -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void yaffsfs_BreakDeviceHandles(struct yaffs_dev *dev)
|
||||
{
|
||||
struct yaffsfs_FileDes *fd;
|
||||
struct yaffsfs_Handle *h;
|
||||
struct yaffs_obj *obj;
|
||||
int i;
|
||||
for (i = 0; i < YAFFSFS_N_HANDLES; i++) {
|
||||
h = yaffsfs_HandleToPointer(i);
|
||||
fd = yaffsfs_HandleToFileDes(i);
|
||||
obj = yaffsfs_HandleToObject(i);
|
||||
if (h && h->useCount > 0) {
|
||||
h->useCount = 0;
|
||||
h->fdId = 0;
|
||||
}
|
||||
if (fd && fd->handleCount > 0 && obj && obj->my_dev == dev) {
|
||||
fd->handleCount = 0;
|
||||
yaffsfs_PutInode(fd->inodeId);
|
||||
fd->inodeId = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Stuff to handle names.
|
||||
*/
|
||||
#ifdef CONFIG_YAFFS_CASE_INSENSITIVE
|
||||
#ifndef CONFIG_YAFFS_WINCE
|
||||
static int yaffs_toupper(YCHAR a)
|
||||
{
|
||||
if (a >= 'a' && a <= 'z')
|
||||
return (a - 'a') + 'A';
|
||||
else
|
||||
return a;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int yaffsfs_Match(YCHAR a, YCHAR b)
|
||||
{
|
||||
return (yaffs_toupper(a) == yaffs_toupper(b));
|
||||
}
|
||||
#else
|
||||
static int yaffsfs_Match(YCHAR a, YCHAR b)
|
||||
{
|
||||
/* case sensitive */
|
||||
return (a == b);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int yaffsfs_IsPathDivider(YCHAR ch)
|
||||
{
|
||||
const YCHAR *str = YAFFS_PATH_DIVIDERS;
|
||||
|
||||
while (*str) {
|
||||
if (*str == ch)
|
||||
return 1;
|
||||
str++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int yaffsfs_CheckNameLength(const YCHAR *name)
|
||||
{
|
||||
int retVal = 0;
|
||||
|
||||
int nameLength = yaffs_strnlen(name, YAFFS_MAX_NAME_LENGTH + 1);
|
||||
|
||||
if (nameLength == 0) {
|
||||
yaffsfs_SetError(-ENOENT);
|
||||
retVal = -1;
|
||||
} else if (nameLength > YAFFS_MAX_NAME_LENGTH) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
retVal = -1;
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
static int yaffsfs_alt_dir_path(const YCHAR *path, YCHAR **ret_path)
|
||||
{
|
||||
YCHAR *alt_path = NULL;
|
||||
int path_length;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* We don't have a definition for max path length.
|
||||
* We will use 3 * max name length instead.
|
||||
*/
|
||||
*ret_path = NULL;
|
||||
path_length = yaffs_strnlen(path, (YAFFS_MAX_NAME_LENGTH + 1) * 3 + 1);
|
||||
|
||||
/* If the last character is a path divider, then we need to
|
||||
* trim it back so that the name look-up works properly.
|
||||
* eg. /foo/new_dir/ -> /foo/newdir
|
||||
* Curveball: Need to handle multiple path dividers:
|
||||
* eg. /foof/sdfse///// -> /foo/sdfse
|
||||
*/
|
||||
if (path_length > 0 && yaffsfs_IsPathDivider(path[path_length - 1])) {
|
||||
alt_path = kmalloc(path_length + 1, 0);
|
||||
if (!alt_path)
|
||||
return -1;
|
||||
yaffs_strcpy(alt_path, path);
|
||||
for (i = path_length - 1;
|
||||
i >= 0 && yaffsfs_IsPathDivider(alt_path[i]); i--)
|
||||
alt_path[i] = (YCHAR) 0;
|
||||
}
|
||||
*ret_path = alt_path;
|
||||
return 0;
|
||||
}
|
||||
|
||||
LIST_HEAD(yaffsfs_deviceList);
|
||||
|
||||
/*
|
||||
* yaffsfs_FindDevice
|
||||
* yaffsfs_FindRoot
|
||||
* Scan the configuration list to find the device
|
||||
* Curveballs: Should match paths that end in '/' too
|
||||
* Curveball2 Might have "/x/ and "/x/y". Need to return the longest match
|
||||
*/
|
||||
static struct yaffs_dev *yaffsfs_FindDevice(const YCHAR *path,
|
||||
YCHAR **restOfPath)
|
||||
{
|
||||
struct list_head *cfg;
|
||||
const YCHAR *leftOver;
|
||||
const YCHAR *p;
|
||||
struct yaffs_dev *retval = NULL;
|
||||
struct yaffs_dev *dev = NULL;
|
||||
int thisMatchLength;
|
||||
int longestMatch = -1;
|
||||
int matching;
|
||||
|
||||
/*
|
||||
* Check all configs, choose the one that:
|
||||
* 1) Actually matches a prefix (ie /a amd /abc will not match
|
||||
* 2) Matches the longest.
|
||||
*/
|
||||
list_for_each(cfg, &yaffsfs_deviceList) {
|
||||
dev = list_entry(cfg, struct yaffs_dev, dev_list);
|
||||
leftOver = path;
|
||||
p = dev->param.name;
|
||||
thisMatchLength = 0;
|
||||
matching = 1;
|
||||
|
||||
if(!p)
|
||||
continue;
|
||||
|
||||
while (matching && *p && *leftOver) {
|
||||
/* Skip over any /s */
|
||||
while (yaffsfs_IsPathDivider(*p))
|
||||
p++;
|
||||
|
||||
/* Skip over any /s */
|
||||
while (yaffsfs_IsPathDivider(*leftOver))
|
||||
leftOver++;
|
||||
|
||||
/* Now match the text part */
|
||||
while (matching &&
|
||||
*p && !yaffsfs_IsPathDivider(*p) &&
|
||||
*leftOver && !yaffsfs_IsPathDivider(*leftOver)) {
|
||||
if (yaffsfs_Match(*p, *leftOver)) {
|
||||
p++;
|
||||
leftOver++;
|
||||
thisMatchLength++;
|
||||
} else {
|
||||
matching = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Skip over any /s in leftOver */
|
||||
while (yaffsfs_IsPathDivider(*leftOver))
|
||||
leftOver++;
|
||||
|
||||
/*Skip over any /s in p */
|
||||
while (yaffsfs_IsPathDivider(*p))
|
||||
p++;
|
||||
|
||||
/* p should now be at the end of the string if fully matched */
|
||||
if (*p)
|
||||
matching = 0;
|
||||
|
||||
if (matching && (thisMatchLength > longestMatch)) {
|
||||
/* Matched prefix */
|
||||
*restOfPath = (YCHAR *) leftOver;
|
||||
retval = dev;
|
||||
longestMatch = thisMatchLength;
|
||||
}
|
||||
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int yaffsfs_CheckPath(const YCHAR *path)
|
||||
{
|
||||
int n = 0;
|
||||
int divs = 0;
|
||||
|
||||
while (*path && n < YAFFS_MAX_NAME_LENGTH && divs < 100) {
|
||||
if (yaffsfs_IsPathDivider(*path)) {
|
||||
n = 0;
|
||||
divs++;
|
||||
} else
|
||||
n++;
|
||||
path++;
|
||||
}
|
||||
|
||||
return (*path) ? -1 : 0;
|
||||
}
|
||||
|
||||
/* FindMountPoint only returns a dev entry if the path is a mount point */
|
||||
static struct yaffs_dev *yaffsfs_FindMountPoint(const YCHAR *path)
|
||||
{
|
||||
struct yaffs_dev *dev;
|
||||
YCHAR *restOfPath = NULL;
|
||||
|
||||
dev = yaffsfs_FindDevice(path, &restOfPath);
|
||||
if (dev && restOfPath && *restOfPath)
|
||||
dev = NULL;
|
||||
return dev;
|
||||
}
|
||||
|
||||
static struct yaffs_obj *yaffsfs_FindRoot(const YCHAR *path,
|
||||
YCHAR **restOfPath)
|
||||
{
|
||||
struct yaffs_dev *dev;
|
||||
|
||||
dev = yaffsfs_FindDevice(path, restOfPath);
|
||||
if (dev && dev->is_mounted)
|
||||
return dev->root_dir;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct yaffs_obj *yaffsfs_FollowLink(struct yaffs_obj *obj,
|
||||
int symDepth, int *loop)
|
||||
{
|
||||
|
||||
if (obj)
|
||||
obj = yaffs_get_equivalent_obj(obj);
|
||||
|
||||
while (obj && obj->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) {
|
||||
YCHAR *alias = obj->variant.symlink_variant.alias;
|
||||
|
||||
if (yaffsfs_IsPathDivider(*alias))
|
||||
/*
|
||||
* Starts with a /, need to scan from root up */
|
||||
/* NB Might not work if this is called with root != NULL
|
||||
*/
|
||||
obj = yaffsfs_FindObject(NULL, alias, symDepth++,
|
||||
1, NULL, NULL, loop);
|
||||
else
|
||||
/*
|
||||
* Relative to here so use the parent of the
|
||||
* symlink as a start
|
||||
*/
|
||||
obj = yaffsfs_FindObject(obj->parent, alias, symDepth++,
|
||||
1, NULL, NULL, loop);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
/*
|
||||
* yaffsfs_FindDirectory
|
||||
* Parse a path to determine the directory and the name within the directory.
|
||||
*
|
||||
* eg. "/data/xx/ff" --> puts name="ff" and returns the directory "/data/xx"
|
||||
*/
|
||||
static struct yaffs_obj *yaffsfs_DoFindDirectory(struct yaffs_obj *startDir,
|
||||
const YCHAR *path,
|
||||
YCHAR **name, int symDepth,
|
||||
int *notDir, int *loop)
|
||||
{
|
||||
struct yaffs_obj *dir;
|
||||
YCHAR *restOfPath;
|
||||
YCHAR str[YAFFS_MAX_NAME_LENGTH + 1];
|
||||
int i;
|
||||
|
||||
if (symDepth > YAFFSFS_MAX_SYMLINK_DEREFERENCES) {
|
||||
if (loop)
|
||||
*loop = 1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (startDir) {
|
||||
dir = startDir;
|
||||
restOfPath = (YCHAR *) path;
|
||||
} else
|
||||
dir = yaffsfs_FindRoot(path, &restOfPath);
|
||||
|
||||
while (dir) {
|
||||
/*
|
||||
* parse off /.
|
||||
* curve ball: also throw away surplus '/'
|
||||
* eg. "/ram/x////ff" gets treated the same as "/ram/x/ff"
|
||||
*/
|
||||
while (yaffsfs_IsPathDivider(*restOfPath))
|
||||
restOfPath++; /* get rid of '/' */
|
||||
|
||||
*name = restOfPath;
|
||||
i = 0;
|
||||
|
||||
while (*restOfPath && !yaffsfs_IsPathDivider(*restOfPath)) {
|
||||
if (i < YAFFS_MAX_NAME_LENGTH) {
|
||||
str[i] = *restOfPath;
|
||||
str[i + 1] = '\0';
|
||||
i++;
|
||||
}
|
||||
restOfPath++;
|
||||
}
|
||||
|
||||
if (!*restOfPath)
|
||||
/* got to the end of the string */
|
||||
return dir;
|
||||
else {
|
||||
if (yaffs_strcmp(str, _Y(".")) == 0) {
|
||||
/* Do nothing */
|
||||
} else if (yaffs_strcmp(str, _Y("..")) == 0) {
|
||||
dir = dir->parent;
|
||||
} else {
|
||||
dir = yaffs_find_by_name(dir, str);
|
||||
|
||||
dir = yaffsfs_FollowLink(dir, symDepth, loop);
|
||||
|
||||
if (dir && dir->variant_type !=
|
||||
YAFFS_OBJECT_TYPE_DIRECTORY) {
|
||||
if (notDir)
|
||||
*notDir = 1;
|
||||
dir = NULL;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
/* directory did not exist. */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct yaffs_obj *yaffsfs_FindDirectory(struct yaffs_obj *relDir,
|
||||
const YCHAR *path,
|
||||
YCHAR **name,
|
||||
int symDepth,
|
||||
int *notDir, int *loop)
|
||||
{
|
||||
return yaffsfs_DoFindDirectory(relDir, path, name, symDepth, notDir,
|
||||
loop);
|
||||
}
|
||||
|
||||
/*
|
||||
* yaffsfs_FindObject turns a path for an existing object into the object
|
||||
*/
|
||||
static struct yaffs_obj *yaffsfs_FindObject(struct yaffs_obj *relDir,
|
||||
const YCHAR *path, int symDepth,
|
||||
int getEquiv,
|
||||
struct yaffs_obj **dirOut,
|
||||
int *notDir, int *loop)
|
||||
{
|
||||
struct yaffs_obj *dir;
|
||||
struct yaffs_obj *obj;
|
||||
YCHAR *name;
|
||||
|
||||
dir =
|
||||
yaffsfs_FindDirectory(relDir, path, &name, symDepth, notDir, loop);
|
||||
|
||||
if (dirOut)
|
||||
*dirOut = dir;
|
||||
|
||||
if (dir && *name)
|
||||
obj = yaffs_find_by_name(dir, name);
|
||||
else
|
||||
obj = dir;
|
||||
|
||||
if (getEquiv)
|
||||
obj = yaffs_get_equivalent_obj(obj);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
* Start of yaffsfs visible functions.
|
||||
*************************************************************************/
|
||||
|
||||
int yaffs_dup(int handle)
|
||||
{
|
||||
int newHandleNumber = -1;
|
||||
struct yaffsfs_FileDes *existingFD = NULL;
|
||||
struct yaffsfs_Handle *existingHandle = NULL;
|
||||
struct yaffsfs_Handle *newHandle = NULL;
|
||||
|
||||
yaffsfs_Lock();
|
||||
existingHandle = yaffsfs_HandleToPointer(handle);
|
||||
existingFD = yaffsfs_HandleToFileDes(handle);
|
||||
if (existingFD)
|
||||
newHandleNumber = yaffsfs_NewHandle(&newHandle);
|
||||
if (newHandle) {
|
||||
newHandle->fdId = existingHandle->fdId;
|
||||
existingFD->handleCount++;
|
||||
}
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
if (!existingFD)
|
||||
yaffsfs_SetError(-EBADF);
|
||||
else if (!newHandle)
|
||||
yaffsfs_SetError(-ENOMEM);
|
||||
|
||||
return newHandleNumber;
|
||||
|
||||
}
|
||||
|
||||
static int yaffsfs_TooManyObjects(struct yaffs_dev *dev)
|
||||
{
|
||||
int current_objects = dev->n_obj - dev->n_deleted_files;
|
||||
|
||||
if (dev->param.max_objects && current_objects > dev->param.max_objects)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
int yaffs_open_sharing_reldir(struct yaffs_obj *reldir, const YCHAR *path,
|
||||
int oflag, int mode, int sharing)
|
||||
{
|
||||
struct yaffs_obj *obj = NULL;
|
||||
struct yaffs_obj *dir = NULL;
|
||||
YCHAR *name;
|
||||
int handle = -1;
|
||||
struct yaffsfs_FileDes *fd = NULL;
|
||||
int openDenied = 0;
|
||||
int symDepth = 0;
|
||||
int errorReported = 0;
|
||||
int rwflags = oflag & (O_RDWR | O_RDONLY | O_WRONLY);
|
||||
u8 shareRead = (sharing & YAFFS_SHARE_READ) ? 1 : 0;
|
||||
u8 shareWrite = (sharing & YAFFS_SHARE_WRITE) ? 1 : 0;
|
||||
u8 sharedReadAllowed;
|
||||
u8 sharedWriteAllowed;
|
||||
u8 alreadyReading;
|
||||
u8 alreadyWriting;
|
||||
u8 readRequested;
|
||||
u8 writeRequested;
|
||||
int notDir = 0;
|
||||
int loop = 0;
|
||||
int is_dir = 0;
|
||||
yaffs_DIR *dsc;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(path, 0, 0)< 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_CheckPath(path) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* O_EXCL only has meaning if O_CREAT is specified */
|
||||
if (!(oflag & O_CREAT))
|
||||
oflag &= ~(O_EXCL);
|
||||
|
||||
/* O_TRUNC has no meaning if (O_CREAT | O_EXCL) is specified */
|
||||
if ((oflag & O_CREAT) & (oflag & O_EXCL))
|
||||
oflag &= ~(O_TRUNC);
|
||||
|
||||
/* Todo: Are there any more flag combos to sanitise ? */
|
||||
|
||||
/* Figure out if reading or writing is requested */
|
||||
|
||||
readRequested = (rwflags == O_RDWR || rwflags == O_RDONLY) ? 1 : 0;
|
||||
writeRequested = (rwflags == O_RDWR || rwflags == O_WRONLY) ? 1 : 0;
|
||||
|
||||
yaffsfs_Lock();
|
||||
|
||||
handle = yaffsfs_NewHandleAndFileDes();
|
||||
|
||||
if (handle < 0) {
|
||||
yaffsfs_SetError(-ENFILE);
|
||||
errorReported = 1;
|
||||
} else {
|
||||
|
||||
fd = yaffsfs_HandleToFileDes(handle);
|
||||
|
||||
/* try to find the exisiting object */
|
||||
obj = yaffsfs_FindObject(reldir, path, 0, 1, NULL, NULL, NULL);
|
||||
|
||||
obj = yaffsfs_FollowLink(obj, symDepth++, &loop);
|
||||
|
||||
if (obj &&
|
||||
obj->variant_type != YAFFS_OBJECT_TYPE_FILE &&
|
||||
obj->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY)
|
||||
obj = NULL;
|
||||
|
||||
if (obj) {
|
||||
|
||||
/* The file already exists or it might be a directory */
|
||||
is_dir = (obj->variant_type ==
|
||||
YAFFS_OBJECT_TYPE_DIRECTORY);
|
||||
|
||||
/* A directory can't be opened except for read */
|
||||
if ( is_dir &&
|
||||
(writeRequested || !readRequested ||
|
||||
(oflag & ~O_RDONLY))) {
|
||||
openDenied = 1;
|
||||
yaffsfs_SetError(-EISDIR);
|
||||
errorReported = 1;
|
||||
}
|
||||
|
||||
if(is_dir) {
|
||||
dsc = yaffsfs_opendir_reldir_no_lock(reldir, path);
|
||||
if (!dsc) {
|
||||
openDenied = 1;
|
||||
yaffsfs_SetError(-ENFILE);
|
||||
errorReported = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Open should fail if O_CREAT and O_EXCL are specified
|
||||
* for a file that exists.
|
||||
*/
|
||||
if (!errorReported &&
|
||||
(oflag & O_EXCL) && (oflag & O_CREAT)) {
|
||||
openDenied = 1;
|
||||
yaffsfs_SetError(-EEXIST);
|
||||
errorReported = 1;
|
||||
}
|
||||
|
||||
/* Check file permissions */
|
||||
if (readRequested && !(obj->yst_mode & S_IREAD))
|
||||
openDenied = 1;
|
||||
|
||||
if (writeRequested && !(obj->yst_mode & S_IWRITE))
|
||||
openDenied = 1;
|
||||
|
||||
if (!errorReported && writeRequested &&
|
||||
obj->my_dev->read_only) {
|
||||
openDenied = 1;
|
||||
yaffsfs_SetError(-EROFS);
|
||||
errorReported = 1;
|
||||
}
|
||||
|
||||
if (openDenied && !errorReported) {
|
||||
yaffsfs_SetError(-EACCES);
|
||||
errorReported = 1;
|
||||
}
|
||||
|
||||
/* Check sharing of an existing object. */
|
||||
if (!openDenied) {
|
||||
struct yaffsfs_FileDes *fdx;
|
||||
int i;
|
||||
|
||||
sharedReadAllowed = 1;
|
||||
sharedWriteAllowed = 1;
|
||||
alreadyReading = 0;
|
||||
alreadyWriting = 0;
|
||||
for (i = 0; i < YAFFSFS_N_HANDLES; i++) {
|
||||
fdx = &yaffsfs_fd[i];
|
||||
if (fdx->handleCount > 0 &&
|
||||
fdx->inodeId >= 0 &&
|
||||
yaffsfs_inode[fdx->inodeId].iObj
|
||||
== obj) {
|
||||
if (!fdx->shareRead)
|
||||
sharedReadAllowed = 0;
|
||||
if (!fdx->shareWrite)
|
||||
sharedWriteAllowed = 0;
|
||||
if (fdx->reading)
|
||||
alreadyReading = 1;
|
||||
if (fdx->writing)
|
||||
alreadyWriting = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ((!sharedReadAllowed && readRequested) ||
|
||||
(!shareRead && alreadyReading) ||
|
||||
(!sharedWriteAllowed && writeRequested) ||
|
||||
(!shareWrite && alreadyWriting)) {
|
||||
openDenied = 1;
|
||||
yaffsfs_SetError(-EBUSY);
|
||||
errorReported = 1;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* If we could not open an existing object, then let's see if
|
||||
* the directory exists. If not, error.
|
||||
*/
|
||||
if (!obj && !errorReported) {
|
||||
dir = yaffsfs_FindDirectory(reldir, path, &name, 0,
|
||||
¬Dir, &loop);
|
||||
if (!dir && notDir) {
|
||||
yaffsfs_SetError(-ENOTDIR);
|
||||
errorReported = 1;
|
||||
} else if (loop) {
|
||||
yaffsfs_SetError(-ELOOP);
|
||||
errorReported = 1;
|
||||
} else if (!dir) {
|
||||
yaffsfs_SetError(-ENOENT);
|
||||
errorReported = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!obj && dir && !errorReported && (oflag & O_CREAT)) {
|
||||
/* Let's see if we can create this file */
|
||||
if (dir->my_dev->read_only) {
|
||||
yaffsfs_SetError(-EROFS);
|
||||
errorReported = 1;
|
||||
} else if (yaffsfs_TooManyObjects(dir->my_dev)) {
|
||||
yaffsfs_SetError(-ENFILE);
|
||||
errorReported = 1;
|
||||
} else
|
||||
obj = yaffs_create_file(dir, name, mode, 0, 0);
|
||||
|
||||
if (!obj && !errorReported) {
|
||||
yaffsfs_SetError(-ENOSPC);
|
||||
errorReported = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!obj && dir && !errorReported && !(oflag & O_CREAT)) {
|
||||
yaffsfs_SetError(-ENOENT);
|
||||
errorReported = 1;
|
||||
}
|
||||
|
||||
if (obj && !openDenied) {
|
||||
int inodeId = yaffsfs_GetInodeIdForObject(obj);
|
||||
|
||||
if (inodeId < 0) {
|
||||
/*
|
||||
* Todo: Fix any problem if inodes run out,
|
||||
* That can't happen if the number of inode
|
||||
* items >= number of handles.
|
||||
*/
|
||||
}
|
||||
|
||||
fd->inodeId = inodeId;
|
||||
fd->reading = readRequested;
|
||||
fd->writing = writeRequested;
|
||||
fd->append = (oflag & O_APPEND) ? 1 : 0;
|
||||
fd->shareRead = shareRead;
|
||||
fd->shareWrite = shareWrite;
|
||||
fd->isDir = is_dir;
|
||||
|
||||
if(is_dir)
|
||||
fd->v.dir = dsc;
|
||||
else
|
||||
fd->v.position = 0;
|
||||
|
||||
/* Hook inode to object */
|
||||
obj->my_inode = (void *)&yaffsfs_inode[inodeId];
|
||||
|
||||
if (!is_dir && (oflag & O_TRUNC) && fd->writing)
|
||||
yaffs_resize_file(obj, 0);
|
||||
} else {
|
||||
yaffsfs_PutHandle(handle);
|
||||
if (!errorReported)
|
||||
yaffsfs_SetError(0); /* Problem */
|
||||
handle = -1;
|
||||
}
|
||||
}
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
int yaffs_open_sharing_reldev(struct yaffs_dev *dev, const YCHAR *path, int oflag,
|
||||
int mode, int sharing)
|
||||
{
|
||||
return yaffs_open_sharing_reldir(ROOT_DIR(dev), path,
|
||||
oflag, mode, sharing);
|
||||
}
|
||||
|
||||
int yaffs_open_sharing(const YCHAR *path, int oflag, int mode, int sharing)
|
||||
{
|
||||
return yaffs_open_sharing_reldir(NULL, path, oflag, mode, sharing);
|
||||
}
|
||||
|
||||
int yaffs_open_reldir(struct yaffs_obj *reldir,const YCHAR *path, int oflag, int mode)
|
||||
{
|
||||
return yaffs_open_sharing_reldir(reldir, path, oflag, mode,
|
||||
YAFFS_SHARE_READ | YAFFS_SHARE_WRITE);
|
||||
}
|
||||
|
||||
int yaffs_open_reldev(struct yaffs_dev *dev,const YCHAR *path, int oflag, int mode)
|
||||
{
|
||||
return yaffs_open_sharing_reldir(ROOT_DIR(dev), path, oflag, mode,
|
||||
YAFFS_SHARE_READ | YAFFS_SHARE_WRITE);
|
||||
}
|
||||
|
||||
int yaffs_open(const YCHAR *path, int oflag, int mode)
|
||||
{
|
||||
return yaffs_open_reldir(NULL, path, oflag, mode);
|
||||
}
|
||||
|
||||
static int yaffs_Dofsync(int handle, int datasync)
|
||||
{
|
||||
int retVal = -1;
|
||||
struct yaffs_obj *obj;
|
||||
|
||||
yaffsfs_Lock();
|
||||
|
||||
obj = yaffsfs_HandleToObject(handle);
|
||||
|
||||
if (!obj)
|
||||
yaffsfs_SetError(-EBADF);
|
||||
else if (obj->my_dev->read_only)
|
||||
yaffsfs_SetError(-EROFS);
|
||||
else {
|
||||
yaffs_flush_file(obj, 1, datasync, 0);
|
||||
retVal = 0;
|
||||
}
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
int yaffs_fsync(int handle)
|
||||
{
|
||||
return yaffs_Dofsync(handle, 0);
|
||||
}
|
||||
|
||||
int yaffs_flush(int handle)
|
||||
{
|
||||
return yaffs_fsync(handle);
|
||||
}
|
||||
|
||||
int yaffs_fdatasync(int handle)
|
||||
{
|
||||
return yaffs_Dofsync(handle, 1);
|
||||
}
|
||||
|
||||
int yaffs_close(int handle)
|
||||
{
|
||||
struct yaffsfs_Handle *h = NULL;
|
||||
struct yaffsfs_FileDes *f;
|
||||
struct yaffs_obj *obj = NULL;
|
||||
int retVal = -1;
|
||||
|
||||
yaffsfs_Lock();
|
||||
|
||||
h = yaffsfs_HandleToPointer(handle);
|
||||
f = yaffsfs_HandleToFileDes(handle);
|
||||
obj = yaffsfs_HandleToObject(handle);
|
||||
|
||||
if (!h || !obj || !f)
|
||||
yaffsfs_SetError(-EBADF);
|
||||
else {
|
||||
/* clean up */
|
||||
if(!f->isDir)
|
||||
yaffs_flush_file(obj, 1, 0, 1);
|
||||
yaffsfs_PutHandle(handle);
|
||||
retVal = 0;
|
||||
}
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
static int yaffsfs_do_read(int handle, void *vbuf, unsigned int nbyte,
|
||||
int isPread, Y_LOFF_T offset)
|
||||
{
|
||||
struct yaffsfs_FileDes *fd = NULL;
|
||||
struct yaffs_obj *obj = NULL;
|
||||
Y_LOFF_T pos = 0;
|
||||
Y_LOFF_T startPos = 0;
|
||||
Y_LOFF_T endPos = 0;
|
||||
int nRead = 0;
|
||||
int nToRead = 0;
|
||||
int totalRead = 0;
|
||||
Y_LOFF_T maxRead;
|
||||
u8 *buf = (u8 *) vbuf;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(vbuf, nbyte, 1) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
fd = yaffsfs_HandleToFileDes(handle);
|
||||
obj = yaffsfs_HandleToObject(handle);
|
||||
|
||||
if (!fd || !obj) {
|
||||
/* bad handle */
|
||||
yaffsfs_SetError(-EBADF);
|
||||
totalRead = -1;
|
||||
} else if (!fd->reading) {
|
||||
/* Not a reading handle */
|
||||
yaffsfs_SetError(-EINVAL);
|
||||
totalRead = -1;
|
||||
}
|
||||
else if (nbyte > YAFFS_MAX_FILE_SIZE) {
|
||||
yaffsfs_SetError(-EINVAL);
|
||||
totalRead = -1;
|
||||
}
|
||||
else {
|
||||
if (isPread)
|
||||
startPos = offset;
|
||||
else
|
||||
startPos = fd->v.position;
|
||||
|
||||
pos = startPos;
|
||||
|
||||
if (yaffs_get_obj_length(obj) > pos)
|
||||
maxRead = yaffs_get_obj_length(obj) - pos;
|
||||
else
|
||||
maxRead = 0;
|
||||
|
||||
if ((s32)nbyte > maxRead)
|
||||
nbyte = maxRead;
|
||||
|
||||
yaffsfs_GetHandle(handle);
|
||||
|
||||
endPos = pos + nbyte;
|
||||
if (pos < 0 || pos > YAFFS_MAX_FILE_SIZE ||
|
||||
nbyte > YAFFS_MAX_FILE_SIZE ||
|
||||
endPos < 0 ||
|
||||
endPos > YAFFS_MAX_FILE_SIZE) {
|
||||
totalRead = -1;
|
||||
nbyte = 0;
|
||||
}
|
||||
|
||||
while (nbyte > 0) {
|
||||
nToRead = YAFFSFS_RW_SIZE -
|
||||
(pos & (YAFFSFS_RW_SIZE - 1));
|
||||
if (nToRead > (s32)nbyte)
|
||||
nToRead = nbyte;
|
||||
|
||||
/* Tricky bit...
|
||||
* Need to reverify object in case the device was
|
||||
* unmounted in another thread.
|
||||
*/
|
||||
obj = yaffsfs_HandleToObject(handle);
|
||||
if (!obj)
|
||||
nRead = 0;
|
||||
else
|
||||
nRead = yaffs_file_rd(obj, buf, pos, nToRead);
|
||||
|
||||
if (nRead > 0) {
|
||||
totalRead += nRead;
|
||||
pos += nRead;
|
||||
buf += nRead;
|
||||
}
|
||||
|
||||
if (nRead == nToRead)
|
||||
nbyte -= nRead;
|
||||
else
|
||||
nbyte = 0; /* no more to read */
|
||||
|
||||
if (nbyte > 0) {
|
||||
yaffsfs_Unlock();
|
||||
yaffsfs_Lock();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
yaffsfs_PutHandle(handle);
|
||||
|
||||
if (!isPread) {
|
||||
if (totalRead >= 0)
|
||||
fd->v.position = startPos + totalRead;
|
||||
else
|
||||
yaffsfs_SetError(-EINVAL);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return (totalRead >= 0) ? totalRead : -1;
|
||||
|
||||
}
|
||||
|
||||
int yaffs_read(int handle, void *buf, unsigned int nbyte)
|
||||
{
|
||||
return yaffsfs_do_read(handle, buf, nbyte, 0, 0);
|
||||
}
|
||||
|
||||
int yaffs_pread(int handle, void *buf, unsigned int nbyte, Y_LOFF_T offset)
|
||||
{
|
||||
return yaffsfs_do_read(handle, buf, nbyte, 1, offset);
|
||||
}
|
||||
|
||||
static int yaffsfs_do_write(int handle, const void *vbuf, unsigned int nbyte,
|
||||
int isPwrite, Y_LOFF_T offset)
|
||||
{
|
||||
struct yaffsfs_FileDes *fd = NULL;
|
||||
struct yaffs_obj *obj = NULL;
|
||||
Y_LOFF_T pos = 0;
|
||||
Y_LOFF_T startPos = 0;
|
||||
Y_LOFF_T endPos;
|
||||
int nWritten = 0;
|
||||
int totalWritten = 0;
|
||||
int write_trhrough = 0;
|
||||
int nToWrite = 0;
|
||||
const u8 *buf = (const u8 *)vbuf;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(vbuf, nbyte, 0) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
fd = yaffsfs_HandleToFileDes(handle);
|
||||
obj = yaffsfs_HandleToObject(handle);
|
||||
|
||||
if (!fd || !obj) {
|
||||
/* bad handle */
|
||||
yaffsfs_SetError(-EBADF);
|
||||
totalWritten = -1;
|
||||
} else if (!fd->writing) {
|
||||
yaffsfs_SetError(-EINVAL);
|
||||
totalWritten = -1;
|
||||
} else if (obj->my_dev->read_only) {
|
||||
yaffsfs_SetError(-EROFS);
|
||||
totalWritten = -1;
|
||||
} else {
|
||||
if (fd->append)
|
||||
startPos = yaffs_get_obj_length(obj);
|
||||
else if (isPwrite)
|
||||
startPos = offset;
|
||||
else
|
||||
startPos = fd->v.position;
|
||||
|
||||
yaffsfs_GetHandle(handle);
|
||||
pos = startPos;
|
||||
endPos = pos + nbyte;
|
||||
|
||||
if (pos < 0 || pos > YAFFS_MAX_FILE_SIZE ||
|
||||
nbyte > YAFFS_MAX_FILE_SIZE ||
|
||||
endPos < 0 ||
|
||||
endPos > YAFFS_MAX_FILE_SIZE) {
|
||||
totalWritten = -1;
|
||||
nbyte = 0;
|
||||
}
|
||||
|
||||
while (nbyte > 0) {
|
||||
|
||||
nToWrite = YAFFSFS_RW_SIZE -
|
||||
(pos & (YAFFSFS_RW_SIZE - 1));
|
||||
if (nToWrite > (s32)nbyte)
|
||||
nToWrite = nbyte;
|
||||
|
||||
/* Tricky bit...
|
||||
* Need to reverify object in case the device was
|
||||
* remounted or unmounted in another thread.
|
||||
*/
|
||||
obj = yaffsfs_HandleToObject(handle);
|
||||
if (!obj || obj->my_dev->read_only)
|
||||
nWritten = 0;
|
||||
else
|
||||
nWritten =
|
||||
yaffs_wr_file(obj, buf, pos, nToWrite,
|
||||
write_trhrough);
|
||||
if (nWritten > 0) {
|
||||
totalWritten += nWritten;
|
||||
pos += nWritten;
|
||||
buf += nWritten;
|
||||
}
|
||||
|
||||
if (nWritten == nToWrite)
|
||||
nbyte -= nToWrite;
|
||||
else
|
||||
nbyte = 0;
|
||||
|
||||
if (nWritten < 1 && totalWritten < 1) {
|
||||
yaffsfs_SetError(-ENOSPC);
|
||||
totalWritten = -1;
|
||||
}
|
||||
|
||||
if (nbyte > 0) {
|
||||
yaffsfs_Unlock();
|
||||
yaffsfs_Lock();
|
||||
}
|
||||
}
|
||||
|
||||
yaffsfs_PutHandle(handle);
|
||||
|
||||
if (!isPwrite) {
|
||||
if (totalWritten > 0)
|
||||
fd->v.position = startPos + totalWritten;
|
||||
else
|
||||
yaffsfs_SetError(-EINVAL);
|
||||
}
|
||||
}
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return (totalWritten >= 0) ? totalWritten : -1;
|
||||
}
|
||||
|
||||
int yaffs_write(int fd, const void *buf, unsigned int nbyte)
|
||||
{
|
||||
return yaffsfs_do_write(fd, buf, nbyte, 0, 0);
|
||||
}
|
||||
|
||||
int yaffs_pwrite(int fd, const void *buf, unsigned int nbyte, Y_LOFF_T offset)
|
||||
{
|
||||
return yaffsfs_do_write(fd, buf, nbyte, 1, offset);
|
||||
}
|
||||
|
||||
int yaffs_truncate_reldir(struct yaffs_obj *reldir, const YCHAR *path,
|
||||
Y_LOFF_T new_size)
|
||||
{
|
||||
struct yaffs_obj *obj = NULL;
|
||||
struct yaffs_obj *dir = NULL;
|
||||
int result = YAFFS_FAIL;
|
||||
int notDir = 0;
|
||||
int loop = 0;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(path, 0, 0) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_CheckPath(path) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return -1;
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
|
||||
obj = yaffsfs_FindObject(reldir, path, 0, 1, &dir, ¬Dir, &loop);
|
||||
obj = yaffsfs_FollowLink(obj, 0, &loop);
|
||||
|
||||
if (!dir && notDir)
|
||||
yaffsfs_SetError(-ENOTDIR);
|
||||
else if (loop)
|
||||
yaffsfs_SetError(-ELOOP);
|
||||
else if (!dir || !obj)
|
||||
yaffsfs_SetError(-ENOENT);
|
||||
else if (obj->my_dev->read_only)
|
||||
yaffsfs_SetError(-EROFS);
|
||||
else if (obj->variant_type != YAFFS_OBJECT_TYPE_FILE)
|
||||
yaffsfs_SetError(-EISDIR);
|
||||
else if (obj->my_dev->read_only)
|
||||
yaffsfs_SetError(-EROFS);
|
||||
else if (new_size < 0 || new_size > YAFFS_MAX_FILE_SIZE)
|
||||
yaffsfs_SetError(-EINVAL);
|
||||
else
|
||||
result = yaffs_resize_file(obj, new_size);
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return (result) ? 0 : -1;
|
||||
}
|
||||
|
||||
int yaffs_truncate_reldev(struct yaffs_dev *dev, const YCHAR *path,
|
||||
Y_LOFF_T new_size)
|
||||
{
|
||||
return yaffs_truncate_reldir(ROOT_DIR(dev), path, new_size);
|
||||
}
|
||||
|
||||
int yaffs_truncate(const YCHAR *path, Y_LOFF_T new_size)
|
||||
{
|
||||
return yaffs_truncate_reldir(NULL, path, new_size);
|
||||
}
|
||||
|
||||
int yaffs_ftruncate(int handle, Y_LOFF_T new_size)
|
||||
{
|
||||
struct yaffsfs_FileDes *fd = NULL;
|
||||
struct yaffs_obj *obj = NULL;
|
||||
int result = 0;
|
||||
|
||||
yaffsfs_Lock();
|
||||
fd = yaffsfs_HandleToFileDes(handle);
|
||||
obj = yaffsfs_HandleToObject(handle);
|
||||
|
||||
if (!fd || !obj)
|
||||
/* bad handle */
|
||||
yaffsfs_SetError(-EBADF);
|
||||
else if (!fd->writing)
|
||||
yaffsfs_SetError(-EINVAL);
|
||||
else if (obj->my_dev->read_only)
|
||||
yaffsfs_SetError(-EROFS);
|
||||
else if (new_size < 0 || new_size > YAFFS_MAX_FILE_SIZE)
|
||||
yaffsfs_SetError(-EINVAL);
|
||||
else
|
||||
/* resize the file */
|
||||
result = yaffs_resize_file(obj, new_size);
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return (result) ? 0 : -1;
|
||||
|
||||
}
|
||||
|
||||
Y_LOFF_T yaffs_lseek(int handle, Y_LOFF_T offset, int whence)
|
||||
{
|
||||
struct yaffsfs_FileDes *fd = NULL;
|
||||
struct yaffs_obj *obj = NULL;
|
||||
Y_LOFF_T pos = -1;
|
||||
Y_LOFF_T fSize = -1;
|
||||
|
||||
yaffsfs_Lock();
|
||||
fd = yaffsfs_HandleToFileDes(handle);
|
||||
obj = yaffsfs_HandleToObject(handle);
|
||||
|
||||
if (!fd || !obj)
|
||||
yaffsfs_SetError(-EBADF);
|
||||
else if (offset > YAFFS_MAX_FILE_SIZE)
|
||||
yaffsfs_SetError(-EINVAL);
|
||||
else {
|
||||
if (whence == SEEK_SET) {
|
||||
if (offset >= 0)
|
||||
pos = offset;
|
||||
} else if (whence == SEEK_CUR) {
|
||||
if ((fd->v.position + offset) >= 0)
|
||||
pos = (fd->v.position + offset);
|
||||
} else if (whence == SEEK_END) {
|
||||
fSize = yaffs_get_obj_length(obj);
|
||||
if (fSize >= 0 && (fSize + offset) >= 0)
|
||||
pos = fSize + offset;
|
||||
}
|
||||
|
||||
if (pos >= 0 && pos <= YAFFS_MAX_FILE_SIZE)
|
||||
fd->v.position = pos;
|
||||
else {
|
||||
yaffsfs_SetError(-EINVAL);
|
||||
pos = -1;
|
||||
}
|
||||
}
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
static int yaffsfs_DoUnlink_reldir(struct yaffs_obj *reldir,
|
||||
const YCHAR *path, int isDirectory)
|
||||
{
|
||||
struct yaffs_obj *dir = NULL;
|
||||
struct yaffs_obj *obj = NULL;
|
||||
YCHAR *name;
|
||||
int result = YAFFS_FAIL;
|
||||
int notDir = 0;
|
||||
int loop = 0;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(path, 0, 0) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_CheckPath(path) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return -1;
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
|
||||
obj = yaffsfs_FindObject(reldir, path, 0, 0, NULL, NULL, NULL);
|
||||
dir = yaffsfs_FindDirectory(reldir, path, &name, 0, ¬Dir, &loop);
|
||||
|
||||
if (!dir && notDir)
|
||||
yaffsfs_SetError(-ENOTDIR);
|
||||
else if (loop)
|
||||
yaffsfs_SetError(-ELOOP);
|
||||
else if (!dir)
|
||||
yaffsfs_SetError(-ENOENT);
|
||||
else if (yaffs_strncmp(name, _Y("."), 2) == 0)
|
||||
yaffsfs_SetError(-EINVAL);
|
||||
else if (!obj)
|
||||
yaffsfs_SetError(-ENOENT);
|
||||
else if (obj->my_dev->read_only)
|
||||
yaffsfs_SetError(-EROFS);
|
||||
else if (!isDirectory &&
|
||||
obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY)
|
||||
yaffsfs_SetError(-EISDIR);
|
||||
else if (isDirectory &&
|
||||
obj->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY)
|
||||
yaffsfs_SetError(-ENOTDIR);
|
||||
else if (isDirectory && obj == obj->my_dev->root_dir)
|
||||
yaffsfs_SetError(-EBUSY); /* Can't rmdir a root */
|
||||
else {
|
||||
result = yaffs_unlinker(dir, name);
|
||||
|
||||
if (result == YAFFS_FAIL && isDirectory)
|
||||
yaffsfs_SetError(-ENOTEMPTY);
|
||||
}
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return (result == YAFFS_FAIL) ? -1 : 0;
|
||||
}
|
||||
|
||||
int yaffs_unlink_reldir(struct yaffs_obj *reldir, const YCHAR *path)
|
||||
{
|
||||
return yaffsfs_DoUnlink_reldir(reldir, path, 0);
|
||||
}
|
||||
|
||||
int yaffs_unlink_reldev(struct yaffs_dev *dev, const YCHAR *path)
|
||||
{
|
||||
return yaffsfs_DoUnlink_reldir(ROOT_DIR(dev), path, 0);
|
||||
}
|
||||
|
||||
int yaffs_unlink(const YCHAR *path)
|
||||
{
|
||||
return yaffs_unlink_reldir(NULL, path);
|
||||
}
|
||||
|
||||
static int rename_file_over_dir(struct yaffs_obj *obj, struct yaffs_obj *newobj)
|
||||
{
|
||||
if (obj && obj->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY &&
|
||||
newobj && newobj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rename_dir_over_file(struct yaffs_obj *obj, struct yaffs_obj *newobj)
|
||||
{
|
||||
if (obj && obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY &&
|
||||
newobj && newobj->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
int yaffs_rename_reldir(struct yaffs_obj *reldir,
|
||||
const YCHAR *oldPath, const YCHAR *newPath)
|
||||
{
|
||||
struct yaffs_obj *olddir = NULL;
|
||||
struct yaffs_obj *newdir = NULL;
|
||||
struct yaffs_obj *obj = NULL;
|
||||
struct yaffs_obj *newobj = NULL;
|
||||
YCHAR *oldname;
|
||||
YCHAR *newname;
|
||||
int result = YAFFS_FAIL;
|
||||
int rename_allowed = 1;
|
||||
int notOldDir = 0;
|
||||
int notNewDir = 0;
|
||||
int oldLoop = 0;
|
||||
int newLoop = 0;
|
||||
|
||||
YCHAR *alt_newpath = NULL;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(oldPath, 0, 0) < 0 ||
|
||||
yaffsfs_CheckMemRegion(newPath, 0, 0) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_CheckPath(oldPath) < 0 || yaffsfs_CheckPath(newPath) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_alt_dir_path(newPath, &alt_newpath) < 0) {
|
||||
yaffsfs_SetError(-ENOMEM);
|
||||
return -1;
|
||||
}
|
||||
if (alt_newpath)
|
||||
newPath = alt_newpath;
|
||||
|
||||
yaffsfs_Lock();
|
||||
|
||||
olddir = yaffsfs_FindDirectory(reldir, oldPath, &oldname, 0,
|
||||
¬OldDir, &oldLoop);
|
||||
newdir = yaffsfs_FindDirectory(reldir, newPath, &newname, 0,
|
||||
¬NewDir, &newLoop);
|
||||
obj = yaffsfs_FindObject(reldir, oldPath, 0, 0, NULL, NULL, NULL);
|
||||
newobj = yaffsfs_FindObject(reldir, newPath, 0, 0, NULL, NULL, NULL);
|
||||
|
||||
/* If the object being renamed is a directory and the
|
||||
* path ended with a "/" then the olddir == obj.
|
||||
* We pass through NULL for the old name to tell the lower layers
|
||||
* to use olddir as the object.
|
||||
*/
|
||||
|
||||
if (olddir == obj)
|
||||
oldname = NULL;
|
||||
|
||||
if ((!olddir && notOldDir) || (!newdir && notNewDir)) {
|
||||
yaffsfs_SetError(-ENOTDIR);
|
||||
rename_allowed = 0;
|
||||
} else if (oldLoop || newLoop) {
|
||||
yaffsfs_SetError(-ELOOP);
|
||||
rename_allowed = 0;
|
||||
} else if (olddir && oldname &&
|
||||
yaffs_strncmp(oldname, _Y("."), 2) == 0) {
|
||||
yaffsfs_SetError(-EINVAL);
|
||||
rename_allowed = 0;
|
||||
} else if (!olddir || !newdir || !obj) {
|
||||
yaffsfs_SetError(-ENOENT);
|
||||
rename_allowed = 0;
|
||||
} else if (obj->my_dev->read_only) {
|
||||
yaffsfs_SetError(-EROFS);
|
||||
rename_allowed = 0;
|
||||
} else if (rename_file_over_dir(obj, newobj)) {
|
||||
yaffsfs_SetError(-EISDIR);
|
||||
rename_allowed = 0;
|
||||
} else if (rename_dir_over_file(obj, newobj)) {
|
||||
yaffsfs_SetError(-ENOTDIR);
|
||||
rename_allowed = 0;
|
||||
} else if (yaffs_is_non_empty_dir(newobj)) {
|
||||
yaffsfs_SetError(-ENOTEMPTY);
|
||||
rename_allowed = 0;
|
||||
} else if (olddir->my_dev != newdir->my_dev) {
|
||||
/* Rename must be on same device */
|
||||
yaffsfs_SetError(-EXDEV);
|
||||
rename_allowed = 0;
|
||||
} else if (obj && obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) {
|
||||
/*
|
||||
* It is a directory, check that it is not being renamed to
|
||||
* being its own decendent.
|
||||
* Do this by tracing from the new directory back to the root,
|
||||
* checking for obj
|
||||
*/
|
||||
|
||||
struct yaffs_obj *xx = newdir;
|
||||
|
||||
while (rename_allowed && xx) {
|
||||
if (xx == obj)
|
||||
rename_allowed = 0;
|
||||
xx = xx->parent;
|
||||
}
|
||||
if (!rename_allowed)
|
||||
yaffsfs_SetError(-EINVAL);
|
||||
}
|
||||
|
||||
if (rename_allowed)
|
||||
result = yaffs_rename_obj(olddir, oldname, newdir, newname);
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
kfree(alt_newpath);
|
||||
|
||||
return (result == YAFFS_FAIL) ? -1 : 0;
|
||||
}
|
||||
|
||||
int yaffs_rename_reldev(struct yaffs_dev *dev, const YCHAR *oldPath,
|
||||
const YCHAR *newPath)
|
||||
{
|
||||
return yaffs_rename_reldir(ROOT_DIR(dev), oldPath, newPath);
|
||||
}
|
||||
int yaffs_rename(const YCHAR *oldPath, const YCHAR *newPath)
|
||||
{
|
||||
return yaffs_rename_reldir(NULL, oldPath, newPath);
|
||||
}
|
||||
|
||||
static int yaffsfs_DoStat(struct yaffs_obj *obj, struct yaffs_stat *buf)
|
||||
{
|
||||
int retVal = -1;
|
||||
|
||||
obj = yaffs_get_equivalent_obj(obj);
|
||||
|
||||
if (obj && buf) {
|
||||
buf->st_dev = (int)obj->my_dev->os_context;
|
||||
buf->st_ino = obj->obj_id;
|
||||
buf->st_mode = obj->yst_mode & ~S_IFMT;
|
||||
|
||||
if (obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY)
|
||||
buf->st_mode |= S_IFDIR;
|
||||
else if (obj->variant_type == YAFFS_OBJECT_TYPE_SYMLINK)
|
||||
buf->st_mode |= S_IFLNK;
|
||||
else if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE)
|
||||
buf->st_mode |= S_IFREG;
|
||||
|
||||
buf->st_nlink = yaffs_get_obj_link_count(obj);
|
||||
buf->st_uid = 0;
|
||||
buf->st_gid = 0;
|
||||
buf->st_rdev = obj->yst_rdev;
|
||||
buf->st_size = yaffs_get_obj_length(obj);
|
||||
buf->st_blksize = obj->my_dev->data_bytes_per_chunk;
|
||||
buf->st_blocks = (buf->st_size + buf->st_blksize - 1) /
|
||||
buf->st_blksize;
|
||||
#if CONFIG_YAFFS_WINCE
|
||||
buf->yst_wince_atime[0] = obj->win_atime[0];
|
||||
buf->yst_wince_atime[1] = obj->win_atime[1];
|
||||
buf->yst_wince_ctime[0] = obj->win_ctime[0];
|
||||
buf->yst_wince_ctime[1] = obj->win_ctime[1];
|
||||
buf->yst_wince_mtime[0] = obj->win_mtime[0];
|
||||
buf->yst_wince_mtime[1] = obj->win_mtime[1];
|
||||
#else
|
||||
buf->yst_atime = obj->yst_atime;
|
||||
buf->yst_ctime = obj->yst_ctime;
|
||||
buf->yst_mtime = obj->yst_mtime;
|
||||
#endif
|
||||
retVal = 0;
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
static int yaffsfs_DoStatOrLStat_reldir(struct yaffs_obj *reldir, const YCHAR *path,
|
||||
struct yaffs_stat *buf, int doLStat)
|
||||
{
|
||||
struct yaffs_obj *obj = NULL;
|
||||
struct yaffs_obj *dir = NULL;
|
||||
int retVal = -1;
|
||||
int notDir = 0;
|
||||
int loop = 0;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(path, 0, 0) < 0 ||
|
||||
yaffsfs_CheckMemRegion(buf, sizeof(*buf), 1) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_CheckPath(path) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return -1;
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
|
||||
obj = yaffsfs_FindObject(reldir, path, 0, 1, &dir, ¬Dir, &loop);
|
||||
|
||||
if (!doLStat && obj)
|
||||
obj = yaffsfs_FollowLink(obj, 0, &loop);
|
||||
|
||||
if (!dir && notDir)
|
||||
yaffsfs_SetError(-ENOTDIR);
|
||||
else if (loop)
|
||||
yaffsfs_SetError(-ELOOP);
|
||||
else if (!dir || !obj)
|
||||
yaffsfs_SetError(-ENOENT);
|
||||
else
|
||||
retVal = yaffsfs_DoStat(obj, buf);
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return retVal;
|
||||
|
||||
}
|
||||
|
||||
int yaffs_stat_reldir(struct yaffs_obj *reldir, const YCHAR *path,
|
||||
struct yaffs_stat *buf)
|
||||
{
|
||||
return yaffsfs_DoStatOrLStat_reldir(reldir, path, buf, 0);
|
||||
}
|
||||
|
||||
int yaffs_lstat_reldir(struct yaffs_obj *reldir, const YCHAR *path,
|
||||
struct yaffs_stat *buf)
|
||||
{
|
||||
return yaffsfs_DoStatOrLStat_reldir(reldir, path, buf, 1);
|
||||
}
|
||||
|
||||
int yaffs_stat_reldev(struct yaffs_dev *dev, const YCHAR *path,
|
||||
struct yaffs_stat *buf)
|
||||
{
|
||||
return yaffsfs_DoStatOrLStat_reldir(ROOT_DIR(dev), path, buf, 0);
|
||||
}
|
||||
|
||||
int yaffs_lstat_reldev(struct yaffs_dev *dev, const YCHAR *path,
|
||||
struct yaffs_stat *buf)
|
||||
{
|
||||
return yaffsfs_DoStatOrLStat_reldir(ROOT_DIR(dev), path, buf, 1);
|
||||
}
|
||||
|
||||
int yaffs_stat(const YCHAR *path, struct yaffs_stat *buf)
|
||||
{
|
||||
return yaffs_stat_reldir(NULL, path, buf);
|
||||
}
|
||||
|
||||
int yaffs_lstat(const YCHAR *path, struct yaffs_stat *buf)
|
||||
{
|
||||
return yaffs_lstat_reldir(NULL, path, buf);
|
||||
}
|
||||
|
||||
int yaffs_fstat(int fd, struct yaffs_stat *buf)
|
||||
{
|
||||
struct yaffs_obj *obj;
|
||||
|
||||
int retVal = -1;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(buf, sizeof(*buf), 1) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
obj = yaffsfs_HandleToObject(fd);
|
||||
|
||||
if (obj)
|
||||
retVal = yaffsfs_DoStat(obj, buf);
|
||||
else
|
||||
/* bad handle */
|
||||
yaffsfs_SetError(-EBADF);
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
static int yaffsfs_DoUtime(struct yaffs_obj *obj,
|
||||
const struct yaffs_utimbuf *buf)
|
||||
{
|
||||
int retVal = -1;
|
||||
struct yaffs_utimbuf local;
|
||||
|
||||
obj = yaffs_get_equivalent_obj(obj);
|
||||
|
||||
if (obj && obj->my_dev->read_only) {
|
||||
yaffsfs_SetError(-EROFS);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if !CONFIG_YAFFS_WINCE
|
||||
if (!buf) {
|
||||
local.actime = Y_CURRENT_TIME;
|
||||
local.modtime = local.actime;
|
||||
buf = &local;
|
||||
}
|
||||
|
||||
if (obj) {
|
||||
int result;
|
||||
|
||||
obj->yst_atime = buf->actime;
|
||||
obj->yst_mtime = buf->modtime;
|
||||
obj->dirty = 1;
|
||||
result = yaffs_flush_file(obj, 0, 0, 0);
|
||||
retVal = result == YAFFS_OK ? 0 : -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
int yaffs_utime_reldir(struct yaffs_obj *reldir, const YCHAR *path,
|
||||
const struct yaffs_utimbuf *buf)
|
||||
{
|
||||
struct yaffs_obj *obj = NULL;
|
||||
struct yaffs_obj *dir = NULL;
|
||||
int retVal = -1;
|
||||
int notDir = 0;
|
||||
int loop = 0;
|
||||
|
||||
if (!path) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_CheckPath(path) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return -1;
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
|
||||
obj = yaffsfs_FindObject(reldir, path, 0, 1, &dir, ¬Dir, &loop);
|
||||
|
||||
if (!dir && notDir)
|
||||
yaffsfs_SetError(-ENOTDIR);
|
||||
else if (loop)
|
||||
yaffsfs_SetError(-ELOOP);
|
||||
else if (!dir || !obj)
|
||||
yaffsfs_SetError(-ENOENT);
|
||||
else
|
||||
retVal = yaffsfs_DoUtime(obj, buf);
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return retVal;
|
||||
|
||||
}
|
||||
|
||||
int yaffs_utime_reldev(struct yaffs_dev *dev, const YCHAR *path,
|
||||
const struct yaffs_utimbuf *buf)
|
||||
{
|
||||
return yaffs_utime_reldir(ROOT_DIR(dev), path, buf);
|
||||
}
|
||||
|
||||
int yaffs_utime(const YCHAR *path, const struct yaffs_utimbuf *buf)
|
||||
{
|
||||
return yaffs_utime_reldir(NULL, path, buf);
|
||||
}
|
||||
|
||||
int yaffs_futime(int fd, const struct yaffs_utimbuf *buf)
|
||||
{
|
||||
struct yaffs_obj *obj;
|
||||
|
||||
int retVal = -1;
|
||||
|
||||
yaffsfs_Lock();
|
||||
obj = yaffsfs_HandleToObject(fd);
|
||||
|
||||
if (obj)
|
||||
retVal = yaffsfs_DoUtime(obj, buf);
|
||||
else
|
||||
/* bad handle */
|
||||
yaffsfs_SetError(-EBADF);
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_YAFFS_WINCE
|
||||
/* xattrib functions */
|
||||
|
||||
static int yaffs_do_setxattr_reldir(struct yaffs_obj *reldir, const YCHAR *path,
|
||||
const char *name, const void *data, int size,
|
||||
int flags, int follow)
|
||||
{
|
||||
struct yaffs_obj *obj;
|
||||
struct yaffs_obj *dir;
|
||||
int notDir = 0;
|
||||
int loop = 0;
|
||||
|
||||
int retVal = -1;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(path, 0, 0) < 0 ||
|
||||
yaffsfs_CheckMemRegion(name, 0, 0) < 0 ||
|
||||
yaffsfs_CheckMemRegion(data, size, 0) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_CheckPath(path) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return -1;
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
|
||||
obj = yaffsfs_FindObject(reldir, path, 0, 1, &dir, ¬Dir, &loop);
|
||||
|
||||
if (follow)
|
||||
obj = yaffsfs_FollowLink(obj, 0, &loop);
|
||||
|
||||
if (!dir && notDir)
|
||||
yaffsfs_SetError(-ENOTDIR);
|
||||
else if (loop)
|
||||
yaffsfs_SetError(-ELOOP);
|
||||
else if (!dir || !obj)
|
||||
yaffsfs_SetError(-ENOENT);
|
||||
else {
|
||||
retVal = yaffs_set_xattrib(obj, name, data, size, flags);
|
||||
if (retVal < 0) {
|
||||
yaffsfs_SetError(retVal);
|
||||
retVal = -1;
|
||||
}
|
||||
}
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return retVal;
|
||||
|
||||
}
|
||||
|
||||
int yaffs_setxattr_reldir(struct yaffs_obj *reldir, const YCHAR *path,
|
||||
const char *name, const void *data, int size, int flags)
|
||||
{
|
||||
return yaffs_do_setxattr_reldir(reldir, path, name, data, size, flags, 1);
|
||||
}
|
||||
|
||||
int yaffs_setxattr_reldev(struct yaffs_dev *dev, const YCHAR *path,
|
||||
const char *name, const void *data, int size, int flags)
|
||||
{
|
||||
return yaffs_setxattr_reldir(ROOT_DIR(dev), path, name, data, size, flags);
|
||||
}
|
||||
|
||||
int yaffs_setxattr(const YCHAR *path, const char *name,
|
||||
const void *data, int size, int flags)
|
||||
{
|
||||
return yaffs_setxattr_reldir(NULL, path, name, data, size, flags);
|
||||
}
|
||||
|
||||
int yaffs_lsetxattr_reldir(struct yaffs_obj *reldir, const YCHAR *path, const char *name,
|
||||
const void *data, int size, int flags)
|
||||
{
|
||||
return yaffs_do_setxattr_reldir(reldir, path, name, data, size, flags, 0);
|
||||
}
|
||||
|
||||
int yaffs_lsetxattr_reldev(struct yaffs_dev *dev, const YCHAR *path, const char *name,
|
||||
const void *data, int size, int flags)
|
||||
{
|
||||
return yaffs_lsetxattr_reldir(ROOT_DIR(dev), path, name, data, size, flags);
|
||||
}
|
||||
|
||||
int yaffs_lsetxattr(const YCHAR *path, const char *name,
|
||||
const void *data, int size, int flags)
|
||||
{
|
||||
return yaffs_lsetxattr_reldir(NULL, path, name, data, size, flags);
|
||||
}
|
||||
|
||||
int yaffs_fsetxattr(int fd, const char *name,
|
||||
const void *data, int size, int flags)
|
||||
{
|
||||
struct yaffs_obj *obj;
|
||||
|
||||
int retVal = -1;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(name, 0, 0) < 0 ||
|
||||
yaffsfs_CheckMemRegion(data, size, 0) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
obj = yaffsfs_HandleToObject(fd);
|
||||
|
||||
if (!obj)
|
||||
yaffsfs_SetError(-EBADF);
|
||||
else {
|
||||
retVal = yaffs_set_xattrib(obj, name, data, size, flags);
|
||||
if (retVal < 0) {
|
||||
yaffsfs_SetError(retVal);
|
||||
retVal = -1;
|
||||
}
|
||||
}
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
static int yaffs_do_getxattr_reldir(struct yaffs_obj *reldir, const YCHAR *path,
|
||||
const char *name, void *data, int size, int follow)
|
||||
{
|
||||
struct yaffs_obj *obj;
|
||||
struct yaffs_obj *dir;
|
||||
int retVal = -1;
|
||||
int notDir = 0;
|
||||
int loop = 0;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(path, 0, 0) < 0 ||
|
||||
yaffsfs_CheckMemRegion(name, 0, 0) < 0 ||
|
||||
yaffsfs_CheckMemRegion(data, size, 1) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_CheckPath(path) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return -1;
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
|
||||
obj = yaffsfs_FindObject(reldir, path, 0, 1, &dir, ¬Dir, &loop);
|
||||
|
||||
if (follow)
|
||||
obj = yaffsfs_FollowLink(obj, 0, &loop);
|
||||
|
||||
if (!dir && notDir)
|
||||
yaffsfs_SetError(-ENOTDIR);
|
||||
else if (loop)
|
||||
yaffsfs_SetError(-ELOOP);
|
||||
else if (!dir || !obj)
|
||||
yaffsfs_SetError(-ENOENT);
|
||||
else {
|
||||
retVal = yaffs_get_xattrib(obj, name, data, size);
|
||||
if (retVal < 0) {
|
||||
yaffsfs_SetError(retVal);
|
||||
retVal = -1;
|
||||
}
|
||||
}
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return retVal;
|
||||
|
||||
}
|
||||
|
||||
int yaffs_getxattr_reldir(struct yaffs_obj *reldir, const YCHAR *path,
|
||||
const char *name, void *data, int size)
|
||||
{
|
||||
return yaffs_do_getxattr_reldir(reldir, path, name, data, size, 1);
|
||||
}
|
||||
|
||||
int yaffs_getxattr_reldev(struct yaffs_dev *dev, const YCHAR *path, const char *name, void *data, int size)
|
||||
{
|
||||
return yaffs_getxattr_reldir(ROOT_DIR(dev), path, name, data, size);
|
||||
}
|
||||
|
||||
int yaffs_getxattr(const YCHAR *path, const char *name, void *data, int size)
|
||||
{
|
||||
return yaffs_getxattr_reldir(NULL, path, name, data, size);
|
||||
}
|
||||
|
||||
int yaffs_lgetxattr_reldir(struct yaffs_obj *reldir, const YCHAR *path,
|
||||
const char *name, void *data, int size)
|
||||
{
|
||||
return yaffs_do_getxattr_reldir(reldir, path, name, data, size, 0);
|
||||
}
|
||||
|
||||
int yaffs_lgetxattr_reldev(struct yaffs_dev *dev, const YCHAR *path, const char *name, void *data, int size)
|
||||
{
|
||||
return yaffs_lgetxattr_reldir(ROOT_DIR(dev), path, name, data, size);
|
||||
}
|
||||
|
||||
int yaffs_lgetxattr(const YCHAR *path, const char *name, void *data, int size)
|
||||
{
|
||||
return yaffs_lgetxattr_reldir(NULL, path, name, data, size);
|
||||
}
|
||||
|
||||
int yaffs_fgetxattr(int fd, const char *name, void *data, int size)
|
||||
{
|
||||
struct yaffs_obj *obj;
|
||||
|
||||
int retVal = -1;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(name, 0, 0) < 0 ||
|
||||
yaffsfs_CheckMemRegion(data, size, 1) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
obj = yaffsfs_HandleToObject(fd);
|
||||
|
||||
if (obj) {
|
||||
retVal = yaffs_get_xattrib(obj, name, data, size);
|
||||
if (retVal < 0) {
|
||||
yaffsfs_SetError(retVal);
|
||||
retVal = -1;
|
||||
}
|
||||
} else
|
||||
/* bad handle */
|
||||
yaffsfs_SetError(-EBADF);
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
static int yaffs_do_listxattr_reldir(struct yaffs_obj *reldir, const YCHAR *path,
|
||||
char *data, int size, int follow)
|
||||
{
|
||||
struct yaffs_obj *obj = NULL;
|
||||
struct yaffs_obj *dir = NULL;
|
||||
int retVal = -1;
|
||||
int notDir = 0;
|
||||
int loop = 0;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(path, 0, 0) < 0 ||
|
||||
yaffsfs_CheckMemRegion(data, size, 1) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_CheckPath(path) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return -1;
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
|
||||
obj = yaffsfs_FindObject(reldir, path, 0, 1, &dir, ¬Dir, &loop);
|
||||
|
||||
if (follow)
|
||||
obj = yaffsfs_FollowLink(obj, 0, &loop);
|
||||
|
||||
if (!dir && notDir)
|
||||
yaffsfs_SetError(-ENOTDIR);
|
||||
else if (loop)
|
||||
yaffsfs_SetError(-ELOOP);
|
||||
else if (!dir || !obj)
|
||||
yaffsfs_SetError(-ENOENT);
|
||||
else {
|
||||
retVal = yaffs_list_xattrib(obj, data, size);
|
||||
if (retVal < 0) {
|
||||
yaffsfs_SetError(retVal);
|
||||
retVal = -1;
|
||||
}
|
||||
}
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return retVal;
|
||||
|
||||
}
|
||||
|
||||
int yaffs_listxattr_reldir(struct yaffs_obj *reldir, const YCHAR *path,
|
||||
char *data, int size)
|
||||
{
|
||||
return yaffs_do_listxattr_reldir(reldir, path, data, size, 1);
|
||||
}
|
||||
|
||||
int yaffs_listxattr_reldev(struct yaffs_dev *dev, const YCHAR *path, char *data, int size)
|
||||
{
|
||||
return yaffs_listxattr_reldir(ROOT_DIR(dev), path, data, size);
|
||||
}
|
||||
|
||||
int yaffs_listxattr(const YCHAR *path, char *data, int size)
|
||||
{
|
||||
return yaffs_listxattr_reldir(NULL, path, data, size);
|
||||
}
|
||||
|
||||
int yaffs_llistxattr_reldir(struct yaffs_obj *reldir, const YCHAR *path,
|
||||
char *data, int size)
|
||||
{
|
||||
return yaffs_do_listxattr_reldir(reldir, path, data, size, 0);
|
||||
}
|
||||
|
||||
int yaffs_llistxattr_reldev(struct yaffs_dev *dev, const YCHAR *path, char *data, int size)
|
||||
{
|
||||
return yaffs_llistxattr_reldir(ROOT_DIR(dev), path, data, size);
|
||||
}
|
||||
|
||||
int yaffs_llistxattr(const YCHAR *path, char *data, int size)
|
||||
{
|
||||
return yaffs_llistxattr_reldir(NULL, path, data, size);
|
||||
}
|
||||
|
||||
int yaffs_flistxattr(int fd, char *data, int size)
|
||||
{
|
||||
struct yaffs_obj *obj;
|
||||
|
||||
int retVal = -1;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(data, size, 1) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
obj = yaffsfs_HandleToObject(fd);
|
||||
|
||||
if (obj) {
|
||||
retVal = yaffs_list_xattrib(obj, data, size);
|
||||
if (retVal < 0) {
|
||||
yaffsfs_SetError(retVal);
|
||||
retVal = -1;
|
||||
}
|
||||
} else
|
||||
/* bad handle */
|
||||
yaffsfs_SetError(-EBADF);
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
static int yaffs_do_removexattr_reldir(struct yaffs_obj *reldir, const YCHAR *path,
|
||||
const char *name, int follow)
|
||||
{
|
||||
struct yaffs_obj *obj = NULL;
|
||||
struct yaffs_obj *dir = NULL;
|
||||
int notDir = 0;
|
||||
int loop = 0;
|
||||
int retVal = -1;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(path, 0, 0) < 0 ||
|
||||
yaffsfs_CheckMemRegion(name, 0, 0) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_CheckPath(path) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return -1;
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
|
||||
obj = yaffsfs_FindObject(reldir, path, 0, 1, &dir, ¬Dir, &loop);
|
||||
|
||||
if (follow)
|
||||
obj = yaffsfs_FollowLink(obj, 0, &loop);
|
||||
|
||||
if (!dir && notDir)
|
||||
yaffsfs_SetError(-ENOTDIR);
|
||||
else if (loop)
|
||||
yaffsfs_SetError(-ELOOP);
|
||||
else if (!dir || !obj)
|
||||
yaffsfs_SetError(-ENOENT);
|
||||
else {
|
||||
retVal = yaffs_remove_xattrib(obj, name);
|
||||
if (retVal < 0) {
|
||||
yaffsfs_SetError(retVal);
|
||||
retVal = -1;
|
||||
}
|
||||
}
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return retVal;
|
||||
|
||||
}
|
||||
|
||||
int yaffs_removexattr_reldir(struct yaffs_obj *reldir, const YCHAR *path,
|
||||
const char *name)
|
||||
{
|
||||
return yaffs_do_removexattr_reldir(reldir, path, name, 1);
|
||||
}
|
||||
|
||||
int yaffs_removexattr_reldev(struct yaffs_dev *dev, const YCHAR *path, const char *name)
|
||||
{
|
||||
return yaffs_removexattr_reldir(ROOT_DIR(dev), path, name);
|
||||
}
|
||||
|
||||
int yaffs_removexattr(const YCHAR *path, const char *name)
|
||||
{
|
||||
return yaffs_removexattr_reldir(NULL, path, name);
|
||||
}
|
||||
|
||||
int yaffs_lremovexattr_reldir(struct yaffs_obj *reldir, const YCHAR *path,
|
||||
const char *name)
|
||||
{
|
||||
return yaffs_do_removexattr_reldir(reldir, path, name, 0);
|
||||
}
|
||||
|
||||
int yaffs_lremovexattr_reldev(struct yaffs_dev *dev, const YCHAR *path, const char *name)
|
||||
{
|
||||
return yaffs_lremovexattr_reldir(ROOT_DIR(dev), path, name);
|
||||
}
|
||||
|
||||
int yaffs_lremovexattr(const YCHAR *path, const char *name)
|
||||
{
|
||||
return yaffs_lremovexattr_reldir(NULL, path, name);
|
||||
}
|
||||
|
||||
int yaffs_fremovexattr(int fd, const char *name)
|
||||
{
|
||||
struct yaffs_obj *obj;
|
||||
|
||||
int retVal = -1;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(name, 0, 0) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
obj = yaffsfs_HandleToObject(fd);
|
||||
|
||||
if (obj) {
|
||||
retVal = yaffs_remove_xattrib(obj, name);
|
||||
if (retVal < 0) {
|
||||
yaffsfs_SetError(retVal);
|
||||
retVal = -1;
|
||||
}
|
||||
} else
|
||||
/* bad handle */
|
||||
yaffsfs_SetError(-EBADF);
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return retVal;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_YAFFS_WINCE
|
||||
int yaffs_get_wince_times(int fd, unsigned *wctime,
|
||||
unsigned *watime, unsigned *wmtime)
|
||||
{
|
||||
struct yaffs_obj *obj;
|
||||
|
||||
int retVal = -1;
|
||||
|
||||
yaffsfs_Lock();
|
||||
obj = yaffsfs_HandleToObject(fd);
|
||||
|
||||
if (obj) {
|
||||
|
||||
if (wctime) {
|
||||
wctime[0] = obj->win_ctime[0];
|
||||
wctime[1] = obj->win_ctime[1];
|
||||
}
|
||||
if (watime) {
|
||||
watime[0] = obj->win_atime[0];
|
||||
watime[1] = obj->win_atime[1];
|
||||
}
|
||||
if (wmtime) {
|
||||
wmtime[0] = obj->win_mtime[0];
|
||||
wmtime[1] = obj->win_mtime[1];
|
||||
}
|
||||
|
||||
retVal = 0;
|
||||
} else
|
||||
/* bad handle */
|
||||
yaffsfs_SetError(-EBADF);
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
int yaffs_set_wince_times(int fd,
|
||||
const unsigned *wctime,
|
||||
const unsigned *watime, const unsigned *wmtime)
|
||||
{
|
||||
struct yaffs_obj *obj;
|
||||
int result;
|
||||
int retVal = -1;
|
||||
|
||||
yaffsfs_Lock();
|
||||
obj = yaffsfs_HandleToObject(fd);
|
||||
|
||||
if (obj) {
|
||||
|
||||
if (wctime) {
|
||||
obj->win_ctime[0] = wctime[0];
|
||||
obj->win_ctime[1] = wctime[1];
|
||||
}
|
||||
if (watime) {
|
||||
obj->win_atime[0] = watime[0];
|
||||
obj->win_atime[1] = watime[1];
|
||||
}
|
||||
if (wmtime) {
|
||||
obj->win_mtime[0] = wmtime[0];
|
||||
obj->win_mtime[1] = wmtime[1];
|
||||
}
|
||||
|
||||
obj->dirty = 1;
|
||||
result = yaffs_flush_file(obj, 0, 0, 0);
|
||||
retVal = 0;
|
||||
} else
|
||||
/* bad handle */
|
||||
yaffsfs_SetError(-EBADF);
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int yaffsfs_DoChMod(struct yaffs_obj *obj, mode_t mode)
|
||||
{
|
||||
int result = -1;
|
||||
|
||||
if (obj)
|
||||
obj = yaffs_get_equivalent_obj(obj);
|
||||
|
||||
if (obj) {
|
||||
obj->yst_mode = mode;
|
||||
obj->dirty = 1;
|
||||
result = yaffs_flush_file(obj, 0, 0, 0);
|
||||
}
|
||||
|
||||
return result == YAFFS_OK ? 0 : -1;
|
||||
}
|
||||
|
||||
int yaffs_access_reldir(struct yaffs_obj *reldir, const YCHAR *path, int amode)
|
||||
{
|
||||
struct yaffs_obj *obj = NULL;
|
||||
struct yaffs_obj *dir = NULL;
|
||||
int notDir = 0;
|
||||
int loop = 0;
|
||||
int retval = -1;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(path, 0, 0) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_CheckPath(path) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (amode & ~(R_OK | W_OK | X_OK)) {
|
||||
yaffsfs_SetError(-EINVAL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
|
||||
obj = yaffsfs_FindObject(reldir, path, 0, 1, &dir, ¬Dir, &loop);
|
||||
obj = yaffsfs_FollowLink(obj, 0, &loop);
|
||||
|
||||
if (!dir && notDir)
|
||||
yaffsfs_SetError(-ENOTDIR);
|
||||
else if (loop)
|
||||
yaffsfs_SetError(-ELOOP);
|
||||
else if (!dir || !obj)
|
||||
yaffsfs_SetError(-ENOENT);
|
||||
else if ((amode & W_OK) && obj->my_dev->read_only)
|
||||
yaffsfs_SetError(-EROFS);
|
||||
else {
|
||||
int access_ok = 1;
|
||||
|
||||
if ((amode & R_OK) && !(obj->yst_mode & S_IREAD))
|
||||
access_ok = 0;
|
||||
if ((amode & W_OK) && !(obj->yst_mode & S_IWRITE))
|
||||
access_ok = 0;
|
||||
if ((amode & X_OK) && !(obj->yst_mode & S_IEXEC))
|
||||
access_ok = 0;
|
||||
|
||||
if (!access_ok)
|
||||
yaffsfs_SetError(-EACCES);
|
||||
else
|
||||
retval = 0;
|
||||
}
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return retval;
|
||||
|
||||
}
|
||||
|
||||
int yaffs_access_reldev(struct yaffs_dev *dev, const YCHAR *path, int amode)
|
||||
{
|
||||
return yaffs_access_reldir(ROOT_DIR(dev), path, amode);
|
||||
}
|
||||
|
||||
int yaffs_access(const YCHAR *path, int amode)
|
||||
{
|
||||
return yaffs_access_reldir(NULL, path, amode);
|
||||
}
|
||||
|
||||
int yaffs_chmod_reldir(struct yaffs_obj *reldir, const YCHAR *path, mode_t mode)
|
||||
{
|
||||
struct yaffs_obj *obj = NULL;
|
||||
struct yaffs_obj *dir = NULL;
|
||||
int retVal = -1;
|
||||
int notDir = 0;
|
||||
int loop = 0;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(path, 0, 0) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_CheckPath(path) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (mode & ~(0777)) {
|
||||
yaffsfs_SetError(-EINVAL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
|
||||
obj = yaffsfs_FindObject(reldir, path, 0, 1, &dir, ¬Dir, &loop);
|
||||
obj = yaffsfs_FollowLink(obj, 0, &loop);
|
||||
|
||||
if (!dir && notDir)
|
||||
yaffsfs_SetError(-ENOTDIR);
|
||||
else if (loop)
|
||||
yaffsfs_SetError(-ELOOP);
|
||||
else if (!dir || !obj)
|
||||
yaffsfs_SetError(-ENOENT);
|
||||
else if (obj->my_dev->read_only)
|
||||
yaffsfs_SetError(-EROFS);
|
||||
else
|
||||
retVal = yaffsfs_DoChMod(obj, mode);
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return retVal;
|
||||
|
||||
}
|
||||
|
||||
int yaffs_chmod_reldev(struct yaffs_dev *dev, const YCHAR *path, mode_t mode)
|
||||
{
|
||||
return yaffs_chmod_reldir(ROOT_DIR(dev), path, mode);
|
||||
}
|
||||
|
||||
int yaffs_chmod(const YCHAR *path, mode_t mode)
|
||||
{
|
||||
return yaffs_chmod_reldir(NULL, path, mode);
|
||||
}
|
||||
|
||||
int yaffs_fchmod(int fd, mode_t mode)
|
||||
{
|
||||
struct yaffs_obj *obj;
|
||||
int retVal = -1;
|
||||
|
||||
if (mode & ~(0777)) {
|
||||
yaffsfs_SetError(-EINVAL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
obj = yaffsfs_HandleToObject(fd);
|
||||
|
||||
if (!obj)
|
||||
yaffsfs_SetError(-EBADF);
|
||||
else if (obj->my_dev->read_only)
|
||||
yaffsfs_SetError(-EROFS);
|
||||
else
|
||||
retVal = yaffsfs_DoChMod(obj, mode);
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
int yaffs_mkdir_reldir(struct yaffs_obj *reldir, const YCHAR *path, mode_t mode)
|
||||
{
|
||||
struct yaffs_obj *parent = NULL;
|
||||
struct yaffs_obj *dir = NULL;
|
||||
YCHAR *name;
|
||||
YCHAR *alt_path = NULL;
|
||||
int retVal = -1;
|
||||
int notDir = 0;
|
||||
int loop = 0;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(path, 0, 0) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_CheckPath(path) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_alt_dir_path(path, &alt_path) < 0) {
|
||||
yaffsfs_SetError(-ENOMEM);
|
||||
return -1;
|
||||
}
|
||||
if (alt_path)
|
||||
path = alt_path;
|
||||
|
||||
yaffsfs_Lock();
|
||||
parent = yaffsfs_FindDirectory(reldir, path, &name, 0, ¬Dir, &loop);
|
||||
if (!parent && notDir)
|
||||
yaffsfs_SetError(-ENOTDIR);
|
||||
else if (loop)
|
||||
yaffsfs_SetError(-ELOOP);
|
||||
else if (!parent)
|
||||
yaffsfs_SetError(-ENOENT);
|
||||
else if (yaffsfs_TooManyObjects(parent->my_dev))
|
||||
yaffsfs_SetError(-ENFILE);
|
||||
else if (yaffs_strnlen(name, 5) == 0) {
|
||||
/* Trying to make the root itself */
|
||||
yaffsfs_SetError(-EEXIST);
|
||||
} else if (parent->my_dev->read_only)
|
||||
yaffsfs_SetError(-EROFS);
|
||||
else {
|
||||
dir = yaffs_create_dir(parent, name, mode, 0, 0);
|
||||
if (dir)
|
||||
retVal = 0;
|
||||
else if (yaffs_find_by_name(parent, name))
|
||||
yaffsfs_SetError(-EEXIST); /* name exists */
|
||||
else
|
||||
yaffsfs_SetError(-ENOSPC); /* assume no space */
|
||||
}
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
kfree(alt_path);
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
int yaffs_mkdir_reldev(struct yaffs_dev *dev, const YCHAR *path, mode_t mode)
|
||||
{
|
||||
return yaffs_mkdir_reldir(ROOT_DIR(dev), path, mode);
|
||||
}
|
||||
|
||||
int yaffs_mkdir(const YCHAR *path, mode_t mode)
|
||||
{
|
||||
return yaffs_mkdir_reldir(NULL, path, mode);
|
||||
}
|
||||
|
||||
int yaffs_rmdir_reldir(struct yaffs_obj *reldir, const YCHAR *path)
|
||||
{
|
||||
int result;
|
||||
YCHAR *alt_path;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(path, 0, 0) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_CheckPath(path) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_alt_dir_path(path, &alt_path) < 0) {
|
||||
yaffsfs_SetError(-ENOMEM);
|
||||
return -1;
|
||||
}
|
||||
if (alt_path)
|
||||
path = alt_path;
|
||||
result = yaffsfs_DoUnlink_reldir(reldir, path, 1);
|
||||
|
||||
kfree(alt_path);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int yaffs_rmdir_reldev(struct yaffs_dev *dev, const YCHAR *path)
|
||||
{
|
||||
return yaffs_rmdir_reldir(ROOT_DIR(dev), path);
|
||||
}
|
||||
|
||||
int yaffs_rmdir(const YCHAR *path)
|
||||
{
|
||||
return yaffs_rmdir_reldir(NULL, path);
|
||||
}
|
||||
|
||||
/*
|
||||
* The mount/unmount/sync functions act on devices rather than reldirs.
|
||||
*/
|
||||
void *yaffs_getdev(const YCHAR *path)
|
||||
{
|
||||
struct yaffs_dev *dev = NULL;
|
||||
YCHAR *dummy;
|
||||
dev = yaffsfs_FindDevice(path, &dummy);
|
||||
return (void *)dev;
|
||||
}
|
||||
|
||||
int yaffs_mount_common(struct yaffs_dev *dev, const YCHAR *path,
|
||||
int read_only, int skip_checkpt)
|
||||
{
|
||||
int retVal = -1;
|
||||
int result = YAFFS_FAIL;
|
||||
|
||||
if (!dev) {
|
||||
if (yaffsfs_CheckMemRegion(path, 0, 0) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
yaffs_trace(YAFFS_TRACE_MOUNT, "yaffs: Mounting %s", path);
|
||||
|
||||
if (yaffsfs_CheckPath(path) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
|
||||
yaffsfs_InitHandles();
|
||||
|
||||
if (!dev)
|
||||
dev = yaffsfs_FindMountPoint(path);
|
||||
|
||||
if (dev) {
|
||||
if (!dev->is_mounted) {
|
||||
dev->read_only = read_only ? 1 : 0;
|
||||
if (skip_checkpt) {
|
||||
u8 skip = dev->param.skip_checkpt_rd;
|
||||
dev->param.skip_checkpt_rd = 1;
|
||||
result = yaffs_guts_initialise(dev);
|
||||
dev->param.skip_checkpt_rd = skip;
|
||||
} else {
|
||||
result = yaffs_guts_initialise(dev);
|
||||
}
|
||||
|
||||
if (result == YAFFS_FAIL)
|
||||
yaffsfs_SetError(-ENOMEM);
|
||||
retVal = result ? 0 : -1;
|
||||
|
||||
} else
|
||||
yaffsfs_SetError(-EBUSY);
|
||||
} else
|
||||
yaffsfs_SetError(-ENODEV);
|
||||
|
||||
yaffsfs_Unlock();
|
||||
return retVal;
|
||||
|
||||
}
|
||||
|
||||
int yaffs_mount3_reldev(struct yaffs_dev *dev, int read_only, int skip_checkpt)
|
||||
{
|
||||
return yaffs_mount_common(dev, NULL, read_only, skip_checkpt);
|
||||
}
|
||||
|
||||
int yaffs_mount3(const YCHAR *path, int read_only, int skip_checkpt)
|
||||
{
|
||||
return yaffs_mount_common(NULL, path, read_only, skip_checkpt);
|
||||
}
|
||||
|
||||
int yaffs_mount2_reldev(struct yaffs_dev *dev, int readonly)
|
||||
{
|
||||
return yaffs_mount_common(dev, NULL, readonly, 0);
|
||||
}
|
||||
|
||||
int yaffs_mount2(const YCHAR *path, int readonly)
|
||||
{
|
||||
return yaffs_mount_common(NULL, path, readonly, 0);
|
||||
}
|
||||
|
||||
int yaffs_mount_reldev(struct yaffs_dev *dev)
|
||||
{
|
||||
return yaffs_mount_common(dev, NULL, 0, 0);
|
||||
}
|
||||
|
||||
int yaffs_mount(const YCHAR *path)
|
||||
{
|
||||
return yaffs_mount_common(NULL, path, 0, 0);
|
||||
}
|
||||
|
||||
int yaffs_sync_common(struct yaffs_dev *dev, const YCHAR *path)
|
||||
{
|
||||
int retVal = -1;
|
||||
YCHAR *dummy;
|
||||
|
||||
if (!dev) {
|
||||
if (yaffsfs_CheckMemRegion(path, 0, 0) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_CheckPath(path) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
if (!dev)
|
||||
dev = yaffsfs_FindDevice(path, &dummy);
|
||||
|
||||
if (dev) {
|
||||
if (!dev->is_mounted)
|
||||
yaffsfs_SetError(-EINVAL);
|
||||
else if (dev->read_only)
|
||||
yaffsfs_SetError(-EROFS);
|
||||
else {
|
||||
|
||||
yaffs_flush_whole_cache(dev, 0);
|
||||
yaffs_checkpoint_save(dev);
|
||||
retVal = 0;
|
||||
|
||||
}
|
||||
} else
|
||||
yaffsfs_SetError(-ENODEV);
|
||||
|
||||
yaffsfs_Unlock();
|
||||
return retVal;
|
||||
}
|
||||
|
||||
int yaffs_sync_reldev(struct yaffs_dev *dev)
|
||||
{
|
||||
return yaffs_sync_common(dev, NULL);
|
||||
}
|
||||
|
||||
int yaffs_sync(const YCHAR *path)
|
||||
{
|
||||
return yaffs_sync_common(NULL, path);
|
||||
}
|
||||
|
||||
|
||||
static int yaffsfs_bg_gc_common(struct yaffs_dev *dev,
|
||||
const YCHAR *path,
|
||||
int urgency)
|
||||
{
|
||||
int retVal = -1;
|
||||
YCHAR *dummy;
|
||||
|
||||
if (!dev) {
|
||||
if (yaffsfs_CheckMemRegion(path, 0, 0) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_CheckPath(path) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
if (!dev)
|
||||
dev = yaffsfs_FindDevice(path, &dummy);
|
||||
|
||||
if (dev) {
|
||||
if (!dev->is_mounted)
|
||||
yaffsfs_SetError(-EINVAL);
|
||||
else
|
||||
retVal = yaffs_bg_gc(dev, urgency);
|
||||
} else
|
||||
yaffsfs_SetError(-ENODEV);
|
||||
|
||||
yaffsfs_Unlock();
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/* Background gc functions.
|
||||
* These return 0 when bg done or greater than 0 when gc has been
|
||||
* done and there is still a lot of garbage to be cleaned up.
|
||||
*/
|
||||
|
||||
int yaffs_do_background_gc(const YCHAR *path, int urgency)
|
||||
{
|
||||
return yaffsfs_bg_gc_common(NULL, path, urgency);
|
||||
}
|
||||
|
||||
int yaffs_do_background_gc_reldev(struct yaffs_dev *dev, int urgency)
|
||||
{
|
||||
return yaffsfs_bg_gc_common(dev, NULL, urgency);
|
||||
}
|
||||
|
||||
static int yaffsfs_IsDevBusy(struct yaffs_dev *dev)
|
||||
{
|
||||
int i;
|
||||
struct yaffs_obj *obj;
|
||||
|
||||
for (i = 0; i < YAFFSFS_N_HANDLES; i++) {
|
||||
obj = yaffsfs_HandleToObject(i);
|
||||
if (obj && obj->my_dev == dev)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int yaffs_remount_common(struct yaffs_dev *dev, const YCHAR *path,
|
||||
int force, int read_only)
|
||||
{
|
||||
int retVal = -1;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(path, 0, 0) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_CheckPath(path) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return -1;
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
if (!dev)
|
||||
dev = yaffsfs_FindMountPoint(path);
|
||||
|
||||
if (dev) {
|
||||
if (dev->is_mounted) {
|
||||
yaffs_flush_whole_cache(dev, 0);
|
||||
|
||||
if (force || !yaffsfs_IsDevBusy(dev)) {
|
||||
if (read_only)
|
||||
yaffs_checkpoint_save(dev);
|
||||
dev->read_only = read_only ? 1 : 0;
|
||||
retVal = 0;
|
||||
} else
|
||||
yaffsfs_SetError(-EBUSY);
|
||||
|
||||
} else
|
||||
yaffsfs_SetError(-EINVAL);
|
||||
|
||||
} else
|
||||
yaffsfs_SetError(-ENODEV);
|
||||
|
||||
yaffsfs_Unlock();
|
||||
return retVal;
|
||||
|
||||
}
|
||||
|
||||
int yaffs_remount_reldev(struct yaffs_dev *dev, int force, int read_only)
|
||||
{
|
||||
return yaffs_remount_common(dev, NULL, force, read_only);
|
||||
}
|
||||
int yaffs_remount(const YCHAR *path, int force, int read_only)
|
||||
{
|
||||
return yaffs_remount_common(NULL, path, force, read_only);
|
||||
}
|
||||
|
||||
int yaffs_unmount2_common(struct yaffs_dev *dev, const YCHAR *path, int force)
|
||||
{
|
||||
int retVal = -1;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(path, 0, 0) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_CheckPath(path) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return -1;
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
if (!dev)
|
||||
dev = yaffsfs_FindMountPoint(path);
|
||||
|
||||
if (dev) {
|
||||
if (dev->is_mounted) {
|
||||
int inUse;
|
||||
yaffs_flush_whole_cache(dev, 0);
|
||||
yaffs_checkpoint_save(dev);
|
||||
inUse = yaffsfs_IsDevBusy(dev);
|
||||
if (!inUse || force) {
|
||||
if (inUse)
|
||||
yaffsfs_BreakDeviceHandles(dev);
|
||||
yaffs_deinitialise(dev);
|
||||
|
||||
retVal = 0;
|
||||
} else
|
||||
yaffsfs_SetError(-EBUSY);
|
||||
|
||||
} else
|
||||
yaffsfs_SetError(-EINVAL);
|
||||
|
||||
} else
|
||||
yaffsfs_SetError(-ENODEV);
|
||||
|
||||
yaffsfs_Unlock();
|
||||
return retVal;
|
||||
|
||||
}
|
||||
|
||||
int yaffs_unmount2_reldev(struct yaffs_dev *dev, int force)
|
||||
{
|
||||
return yaffs_unmount2_common(dev, NULL, force);
|
||||
}
|
||||
|
||||
int yaffs_unmount2(const YCHAR *path, int force)
|
||||
{
|
||||
return yaffs_unmount2_common(NULL, path, force);
|
||||
}
|
||||
|
||||
int yaffs_unmount_reldev(struct yaffs_dev *dev)
|
||||
{
|
||||
return yaffs_unmount2_reldev(dev, 0);
|
||||
}
|
||||
|
||||
int yaffs_unmount(const YCHAR *path)
|
||||
{
|
||||
return yaffs_unmount2(path, 0);
|
||||
}
|
||||
|
||||
int yaffs_format_common(struct yaffs_dev *dev,
|
||||
const YCHAR *path,
|
||||
int unmount_flag,
|
||||
int force_unmount_flag,
|
||||
int remount_flag)
|
||||
{
|
||||
int retVal = 0;
|
||||
int result;
|
||||
|
||||
if (!dev) {
|
||||
if (!path) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_CheckPath(path) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
if (!dev)
|
||||
dev = yaffsfs_FindMountPoint(path);
|
||||
|
||||
if (dev) {
|
||||
int was_mounted = dev->is_mounted;
|
||||
|
||||
if (dev->is_mounted && unmount_flag) {
|
||||
int inUse;
|
||||
yaffs_flush_whole_cache(dev, 0);
|
||||
yaffs_checkpoint_save(dev);
|
||||
inUse = yaffsfs_IsDevBusy(dev);
|
||||
if (!inUse || force_unmount_flag) {
|
||||
if (inUse)
|
||||
yaffsfs_BreakDeviceHandles(dev);
|
||||
yaffs_deinitialise(dev);
|
||||
}
|
||||
}
|
||||
|
||||
if(dev->is_mounted) {
|
||||
yaffsfs_SetError(-EBUSY);
|
||||
retVal = -1;
|
||||
} else {
|
||||
yaffs_guts_format_dev(dev);
|
||||
if(was_mounted && remount_flag) {
|
||||
result = yaffs_guts_initialise(dev);
|
||||
if (result == YAFFS_FAIL) {
|
||||
yaffsfs_SetError(-ENOMEM);
|
||||
retVal = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
yaffsfs_SetError(-ENODEV);
|
||||
retVal = -1;
|
||||
}
|
||||
|
||||
yaffsfs_Unlock();
|
||||
return retVal;
|
||||
|
||||
}
|
||||
|
||||
int yaffs_format_reldev(struct yaffs_dev *dev,
|
||||
int unmount_flag,
|
||||
int force_unmount_flag,
|
||||
int remount_flag)
|
||||
{
|
||||
return yaffs_format_common(dev, NULL, unmount_flag,
|
||||
force_unmount_flag, remount_flag);
|
||||
}
|
||||
|
||||
int yaffs_format(const YCHAR *path,
|
||||
int unmount_flag,
|
||||
int force_unmount_flag,
|
||||
int remount_flag)
|
||||
{
|
||||
return yaffs_format_common(NULL, path, unmount_flag,
|
||||
force_unmount_flag, remount_flag);
|
||||
}
|
||||
|
||||
Y_LOFF_T yaffs_freespace_common(struct yaffs_dev *dev, const YCHAR *path)
|
||||
{
|
||||
Y_LOFF_T retVal = -1;
|
||||
YCHAR *dummy;
|
||||
|
||||
if (!dev) {
|
||||
if (yaffsfs_CheckMemRegion(path, 0, 0) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_CheckPath(path) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
if (!dev)
|
||||
dev = yaffsfs_FindDevice(path, &dummy);
|
||||
if (dev && dev->is_mounted) {
|
||||
retVal = yaffs_get_n_free_chunks(dev);
|
||||
retVal *= dev->data_bytes_per_chunk;
|
||||
|
||||
} else
|
||||
yaffsfs_SetError(-EINVAL);
|
||||
|
||||
yaffsfs_Unlock();
|
||||
return retVal;
|
||||
}
|
||||
|
||||
Y_LOFF_T yaffs_freespace_reldev(struct yaffs_dev *dev)
|
||||
{
|
||||
return yaffs_freespace_common(dev, NULL);
|
||||
}
|
||||
|
||||
Y_LOFF_T yaffs_freespace(const YCHAR *path)
|
||||
{
|
||||
return yaffs_freespace_common(NULL, path);
|
||||
}
|
||||
|
||||
Y_LOFF_T yaffs_totalspace_common(struct yaffs_dev *dev, const YCHAR *path)
|
||||
{
|
||||
Y_LOFF_T retVal = -1;
|
||||
YCHAR *dummy;
|
||||
|
||||
if (!dev) {
|
||||
if (yaffsfs_CheckMemRegion(path, 0, 0) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_CheckPath(path) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
if (!dev)
|
||||
dev = yaffsfs_FindDevice(path, &dummy);
|
||||
if (dev && dev->is_mounted) {
|
||||
retVal = (dev->param.end_block - dev->param.start_block + 1) -
|
||||
dev->param.n_reserved_blocks;
|
||||
retVal *= dev->param.chunks_per_block;
|
||||
retVal *= dev->data_bytes_per_chunk;
|
||||
|
||||
} else
|
||||
yaffsfs_SetError(-EINVAL);
|
||||
|
||||
yaffsfs_Unlock();
|
||||
return retVal;
|
||||
}
|
||||
|
||||
Y_LOFF_T yaffs_totalspace_reldev(struct yaffs_dev *dev)
|
||||
{
|
||||
return yaffs_totalspace_common(dev, NULL);
|
||||
}
|
||||
|
||||
Y_LOFF_T yaffs_totalspace(const YCHAR *path)
|
||||
{
|
||||
return yaffs_totalspace_common(NULL, path);
|
||||
}
|
||||
|
||||
int yaffs_inodecount_common(struct yaffs_dev *dev, const YCHAR *path)
|
||||
{
|
||||
Y_LOFF_T retVal = -1;
|
||||
YCHAR *dummy;
|
||||
|
||||
if (!dev) {
|
||||
if (yaffsfs_CheckMemRegion(path, 0, 0) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_CheckPath(path) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
if (!dev)
|
||||
dev = yaffsfs_FindDevice(path, &dummy);
|
||||
if (dev && dev->is_mounted) {
|
||||
int n_obj = dev->n_obj;
|
||||
if (n_obj > dev->n_hardlinks)
|
||||
retVal = n_obj - dev->n_hardlinks;
|
||||
}
|
||||
|
||||
if (retVal < 0)
|
||||
yaffsfs_SetError(-EINVAL);
|
||||
|
||||
yaffsfs_Unlock();
|
||||
return retVal;
|
||||
}
|
||||
|
||||
int yaffs_inodecount_reldev(struct yaffs_dev *dev)
|
||||
{
|
||||
return yaffs_inodecount_common(dev, NULL);
|
||||
}
|
||||
|
||||
int yaffs_inodecount(const YCHAR *path)
|
||||
{
|
||||
return yaffs_inodecount_common(NULL, path);
|
||||
}
|
||||
|
||||
void yaffs_add_device(struct yaffs_dev *dev)
|
||||
{
|
||||
struct list_head *cfg;
|
||||
/* First check that the device is not in the list. */
|
||||
|
||||
list_for_each(cfg, &yaffsfs_deviceList) {
|
||||
if (dev == list_entry(cfg, struct yaffs_dev, dev_list))
|
||||
return;
|
||||
}
|
||||
|
||||
dev->is_mounted = 0;
|
||||
dev->param.remove_obj_fn = yaffsfs_RemoveObjectCallback;
|
||||
|
||||
if (!dev->dev_list.next)
|
||||
INIT_LIST_HEAD(&dev->dev_list);
|
||||
|
||||
list_add(&dev->dev_list, &yaffsfs_deviceList);
|
||||
|
||||
|
||||
}
|
||||
|
||||
void yaffs_remove_device(struct yaffs_dev *dev)
|
||||
{
|
||||
list_del_init(&dev->dev_list);
|
||||
}
|
||||
|
||||
/* Functions to iterate through devices. NB Use with extreme care! */
|
||||
|
||||
static struct list_head *dev_iterator;
|
||||
void yaffs_dev_rewind(void)
|
||||
{
|
||||
dev_iterator = yaffsfs_deviceList.next;
|
||||
}
|
||||
|
||||
struct yaffs_dev *yaffs_next_dev(void)
|
||||
{
|
||||
struct yaffs_dev *retval;
|
||||
|
||||
if (!dev_iterator)
|
||||
return NULL;
|
||||
if (dev_iterator == &yaffsfs_deviceList)
|
||||
return NULL;
|
||||
|
||||
retval = list_entry(dev_iterator, struct yaffs_dev, dev_list);
|
||||
dev_iterator = dev_iterator->next;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Directory search stuff. */
|
||||
|
||||
static struct list_head search_contexts;
|
||||
|
||||
static void yaffsfs_SetDirRewound(struct yaffsfs_DirSearchContext *dsc)
|
||||
{
|
||||
if (dsc &&
|
||||
dsc->dirObj &&
|
||||
dsc->dirObj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) {
|
||||
|
||||
dsc->offset = 0;
|
||||
|
||||
if (list_empty(&dsc->dirObj->variant.dir_variant.children))
|
||||
dsc->nextReturn = NULL;
|
||||
else
|
||||
dsc->nextReturn =
|
||||
list_entry(dsc->dirObj->variant.dir_variant.
|
||||
children.next, struct yaffs_obj,
|
||||
siblings);
|
||||
} else {
|
||||
/* Hey someone isn't playing nice! */
|
||||
}
|
||||
}
|
||||
|
||||
static void yaffsfs_DirAdvance(struct yaffsfs_DirSearchContext *dsc)
|
||||
{
|
||||
if (dsc &&
|
||||
dsc->dirObj &&
|
||||
dsc->dirObj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) {
|
||||
|
||||
if (dsc->nextReturn == NULL ||
|
||||
list_empty(&dsc->dirObj->variant.dir_variant.children))
|
||||
dsc->nextReturn = NULL;
|
||||
else {
|
||||
struct list_head *next = dsc->nextReturn->siblings.next;
|
||||
|
||||
if (next == &dsc->dirObj->variant.dir_variant.children)
|
||||
dsc->nextReturn = NULL; /* end of list */
|
||||
else
|
||||
dsc->nextReturn = list_entry(next,
|
||||
struct yaffs_obj,
|
||||
siblings);
|
||||
}
|
||||
} else {
|
||||
/* Hey someone isn't playing nice! */
|
||||
}
|
||||
}
|
||||
|
||||
static void yaffsfs_RemoveObjectCallback(struct yaffs_obj *obj)
|
||||
{
|
||||
|
||||
struct list_head *i;
|
||||
struct yaffsfs_DirSearchContext *dsc;
|
||||
|
||||
/* if search contexts not initilised then skip */
|
||||
if (!search_contexts.next)
|
||||
return;
|
||||
|
||||
/* Iterate through the directory search contexts.
|
||||
* If any are the one being removed, then advance the dsc to
|
||||
* the next one to prevent a hanging ptr.
|
||||
*/
|
||||
list_for_each(i, &search_contexts) {
|
||||
if (i) {
|
||||
dsc = list_entry(i, struct yaffsfs_DirSearchContext,
|
||||
others);
|
||||
if (dsc->nextReturn == obj)
|
||||
yaffsfs_DirAdvance(dsc);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static yaffs_DIR *yaffsfs_opendir_reldir_no_lock(struct yaffs_obj *reldir,
|
||||
const YCHAR *dirname)
|
||||
{
|
||||
yaffs_DIR *dir = NULL;
|
||||
struct yaffs_obj *obj = NULL;
|
||||
struct yaffsfs_DirSearchContext *dsc = NULL;
|
||||
int notDir = 0;
|
||||
int loop = 0;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(dirname, 0, 0) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (yaffsfs_CheckPath(dirname) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
obj = yaffsfs_FindObject(reldir, dirname, 0, 1, NULL, ¬Dir, &loop);
|
||||
|
||||
if (!obj && notDir)
|
||||
yaffsfs_SetError(-ENOTDIR);
|
||||
else if (loop)
|
||||
yaffsfs_SetError(-ELOOP);
|
||||
else if (!obj)
|
||||
yaffsfs_SetError(-ENOENT);
|
||||
else if (obj->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY)
|
||||
yaffsfs_SetError(-ENOTDIR);
|
||||
else {
|
||||
int i;
|
||||
|
||||
for (i = 0, dsc = NULL; i < YAFFSFS_N_DSC && !dsc; i++) {
|
||||
if (!yaffsfs_dsc[i].inUse)
|
||||
dsc = &yaffsfs_dsc[i];
|
||||
}
|
||||
|
||||
dir = (yaffs_DIR *) dsc;
|
||||
|
||||
if (dsc) {
|
||||
memset(dsc, 0, sizeof(struct yaffsfs_DirSearchContext));
|
||||
dsc->inUse = 1;
|
||||
dsc->dirObj = obj;
|
||||
yaffs_strncpy(dsc->name, dirname, NAME_MAX);
|
||||
INIT_LIST_HEAD(&dsc->others);
|
||||
|
||||
if (!search_contexts.next)
|
||||
INIT_LIST_HEAD(&search_contexts);
|
||||
|
||||
list_add(&dsc->others, &search_contexts);
|
||||
yaffsfs_SetDirRewound(dsc);
|
||||
}
|
||||
}
|
||||
return dir;
|
||||
}
|
||||
|
||||
yaffs_DIR *yaffs_opendir_reldir(struct yaffs_obj *reldir, const YCHAR *dirname)
|
||||
{
|
||||
yaffs_DIR *ret;
|
||||
|
||||
yaffsfs_Lock();
|
||||
ret = yaffsfs_opendir_reldir_no_lock(reldir, dirname);
|
||||
yaffsfs_Unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
yaffs_DIR *yaffs_opendir_reldev(struct yaffs_dev *dev, const YCHAR *dirname)
|
||||
{
|
||||
return yaffs_opendir_reldir(ROOT_DIR(dev), dirname);
|
||||
}
|
||||
|
||||
yaffs_DIR *yaffs_opendir(const YCHAR *dirname)
|
||||
{
|
||||
return yaffs_opendir_reldir(NULL, dirname);
|
||||
}
|
||||
|
||||
struct yaffs_dirent *yaffsfs_readdir_no_lock(yaffs_DIR * dirp)
|
||||
{
|
||||
struct yaffsfs_DirSearchContext *dsc;
|
||||
struct yaffs_dirent *retVal = NULL;
|
||||
|
||||
dsc = (struct yaffsfs_DirSearchContext *) dirp;
|
||||
|
||||
|
||||
if (dsc && dsc->inUse) {
|
||||
yaffsfs_SetError(0);
|
||||
if (dsc->nextReturn) {
|
||||
dsc->de.d_ino =
|
||||
yaffs_get_equivalent_obj(dsc->nextReturn)->obj_id;
|
||||
dsc->de.d_dont_use = (unsigned)dsc->nextReturn;
|
||||
dsc->de.d_off = dsc->offset++;
|
||||
yaffs_get_obj_name(dsc->nextReturn,
|
||||
dsc->de.d_name, NAME_MAX);
|
||||
if (yaffs_strnlen(dsc->de.d_name, NAME_MAX + 1) == 0) {
|
||||
/* this should not happen! */
|
||||
yaffs_strcpy(dsc->de.d_name, _Y("zz"));
|
||||
}
|
||||
dsc->de.d_reclen = sizeof(struct yaffs_dirent);
|
||||
retVal = &dsc->de;
|
||||
yaffsfs_DirAdvance(dsc);
|
||||
} else
|
||||
retVal = NULL;
|
||||
} else
|
||||
yaffsfs_SetError(-EBADF);
|
||||
|
||||
return retVal;
|
||||
|
||||
}
|
||||
struct yaffs_dirent *yaffs_readdir(yaffs_DIR * dirp)
|
||||
{
|
||||
struct yaffs_dirent *ret;
|
||||
|
||||
yaffsfs_Lock();
|
||||
ret = yaffsfs_readdir_no_lock(dirp);
|
||||
yaffsfs_Unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void yaffsfs_rewinddir_no_lock(yaffs_DIR *dirp)
|
||||
{
|
||||
struct yaffsfs_DirSearchContext *dsc;
|
||||
|
||||
dsc = (struct yaffsfs_DirSearchContext *) dirp;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(dirp, sizeof(*dsc), 0) < 0)
|
||||
return;
|
||||
|
||||
yaffsfs_SetDirRewound(dsc);
|
||||
|
||||
}
|
||||
|
||||
void yaffs_rewinddir(yaffs_DIR *dirp)
|
||||
{
|
||||
yaffsfs_Lock();
|
||||
yaffsfs_rewinddir_no_lock(dirp);
|
||||
yaffsfs_Unlock();
|
||||
}
|
||||
|
||||
struct yaffs_dirent *yaffs_readdir_fd(int fd)
|
||||
{
|
||||
struct yaffs_dirent *ret = NULL;
|
||||
struct yaffsfs_FileDes *f;
|
||||
|
||||
yaffsfs_Lock();
|
||||
f = yaffsfs_HandleToFileDes(fd);
|
||||
if(f && f->isDir)
|
||||
ret = yaffsfs_readdir_no_lock(f->v.dir);
|
||||
yaffsfs_Unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void yaffs_rewinddir_fd(int fd)
|
||||
{
|
||||
struct yaffsfs_FileDes *f;
|
||||
|
||||
yaffsfs_Lock();
|
||||
f = yaffsfs_HandleToFileDes(fd);
|
||||
if(f && f->isDir)
|
||||
yaffsfs_rewinddir_no_lock(f->v.dir);
|
||||
yaffsfs_Unlock();
|
||||
}
|
||||
|
||||
|
||||
static int yaffsfs_closedir_no_lock(yaffs_DIR *dirp)
|
||||
{
|
||||
struct yaffsfs_DirSearchContext *dsc;
|
||||
|
||||
dsc = (struct yaffsfs_DirSearchContext *) dirp;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(dirp, sizeof(*dsc), 0) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
dsc->inUse = 0;
|
||||
list_del(&dsc->others); /* unhook from list */
|
||||
|
||||
return 0;
|
||||
}
|
||||
int yaffs_closedir(yaffs_DIR *dirp)
|
||||
{
|
||||
int ret;
|
||||
|
||||
yaffsfs_Lock();
|
||||
ret = yaffsfs_closedir_no_lock(dirp);
|
||||
yaffsfs_Unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* End of directory stuff */
|
||||
|
||||
int yaffs_symlink_reldir(struct yaffs_obj *reldir,
|
||||
const YCHAR *oldpath, const YCHAR *newpath)
|
||||
{
|
||||
struct yaffs_obj *parent = NULL;
|
||||
struct yaffs_obj *obj;
|
||||
YCHAR *name;
|
||||
int retVal = -1;
|
||||
int mode = 0; /* ignore for now */
|
||||
int notDir = 0;
|
||||
int loop = 0;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(oldpath, 0, 0) < 0 ||
|
||||
yaffsfs_CheckMemRegion(newpath, 0, 0) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_CheckPath(newpath) < 0 || yaffsfs_CheckPath(oldpath) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return -1;
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
parent = yaffsfs_FindDirectory(reldir, newpath, &name, 0, ¬Dir, &loop);
|
||||
if (!parent && notDir)
|
||||
yaffsfs_SetError(-ENOTDIR);
|
||||
else if (loop)
|
||||
yaffsfs_SetError(-ELOOP);
|
||||
else if (!parent || yaffs_strnlen(name, 5) < 1)
|
||||
yaffsfs_SetError(-ENOENT);
|
||||
else if (yaffsfs_TooManyObjects(parent->my_dev))
|
||||
yaffsfs_SetError(-ENFILE);
|
||||
else if (parent->my_dev->read_only)
|
||||
yaffsfs_SetError(-EROFS);
|
||||
else if (parent) {
|
||||
obj = yaffs_create_symlink(parent, name, mode, 0, 0, oldpath);
|
||||
if (obj)
|
||||
retVal = 0;
|
||||
else if (yaffsfs_FindObject(reldir, newpath, 0, 0,
|
||||
NULL, NULL, NULL))
|
||||
yaffsfs_SetError(-EEXIST);
|
||||
else
|
||||
yaffsfs_SetError(-ENOSPC);
|
||||
}
|
||||
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return retVal;
|
||||
|
||||
}
|
||||
int yaffs_symlink(const YCHAR *oldpath, const YCHAR *newpath)
|
||||
{
|
||||
return yaffs_symlink_reldir(NULL, oldpath, newpath);
|
||||
}
|
||||
|
||||
int yaffs_readlink_reldir(struct yaffs_obj *reldir,const YCHAR *path,
|
||||
YCHAR *buf, int bufsiz)
|
||||
{
|
||||
struct yaffs_obj *obj = NULL;
|
||||
struct yaffs_obj *dir = NULL;
|
||||
int retVal = -1;
|
||||
int notDir = 0;
|
||||
int loop = 0;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(path, 0, 0) < 0 ||
|
||||
yaffsfs_CheckMemRegion(buf, bufsiz, 1) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
|
||||
obj = yaffsfs_FindObject(reldir, path, 0, 1, &dir, ¬Dir, &loop);
|
||||
|
||||
if (!dir && notDir)
|
||||
yaffsfs_SetError(-ENOTDIR);
|
||||
else if (loop)
|
||||
yaffsfs_SetError(-ELOOP);
|
||||
else if (!dir || !obj)
|
||||
yaffsfs_SetError(-ENOENT);
|
||||
else if (obj->variant_type != YAFFS_OBJECT_TYPE_SYMLINK)
|
||||
yaffsfs_SetError(-EINVAL);
|
||||
else {
|
||||
YCHAR *alias = obj->variant.symlink_variant.alias;
|
||||
memset(buf, 0, bufsiz);
|
||||
yaffs_strncpy(buf, alias, bufsiz - 1);
|
||||
retVal = 0;
|
||||
}
|
||||
yaffsfs_Unlock();
|
||||
return retVal;
|
||||
}
|
||||
int yaffs_readlink(const YCHAR *path, YCHAR *buf, int bufsiz)
|
||||
{
|
||||
return yaffs_readlink_reldir(NULL, path, buf, bufsiz);
|
||||
}
|
||||
|
||||
int yaffs_link_reldir(struct yaffs_obj *reldir,
|
||||
const YCHAR *oldpath, const YCHAR *linkpath)
|
||||
{
|
||||
/* Creates a link called newpath to existing oldpath */
|
||||
struct yaffs_obj *obj = NULL;
|
||||
struct yaffs_obj *lnk = NULL;
|
||||
struct yaffs_obj *obj_dir = NULL;
|
||||
struct yaffs_obj *lnk_dir = NULL;
|
||||
int retVal = -1;
|
||||
int notDirObj = 0;
|
||||
int notDirLnk = 0;
|
||||
int objLoop = 0;
|
||||
int lnkLoop = 0;
|
||||
YCHAR *newname;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(oldpath, 0, 0) < 0 ||
|
||||
yaffsfs_CheckMemRegion(linkpath, 0, 0) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_CheckPath(linkpath) < 0 || yaffsfs_CheckPath(oldpath) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return -1;
|
||||
}
|
||||
|
||||
yaffsfs_Lock();
|
||||
|
||||
obj = yaffsfs_FindObject(reldir, oldpath, 0, 1,
|
||||
&obj_dir, ¬DirObj, &objLoop);
|
||||
lnk = yaffsfs_FindObject(reldir, linkpath, 0, 0, NULL, NULL, NULL);
|
||||
lnk_dir = yaffsfs_FindDirectory(reldir, linkpath, &newname,
|
||||
0, ¬DirLnk, &lnkLoop);
|
||||
|
||||
if ((!obj_dir && notDirObj) || (!lnk_dir && notDirLnk))
|
||||
yaffsfs_SetError(-ENOTDIR);
|
||||
else if (objLoop || lnkLoop)
|
||||
yaffsfs_SetError(-ELOOP);
|
||||
else if (!obj_dir || !lnk_dir || !obj)
|
||||
yaffsfs_SetError(-ENOENT);
|
||||
else if (obj->my_dev->read_only)
|
||||
yaffsfs_SetError(-EROFS);
|
||||
else if (yaffsfs_TooManyObjects(obj->my_dev))
|
||||
yaffsfs_SetError(-ENFILE);
|
||||
else if (lnk)
|
||||
yaffsfs_SetError(-EEXIST);
|
||||
else if (lnk_dir->my_dev != obj->my_dev)
|
||||
yaffsfs_SetError(-EXDEV);
|
||||
else {
|
||||
retVal = yaffsfs_CheckNameLength(newname);
|
||||
|
||||
if (retVal == 0) {
|
||||
lnk = yaffs_link_obj(lnk_dir, newname, obj);
|
||||
if (lnk)
|
||||
retVal = 0;
|
||||
else {
|
||||
yaffsfs_SetError(-ENOSPC);
|
||||
retVal = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
yaffsfs_Unlock();
|
||||
|
||||
return retVal;
|
||||
}
|
||||
int yaffs_link(const YCHAR *oldpath, const YCHAR *linkpath)
|
||||
{
|
||||
return yaffs_link_reldir(NULL, oldpath, linkpath);
|
||||
}
|
||||
|
||||
int yaffs_mknod_reldir(struct yaffs_obj *reldir, const YCHAR *pathname,
|
||||
mode_t mode, dev_t dev_val)
|
||||
{
|
||||
(void) pathname;
|
||||
(void) mode;
|
||||
(void) dev_val;
|
||||
(void) reldir;
|
||||
|
||||
yaffsfs_SetError(-EINVAL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int yaffs_mknod_reldev(struct yaffs_dev *dev, const YCHAR *pathname, mode_t mode, dev_t dev_val)
|
||||
{
|
||||
return yaffs_mknod_reldir(ROOT_DIR(dev), pathname, mode, dev_val);
|
||||
}
|
||||
|
||||
int yaffs_mknod(const YCHAR *pathname, mode_t mode, dev_t dev_val)
|
||||
{
|
||||
return yaffs_mknod_reldir(NULL, pathname, mode, dev_val);
|
||||
}
|
||||
|
||||
/*
|
||||
* D E B U G F U N C T I O N S
|
||||
*/
|
||||
|
||||
/*
|
||||
* yaffs_n_handles()
|
||||
* Returns number of handles attached to the object
|
||||
*/
|
||||
int yaffs_n_handles_reldir(struct yaffs_obj *reldir, const YCHAR *path)
|
||||
{
|
||||
struct yaffs_obj *obj;
|
||||
|
||||
if (yaffsfs_CheckMemRegion(path, 0, 0) < 0) {
|
||||
yaffsfs_SetError(-EFAULT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (yaffsfs_CheckPath(path) < 0) {
|
||||
yaffsfs_SetError(-ENAMETOOLONG);
|
||||
return -1;
|
||||
}
|
||||
|
||||
obj = yaffsfs_FindObject(reldir, path, 0, 1, NULL, NULL, NULL);
|
||||
|
||||
if (obj)
|
||||
return yaffsfs_CountHandles(obj);
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
int yaffs_n_handles(const YCHAR *path)
|
||||
{
|
||||
return yaffs_n_handles_reldir(NULL, path);
|
||||
}
|
||||
|
||||
int yaffs_get_error(void)
|
||||
{
|
||||
return yaffsfs_GetLastError();
|
||||
}
|
||||
|
||||
int yaffs_set_error(int error)
|
||||
{
|
||||
yaffsfs_SetError(error);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int yaffs_dump_dev_reldir(struct yaffs_obj *reldir, const YCHAR *path)
|
||||
{
|
||||
#if 1
|
||||
(void) path;
|
||||
(void) reldir;
|
||||
#else
|
||||
YCHAR *rest;
|
||||
|
||||
|
||||
if (!reldir)
|
||||
reldir = yaffsfs_FindRoot(path, &rest);
|
||||
|
||||
if (reldir) {
|
||||
struct yaffs_dev *dev = reldir->my_dev;
|
||||
|
||||
printf("\n"
|
||||
"n_page_writes.......... %d\n"
|
||||
"n_page_reads........... %d\n"
|
||||
"n_erasures....... %d\n"
|
||||
"n_gc_copies............ %d\n"
|
||||
"garbageCollections... %d\n"
|
||||
"passiveGarbageColl'ns %d\n"
|
||||
"\n",
|
||||
dev->n_page_writes,
|
||||
dev->n_page_reads,
|
||||
dev->n_erasures,
|
||||
dev->n_gc_copies,
|
||||
dev->garbageCollections, dev->passiveGarbageCollections);
|
||||
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int yaffs_dump_dev(const YCHAR *path)
|
||||
{
|
||||
return yaffs_dump_dev_reldir(NULL, path);
|
||||
}
|
@ -35,6 +35,17 @@ static xSemaphoreHandle mutex = 0;
|
||||
|
||||
struct flashfs_logfs_cfg;
|
||||
|
||||
/**
|
||||
* glue macros for file IO
|
||||
**/
|
||||
#define FILEINFO FILE *
|
||||
#define PIOS_FOPEN_READ(filename, file) (file = fopen((char *)filename, "r")) == NULL
|
||||
#define PIOS_FOPEN_WRITE(filename, file) (file = fopen((char *)filename, "w")) == NULL
|
||||
#define PIOS_FREAD(file, bufferadr, length, resultadr) (*resultadr = fread((uint8_t *)bufferadr, 1, length, *file)) != length
|
||||
#define PIOS_FWRITE(file, bufferadr, length, resultadr) *resultadr = fwrite((uint8_t *)bufferadr, 1, length, *file)
|
||||
#define PIOS_FCLOSE(file) fclose(file)
|
||||
#define PIOS_FUNLINK(file) unlink((char *)filename)
|
||||
|
||||
|
||||
/**
|
||||
* Get an 8 character (plus extension) filename for the object.
|
||||
|
301
flight/pios/common/pios_logfs.c
Normal file
301
flight/pios/common/pios_logfs.c
Normal file
@ -0,0 +1,301 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file pios_logfs.c
|
||||
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2013.
|
||||
* @addtogroup PIOS PIOS Core hardware abstraction layer
|
||||
* @{
|
||||
* @brief Log Structured Filesystem leverages yaffs
|
||||
*****************************************************************************/
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
|
||||
#include "pios.h"
|
||||
#include "pios_stdio.h"
|
||||
#include "pios_trace.h"
|
||||
|
||||
#include <openpilot.h>
|
||||
|
||||
/**
|
||||
* Get an object file name and device name and path name
|
||||
* @param[in] fs_id flash device id
|
||||
* @param[in] obj The object handle.
|
||||
* @param[in] instId The instance ID
|
||||
* @param[in] file Filename string pointer -- must be 14 bytes long and allocated
|
||||
* @param[in] path string pointer -- must be 26 bytes long and allocated
|
||||
*/
|
||||
#define OBJECTPATHNAME_LEN 26
|
||||
static void getObjectPathAndName(uintptr_t fs_id, uint32_t obj_id, uint16_t obj_inst_id, char *filename)
|
||||
{
|
||||
uint32_t prefix = obj_id + (obj_inst_id / 256) * 16; // put upper 8 bit of instance id into object id modification,
|
||||
// skip least sig nibble since that is used for meta object id
|
||||
uint8_t suffix = obj_inst_id & 0xff;
|
||||
|
||||
snprintf((char *)filename, OBJECTPATHNAME_LEN, "/dev%01u/logfs/%08X.o%02X", (unsigned)fs_id, prefix, suffix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an devicename
|
||||
* @param[in] fs_id flash device id
|
||||
* @param[in] devicename string pointer -- must be 8 bytes long and allocated
|
||||
*/
|
||||
#define DEVICENAME_LEN 8
|
||||
static void getDeviceName(uintptr_t fs_id, char *devicename)
|
||||
{
|
||||
snprintf((char *)devicename, DEVICENAME_LEN, "/dev%01u", (unsigned) fs_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an logfs path
|
||||
* @param[in] fs_id flash device id
|
||||
* @param[in] devicename string pointer -- must be 14 bytes long and allocated
|
||||
*/
|
||||
#define LOGFSPATH_LEN 14
|
||||
static void getLogfsPath(uintptr_t fs_id, char *devicename)
|
||||
{
|
||||
snprintf((char *)devicename, LOGFSPATH_LEN, "/dev%01u/logfs", (unsigned)fs_id);
|
||||
}
|
||||
|
||||
//Simplistic API takes the fs_id and maps to a path /dev0 eg. not really necessary
|
||||
// but allows reuse existing API plus new API in parallel without too much conflict
|
||||
|
||||
int32_t PIOS_FLASHFS_Logfs_Destroy(
|
||||
__attribute__((unused)) uintptr_t fs_id)
|
||||
{
|
||||
pios_DIR *dp;
|
||||
struct pios_dirent *ep;
|
||||
char path[LOGFSPATH_LEN];
|
||||
getLogfsPath(fs_id, path);
|
||||
|
||||
pios_trace(PIOS_TRACE_TEST, "PIOS_FLASHFS_Logfs_Destroy");
|
||||
|
||||
|
||||
dp = pios_opendir (path);
|
||||
if (dp != NULL)
|
||||
{
|
||||
while ((ep = pios_readdir(dp))) {
|
||||
pios_unlink (ep->d_name);
|
||||
}
|
||||
(void) pios_closedir (dp);
|
||||
}
|
||||
else
|
||||
{
|
||||
pios_trace(PIOS_TRACE_ERROR, "Couldn't open the directory %s.", path);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**********************************
|
||||
*
|
||||
* Provide a PIOS_FLASHFS_* driver
|
||||
*
|
||||
*********************************/
|
||||
#include "pios_flashfs.h" /* API for flash filesystem */
|
||||
|
||||
/**
|
||||
* @brief Saves one object instance to the filesystem
|
||||
* @param[in] fs_id The filesystem to use for this action
|
||||
* @param[in] obj UAVObject ID of the object to save
|
||||
* @param[in] obj_inst_id The instance number of the object being saved
|
||||
* @param[in] obj_data Contents of the object being saved
|
||||
* @param[in] obj_size Size of the object being saved
|
||||
* @return 0 if success or error code
|
||||
* @retval -1 if fs_id is not a valid filesystem instance
|
||||
* @retval -2 if failed to start transaction
|
||||
* @retval -3 if failure to delete any previous versions of the object
|
||||
* @retval -4 if filesystem is entirely full and garbage collection won't help
|
||||
* @retval -5 if garbage collection failed
|
||||
* @retval -6 if filesystem is full even after garbage collection should have freed space
|
||||
* @retval -7 if writing the new object to the filesystem failed
|
||||
*/
|
||||
int32_t PIOS_FLASHFS_ObjSave(
|
||||
__attribute__((unused)) uintptr_t fs_id,
|
||||
uint32_t obj_id,
|
||||
uint16_t obj_inst_id,
|
||||
uint8_t *obj_data,
|
||||
uint16_t obj_size)
|
||||
{
|
||||
int fd;
|
||||
char filename[OBJECTPATHNAME_LEN];
|
||||
uint32_t bytes_written = 0;
|
||||
|
||||
pios_trace(PIOS_TRACE_TEST, "PIOS_FLASHFS_ObjSave");
|
||||
|
||||
// Get filename
|
||||
getObjectPathAndName(fs_id, obj_id, obj_inst_id, filename);
|
||||
|
||||
// Open file
|
||||
fd = pios_open(filename, O_CREAT | O_RDWR | O_TRUNC, S_IREAD | S_IWRITE);
|
||||
pios_trace(PIOS_TRACE_TEST, "pios_open (%s) retval=%d.", filename, fd);
|
||||
|
||||
if (fd < 0) {
|
||||
pios_trace(PIOS_TRACE_ERROR, "Couldn't open %s", filename);
|
||||
}
|
||||
else {
|
||||
// Append object
|
||||
bytes_written = pios_write(fd, obj_data, obj_size);
|
||||
pios_trace(PIOS_TRACE_TEST, "pios_write bytes_written=%d obj_size=%d", bytes_written, obj_size);
|
||||
|
||||
// Done, close file and unlock
|
||||
pios_close(fd);
|
||||
}
|
||||
|
||||
if (bytes_written != obj_size) {
|
||||
return -7;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Load one object instance from the filesystem
|
||||
* @param[in] fs_id The filesystem to use for this action
|
||||
* @param[in] obj UAVObject ID of the object to load
|
||||
* @param[in] obj_inst_id The instance of the object to load
|
||||
* @param[in] obj_data Buffer to hold the contents of the loaded object
|
||||
* @param[in] obj_size Size of the object to be loaded
|
||||
* @return 0 if success or error code
|
||||
* @retval -1 if fs_id is not a valid filesystem instance
|
||||
* @retval -2 if failed to start transaction
|
||||
* @retval -3 if object not found in filesystem
|
||||
* @retval -4 if object size in filesystem does not exactly match buffer size
|
||||
* @retval -5 if reading the object data from flash fails
|
||||
*/
|
||||
int32_t PIOS_FLASHFS_ObjLoad(
|
||||
__attribute__((unused)) uintptr_t fs_id,
|
||||
uint32_t obj_id,
|
||||
uint16_t obj_inst_id,
|
||||
uint8_t *obj_data,
|
||||
uint16_t obj_size)
|
||||
{
|
||||
int fd;
|
||||
char filename[OBJECTPATHNAME_LEN];
|
||||
uint32_t bytes_read = 0;
|
||||
|
||||
pios_trace(PIOS_TRACE_TEST, "PIOS_FLASHFS_ObjLoad");
|
||||
|
||||
// Get filename
|
||||
getObjectPathAndName(fs_id, obj_id, obj_inst_id, filename);
|
||||
|
||||
fd = pios_open(filename,O_RDONLY, S_IREAD | S_IWRITE);
|
||||
|
||||
pios_trace(PIOS_TRACE_TEST, "pios_open (%s) retval=%d.", filename, fd);
|
||||
|
||||
if (fd < 0) {
|
||||
pios_trace(PIOS_TRACE_ERROR, "Couldn't open %s", filename);
|
||||
}
|
||||
else {
|
||||
// Load object
|
||||
bytes_read = pios_read(fd, (void *)obj_data, (uint32_t)obj_size);
|
||||
pios_trace(PIOS_TRACE_TEST, "pios_read (%d) expected=%d.", obj_size, bytes_read);
|
||||
|
||||
// Done, close file and unlock
|
||||
pios_close(fd);
|
||||
}
|
||||
|
||||
if (bytes_read != obj_size)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Delete one instance of an object from the filesystem
|
||||
* @param[in] fs_id The filesystem to use for this action
|
||||
* @param[in] obj UAVObject ID of the object to delete
|
||||
* @param[in] obj_inst_id The instance of the object to delete
|
||||
* @return 0 if success or error code
|
||||
* @retval -1 if fs_id is not a valid filesystem instance
|
||||
* @retval -2 if failed to start transaction
|
||||
* @retval -3 if failed to delete the object from the filesystem
|
||||
*/
|
||||
int32_t PIOS_FLASHFS_ObjDelete(
|
||||
__attribute__((unused)) uintptr_t fs_id,
|
||||
uint32_t obj_id,
|
||||
uint16_t obj_inst_id)
|
||||
{
|
||||
char filename[OBJECTPATHNAME_LEN];
|
||||
int retval;
|
||||
pios_trace(PIOS_TRACE_TEST, "PIOS_FLASHFS_ObjDelete");
|
||||
|
||||
// Get filename
|
||||
getObjectPathAndName(fs_id, obj_id, obj_inst_id, filename);
|
||||
|
||||
// Delete file
|
||||
retval = pios_unlink((char *)filename);
|
||||
pios_trace(PIOS_TRACE_TEST, "pios_unlink(%s) retval=%d" , filename, retval);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Erases all filesystem arenas and activate the first arena
|
||||
* @param[in] fs_id The filesystem to use for this action
|
||||
* @return 0 if success or error code
|
||||
* @retval -1 if fs_id is not a valid filesystem instance
|
||||
* @retval -2 if failed to start transaction
|
||||
* @retval -3 if failed to erase all arenas
|
||||
* @retval -4 if failed to activate arena 0
|
||||
* @retval -5 if failed to mount arena 0
|
||||
*/
|
||||
int32_t PIOS_FLASHFS_Format(
|
||||
__attribute__((unused)) uintptr_t fs_id)
|
||||
{
|
||||
int retval;
|
||||
pios_trace(PIOS_TRACE_TEST, "PIOS_FLASHFS_Format");
|
||||
|
||||
// Convert fs_id into device name
|
||||
char devicename[DEVICENAME_LEN];
|
||||
getDeviceName(fs_id, devicename);
|
||||
|
||||
retval = pios_format(devicename,
|
||||
TRUE, // unmount flag
|
||||
TRUE, // force unmount flag
|
||||
TRUE); // remount
|
||||
|
||||
pios_trace(PIOS_TRACE_TEST, "pios_format (%s) retval=%d.", devicename, retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returs stats for the filesystems
|
||||
* @param[in] fs_id The filesystem to use for this action
|
||||
* @return 0 if success or error code
|
||||
* @retval -1 if fs_id is not a valid filesystem instance
|
||||
*/
|
||||
int32_t PIOS_FLASHFS_GetStats(
|
||||
__attribute__((unused)) uintptr_t fs_id,
|
||||
__attribute__((unused)) struct PIOS_FLASHFS_Stats *stats)
|
||||
{
|
||||
// pios_trace(PIOS_TRACE_TEST, "PIOS_FLASHFS_GetStats");
|
||||
|
||||
// Convert fs_id into device name
|
||||
char devicename[DEVICENAME_LEN];
|
||||
getDeviceName(fs_id, devicename);
|
||||
|
||||
// Get yaffs statistics for that device
|
||||
stats->num_free_slots = yaffs_freespace(devicename);
|
||||
stats->num_active_slots = yaffs_totalspace(devicename) - stats->num_free_slots;
|
||||
|
||||
// Return device usage statistics
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @}
|
||||
* @}
|
||||
*/
|
45
flight/pios/common/pios_trace.c
Normal file
45
flight/pios/common/pios_trace.c
Normal file
@ -0,0 +1,45 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
*
|
||||
* @file pios_trace.c
|
||||
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2014.
|
||||
* @addtogroup PiOS
|
||||
* @{
|
||||
* @addtogroup PiOS
|
||||
* @{
|
||||
* @brief PiOS trace debug function
|
||||
*****************************************************************************/
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include "pios_trace.h"
|
||||
|
||||
unsigned pios_trace_mask = 0;
|
||||
|
||||
|
||||
unsigned pios_set_trace(unsigned tm)
|
||||
{
|
||||
pios_trace_mask = tm;
|
||||
return pios_trace_mask;
|
||||
}
|
||||
|
||||
unsigned pios_get_trace(void)
|
||||
{
|
||||
return pios_trace_mask;
|
||||
}
|
||||
|
@ -34,6 +34,9 @@ struct PIOS_FLASHFS_Stats {
|
||||
uint16_t num_active_slots; /* slots in active state */
|
||||
};
|
||||
|
||||
// define logfs subdirectory of a yaffs flash device
|
||||
#define PIOS_LOGFS_DIR "logfs"
|
||||
|
||||
int32_t PIOS_FLASHFS_Format(uintptr_t fs_id);
|
||||
int32_t PIOS_FLASHFS_ObjSave(uintptr_t fs_id, uint32_t obj_id, uint16_t obj_inst_id, uint8_t *obj_data, uint16_t obj_size);
|
||||
int32_t PIOS_FLASHFS_ObjLoad(uintptr_t fs_id, uint32_t obj_id, uint16_t obj_inst_id, uint8_t *obj_data, uint16_t obj_size);
|
||||
|
93
flight/pios/inc/pios_stdio.h
Normal file
93
flight/pios/inc/pios_stdio.h
Normal file
@ -0,0 +1,93 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
*
|
||||
* @file pios_stdio.h
|
||||
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2014.
|
||||
* @addtogroup PiOS
|
||||
* @{
|
||||
* @addtogroup PiOS
|
||||
* @{
|
||||
* @brief PiOS stdio posix file functions
|
||||
*****************************************************************************/
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef PIOS_STDIO_H
|
||||
#define PIOS_STDIO_H
|
||||
|
||||
#include "yaffsfs.h"
|
||||
|
||||
#define pios_open(path, oflag, mode) yaffs_open(path, oflag, mode)
|
||||
#define pios_close(fd) yaffs_close(fd)
|
||||
#define pios_fsync(fd) yaffs_fsync(fd)
|
||||
#define pios_flush(fd) yaffs_flush(fd)
|
||||
#define pios_read(fd, buf, nbyte) yaffs_read(fd, buf, nbyte)
|
||||
#define pios_write(fd, buf, nbyte) yaffs_write(fd, buf, nbyte)
|
||||
#define pios_fdatasync(fd) yaffs_fdatasync(fd)
|
||||
#define pios_access(path, amode) yaffs_access(path, amode)
|
||||
#define pios_dup(fd) yaffs_dup(fd)
|
||||
#define pios_pread(fd, buf, nbyte, offset) yaffs_pread(fd, buf, nbyte, offset)
|
||||
#define pios_pwrite(fd, buf, nbyte, offset) yaffs_pwrite(fd, buf, nbyte, offset)
|
||||
#define pios_lseek(fd, offset, whence) yaffs_lseek(fd, offset, whence)
|
||||
#define pios_truncate(path, new_size) yaffs_truncate(path, new_size)
|
||||
#define pios_ftruncate(fd, new_size) yaffs_ftruncate(fd, new_size)
|
||||
#define pios_unlink(path) yaffs_unlink(path)
|
||||
#define pios_rename(oldPath, newPath) yaffs_rename(oldPath, newPath)
|
||||
#define pios_stat(path, buf) yaffs_stat(path, buf)
|
||||
#define pios_lstat(path, buf) yaffs_lstat(path, buf)
|
||||
#define pios_fstat(fd, buf) yaffs_fstat(fd, buf)
|
||||
#define pios_utime(path, buf) yaffs_utime(path, buf)
|
||||
#define pios_futime(fd, buf) yaffs_futime(fd, buf)
|
||||
#define pios_setxattr(path, name, data, size, flags) yaffs_setxattr(path, name, data, size, flags)
|
||||
#define pios_lsetxattr(path, name, data, size, flags) yaffs_lsetxattr(path, name, data, size, flags)
|
||||
#define pios_fsetxattr(fd, name, data, size, flags) yaffs_fsetxattr(path, name, data, size, flags)
|
||||
|
||||
#define pios_getxattr(path, name, data, size) yaffs_getxattr(path, name, data, size)
|
||||
#define pios_lgetxattr(path, name, data, size) yaffs_lgetxattr(path, name, data, size)
|
||||
#define pios_fgetxattr(fd, name, data, size) yaffs_fgetxattr(fd, name, data, size)
|
||||
#define pios_removexattr(path, name) yaffs_removexattr(path, name)
|
||||
#define pios_lremoveattr(path, name) yaffs_lremovexattr(path, name)
|
||||
#define pios_fremovexattr(fd, name) yaffs_fremovexattr(fd, name)
|
||||
#define pios_listxattr(path, list, size) yaffs_listxattr(path, list, size)
|
||||
#define pios_llistxattr(path, list,size) yaffs_llistxattr(path, list, size)
|
||||
#define pios_flistxattr(fd, list, size) yaffs_flistxattr(fd, list, size)
|
||||
#define pios_chmod(path, mode) yaffs_chmod(path, mode)
|
||||
#define pios_fchmod(fd, mode) yaffs_fchmod(fd, mode)
|
||||
#define pios_mkdir(path, mode) yaffs_mkdir(path, mode)
|
||||
#define pios_rmdir(path) yaffs_rmdir(path)
|
||||
#define pios_opendir(dirname) yaffs_opendir(dirname)
|
||||
#define pios_readdir(dirp) yaffs_readdir(dirp)
|
||||
#define pios_rewinddir(dirp) yaffs_rewinddir(dirp)
|
||||
#define pios_closedir(dirp) yaffs_closedir(dirp)
|
||||
#define pios_mount(path) yaffs_mount(path)
|
||||
#define pios_mount2(path, read_only) yaffs_mount2(path, read_only)
|
||||
#define pios_mount3(path, read_only, skip_checkpt) yaffs_mount3(path, read_only, skip_checkpt)
|
||||
#define pios_umount(path) yaffs_unmount(path)
|
||||
#define pios_umount2(path, force) yaffs_unmount2(path, force)
|
||||
#define pios_remount(path, force, read_only) yaffs_remount(path, force,read_only)
|
||||
#define pios_format(path, unmount_flag, force_unmount_flag, remount_flag) yaffs_format(path, unmount_flag, force_unmount_flag, remount_flag)
|
||||
#define pios_sync(path) yaffs_sync(path)
|
||||
#define pios_symlink(oldpath, newpath) yaffs_symlink(oldpath, newpath)
|
||||
#define pios_readlink(path, buf, bufsize) yaffs_readlink(path, buf, bufsiz)
|
||||
#define pios_link(oldpath, newpath) yaffs_link(oldpath, newpath)
|
||||
#define pios_mknod(pathname, mode, dev) yaffs_mknod(pathname, mode, dev)
|
||||
#define pios_freespace(path) yaffs_freespace(path)
|
||||
#define pios_totalspace(path) yaffs_totalspace(path)
|
||||
|
||||
#define pios_DIR yaffs_DIR
|
||||
#define pios_dirent yaffs_dirent
|
||||
|
||||
|
||||
#endif /* PIOS_STDIO_H */
|
83
flight/pios/inc/pios_trace.h
Normal file
83
flight/pios/inc/pios_trace.h
Normal file
@ -0,0 +1,83 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
*
|
||||
* @file pios_trace.h
|
||||
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2014.
|
||||
* @addtogroup PiOS
|
||||
* @{
|
||||
* @addtogroup PiOS
|
||||
* @{
|
||||
* @brief PiOS debug trace printing interface
|
||||
*****************************************************************************/
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef PIOS_TRACE_H
|
||||
#define PIOS_TRACE_H
|
||||
|
||||
extern unsigned int pios_trace_mask;
|
||||
|
||||
/*
|
||||
* Tracing flags.
|
||||
* The flags masked in PIOS_TRACE_ALWAYS are always traced.
|
||||
*/
|
||||
|
||||
#define PIOS_TRACE_OS 0x00000002
|
||||
#define PIOS_TRACE_ALLOCATE 0x00000004
|
||||
#define PIOS_TRACE_TEST 0x00000008
|
||||
#define PIOS_TRACE_BAD_BLOCKS 0x00000010
|
||||
#define PIOS_TRACE_ERASE 0x00000020
|
||||
#define PIOS_TRACE_GC 0x00000040
|
||||
#define PIOS_TRACE_WRITE 0x00000080
|
||||
#define PIOS_TRACE_TRACING 0x00000100
|
||||
#define PIOS_TRACE_DELETION 0x00000200
|
||||
#define PIOS_TRACE_BUFFERS 0x00000400
|
||||
#define PIOS_TRACE_NANDACCESS 0x00000800
|
||||
#define PIOS_TRACE_GC_DETAIL 0x00001000
|
||||
#define PIOS_TRACE_SCAN_DEBUG 0x00002000
|
||||
#define PIOS_TRACE_MTD 0x00004000
|
||||
#define PIOS_TRACE_CHECKPOINT 0x00008000
|
||||
|
||||
#define PIOS_TRACE_VERIFY 0x00010000
|
||||
#define PIOS_TRACE_VERIFY_NAND 0x00020000
|
||||
#define PIOS_TRACE_VERIFY_FULL 0x00040000
|
||||
#define PIOS_TRACE_VERIFY_ALL 0x000f0000
|
||||
|
||||
#define PIOS_TRACE_SYNC 0x00100000
|
||||
#define PIOS_TRACE_BACKGROUND 0x00200000
|
||||
#define PIOS_TRACE_LOCK 0x00400000
|
||||
#define PIOS_TRACE_MOUNT 0x00800000
|
||||
|
||||
#define PIOS_TRACE_ERROR 0x40000000
|
||||
#define PIOS_TRACE_BUG 0x80000000
|
||||
#define PIOS_TRACE_ALWAYS 0xf0000000
|
||||
|
||||
|
||||
|
||||
#ifdef PIOS_TRACE
|
||||
#define pios_trace(msk, fmt, ...) do { \
|
||||
if (pios_trace_mask & (msk)) \
|
||||
printf("pios_trace: " fmt "\n", ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
#else
|
||||
#define pios_trace(msk, fmt, ...)
|
||||
#endif
|
||||
|
||||
/* Trace control functions */
|
||||
unsigned pios_set_trace(unsigned tm);
|
||||
unsigned pios_get_trace(void);
|
||||
|
||||
|
||||
#endif
|
27
flight/pios/posix/libraries/yaffs2/yaffs_impl.mk
Normal file
27
flight/pios/posix/libraries/yaffs2/yaffs_impl.mk
Normal file
@ -0,0 +1,27 @@
|
||||
#
|
||||
# Rules to add yaffs2 to the PiOS target
|
||||
#
|
||||
#
|
||||
# Note that the PIOS target-specific makefile will detect that YAFFS2_DIR
|
||||
# has been defined and add in the target-specific pieces separately.
|
||||
#
|
||||
|
||||
|
||||
#
|
||||
# Directory containing this makefile
|
||||
#
|
||||
YAFFS2_SIMPOSIX_DIR := $(dir $(lastword $(MAKEFILE_LIST)))
|
||||
|
||||
# Compiler options
|
||||
#
|
||||
CDEFS += -DCONFIG_YAFFS_DIRECT
|
||||
CDEFS += -DCONFIG_YAFFS_DEFINES_TYPES
|
||||
CDEFS += -DCONFIG_YAFFS_PROVIDE_DEFS
|
||||
CDEFS += -DCONFIG_YAFFSFS_PROVIDE_VALUES
|
||||
#ARCHFLAGS += -DARCH_POSIX
|
||||
|
||||
#
|
||||
# Yaffs2 device library source and includes
|
||||
#
|
||||
SRC += $(sort $(wildcard $(YAFFS2_SIMPOSIX_DIR)*.c))
|
||||
#EXTRAINCDIRS += $(YAFFS2_DIR)/inc
|
347
flight/pios/posix/libraries/yaffs2/yaffs_nor_drv.c
Normal file
347
flight/pios/posix/libraries/yaffs2/yaffs_nor_drv.c
Normal file
@ -0,0 +1,347 @@
|
||||
/*
|
||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is an interface module for handling NOR in yaffs1 mode.
|
||||
*/
|
||||
|
||||
/* This code is intended to be used with "regular" NOR that is bit-modifyable.
|
||||
*
|
||||
* Each "chunk" is a contguous area of 512 + 16 bytes.
|
||||
* This makes 248 such chunks with some space left over where a format markerr
|
||||
* is stored.
|
||||
*/
|
||||
|
||||
#include "yaffs_nor_drv.h"
|
||||
|
||||
#include "yportenv.h"
|
||||
#include "yaffs_trace.h"
|
||||
|
||||
#include "yaffs_flashif.h"
|
||||
#include "yaffs_guts.h"
|
||||
|
||||
/* Tunable data */
|
||||
#define DATA_BYTES_PER_CHUNK 512
|
||||
#define SPARE_BYTES_PER_CHUNK 16
|
||||
#define BLOCK_SIZE_IN_BYTES (128*1024)
|
||||
#define BYTES_IN_DEVICE (4*1024*1024)
|
||||
|
||||
#define BYTES_PER_CHUNK (DATA_BYTES_PER_CHUNK + SPARE_BYTES_PER_CHUNK)
|
||||
#define SPARE_AREA_OFFSET DATA_BYTES_PER_CHUNK
|
||||
#define CHUNKS_PER_BLOCK (BLOCK_SIZE_IN_BYTES/BYTES_PER_CHUNK)
|
||||
|
||||
#define BLOCKS_IN_DEVICE (BYTES_IN_DEVICE/BLOCK_SIZE_IN_BYTES)
|
||||
|
||||
#define YNOR_PREMARKER (0xF6)
|
||||
#define YNOR_POSTMARKER (0xF0)
|
||||
|
||||
#define FORMAT_OFFSET (CHUNKS_PER_BLOCK * BYTES_PER_CHUNK)
|
||||
#define FORMAT_VALUE 0x1234
|
||||
|
||||
#if 1
|
||||
|
||||
/* Compile this for a simulation */
|
||||
#include "ynorsim.h"
|
||||
|
||||
static struct nor_sim *nor_sim;
|
||||
|
||||
#define nor_drv_FlashInit() do {nor_sim = ynorsim_initialise("emfile-nor", BLOCKS_IN_DEVICE, BLOCK_SIZE_IN_BYTES); } while(0)
|
||||
#define nor_drv_FlashDeinit() ynorsim_shutdown(nor_sim)
|
||||
#define nor_drv_FlashWrite32(addr,buf,nwords) ynorsim_wr32(nor_sim,addr,buf,nwords)
|
||||
#define nor_drv_FlashRead32(addr,buf,nwords) ynorsim_rd32(nor_sim,addr,buf,nwords)
|
||||
#define nor_drv_FlashEraseBlock(addr) ynorsim_erase(nor_sim,addr)
|
||||
#define DEVICE_BASE ynorsim_get_base(nor_sim)
|
||||
|
||||
#else
|
||||
|
||||
/* Compile this to hook up to read hardware */
|
||||
#include "../blob/yflashrw.h"
|
||||
#define nor_drv_FlashInit() do{} while(0)
|
||||
#define nor_drv_FlashDeinit() do {} while(0)
|
||||
#define nor_drv_FlashWrite32(addr,buf,nwords) Y_FlashWrite(addr,buf,nwords)
|
||||
#define nor_drv_FlashRead32(addr,buf,nwords) Y_FlashRead(addr,buf,nwords)
|
||||
#define nor_drv_FlashEraseBlock(addr) Y_FlashErase(addr,BLOCK_SIZE_IN_BYTES)
|
||||
#define DEVICE_BASE (32 * 1024 * 1024)
|
||||
#endif
|
||||
|
||||
|
||||
static u32 *Block2Addr(struct yaffs_dev *dev, int blockNumber)
|
||||
{
|
||||
u8 *addr;
|
||||
|
||||
dev=dev;
|
||||
|
||||
addr = (u8*)DEVICE_BASE;
|
||||
addr += blockNumber * BLOCK_SIZE_IN_BYTES;
|
||||
|
||||
return (u32 *) addr;
|
||||
}
|
||||
|
||||
static u32 *Block2FormatAddr(struct yaffs_dev *dev, int blockNumber)
|
||||
{
|
||||
u8 *addr;
|
||||
|
||||
addr = (u8*) Block2Addr(dev,blockNumber);
|
||||
addr += FORMAT_OFFSET;
|
||||
|
||||
return (u32 *)addr;
|
||||
}
|
||||
|
||||
static u32 *Chunk2DataAddr(struct yaffs_dev *dev, int chunk_id)
|
||||
{
|
||||
unsigned block;
|
||||
unsigned chunkInBlock;
|
||||
u8 *addr;
|
||||
|
||||
block = chunk_id/dev->param.chunks_per_block;
|
||||
chunkInBlock = chunk_id % dev->param.chunks_per_block;
|
||||
|
||||
addr = (u8*) Block2Addr(dev,block);
|
||||
addr += chunkInBlock * BYTES_PER_CHUNK;
|
||||
|
||||
return (u32 *)addr;
|
||||
}
|
||||
|
||||
static u32 *Chunk2SpareAddr(struct yaffs_dev *dev, int chunk_id)
|
||||
{
|
||||
u8 *addr;
|
||||
|
||||
addr = (u8*) Chunk2DataAddr(dev, chunk_id);
|
||||
addr += SPARE_AREA_OFFSET;
|
||||
return (u32 *)addr;
|
||||
}
|
||||
|
||||
|
||||
static void nor_drv_AndBytes(u8*target, const u8 *src, int nbytes)
|
||||
{
|
||||
while(nbytes > 0){
|
||||
*target &= *src;
|
||||
target++;
|
||||
src++;
|
||||
nbytes--;
|
||||
}
|
||||
}
|
||||
|
||||
static int nor_drv_WriteChunkToNAND(struct yaffs_dev *dev,int nand_chunk,
|
||||
const u8 *data, int data_len,
|
||||
const u8 *oob, int oob_len)
|
||||
{
|
||||
u32 *dataAddr = Chunk2DataAddr(dev,nand_chunk);
|
||||
u32 *spareAddr = Chunk2SpareAddr(dev,nand_chunk);
|
||||
|
||||
struct yaffs_spare *spare = (struct yaffs_spare *)oob;
|
||||
struct yaffs_spare tmpSpare;
|
||||
|
||||
(void) oob_len;
|
||||
|
||||
/* We should only be getting called for one of 3 reasons:
|
||||
* Writing a chunk: data and spare will not be NULL
|
||||
* Writing a deletion marker: data will be NULL, spare not NULL
|
||||
* Writing a bad block marker: data will be NULL, spare not NULL
|
||||
*/
|
||||
|
||||
if(sizeof(struct yaffs_spare) != SPARE_BYTES_PER_CHUNK)
|
||||
BUG();
|
||||
|
||||
if(data && oob) {
|
||||
if(spare->page_status != 0xff)
|
||||
BUG();
|
||||
/* Write a pre-marker */
|
||||
memset(&tmpSpare,0xff,sizeof(tmpSpare));
|
||||
tmpSpare.page_status = YNOR_PREMARKER;
|
||||
nor_drv_FlashWrite32(spareAddr,(u32 *)&tmpSpare,sizeof(struct yaffs_spare)/sizeof(u32));
|
||||
|
||||
/* Write the data */
|
||||
nor_drv_FlashWrite32(dataAddr,(u32 *)data, data_len/ sizeof(u32));
|
||||
|
||||
memcpy(&tmpSpare,spare,sizeof(struct yaffs_spare));
|
||||
|
||||
/* Write the real tags, but override the premarker*/
|
||||
tmpSpare.page_status = YNOR_PREMARKER;
|
||||
nor_drv_FlashWrite32(spareAddr,(u32 *)&tmpSpare,sizeof(struct yaffs_spare)/sizeof(u32));
|
||||
|
||||
/* Write a post-marker */
|
||||
tmpSpare.page_status = YNOR_POSTMARKER;
|
||||
nor_drv_FlashWrite32(spareAddr,(u32 *)&tmpSpare,sizeof(tmpSpare)/sizeof(u32));
|
||||
|
||||
} else if(spare){
|
||||
/* This has to be a read-modify-write operation to handle NOR-ness */
|
||||
|
||||
nor_drv_FlashRead32(spareAddr,(u32 *)&tmpSpare,sizeof(struct yaffs_spare)/sizeof(u32));
|
||||
|
||||
nor_drv_AndBytes((u8 *)&tmpSpare,(u8 *)spare,sizeof(struct yaffs_spare));
|
||||
|
||||
nor_drv_FlashWrite32(spareAddr,(u32 *)&tmpSpare,sizeof(struct yaffs_spare)/sizeof(u32));
|
||||
} else {
|
||||
BUG();
|
||||
}
|
||||
|
||||
return YAFFS_OK;
|
||||
}
|
||||
|
||||
static int nor_drv_ReadChunkFromNAND(struct yaffs_dev *dev,int nand_chunk,
|
||||
u8 *data, int data_len,
|
||||
u8 *oob, int oob_len,
|
||||
enum yaffs_ecc_result *ecc_result)
|
||||
{
|
||||
struct yaffs_spare *spare = (struct yaffs_spare *)oob;
|
||||
|
||||
u32 *dataAddr = Chunk2DataAddr(dev,nand_chunk);
|
||||
u32 *spareAddr = Chunk2SpareAddr(dev,nand_chunk);
|
||||
|
||||
if (data) {
|
||||
nor_drv_FlashRead32(dataAddr,(u32 *)data,dev->param.total_bytes_per_chunk / sizeof(u32));
|
||||
}
|
||||
|
||||
if (oob) {
|
||||
nor_drv_FlashRead32(spareAddr,(u32 *)spare, oob_len/ sizeof(u32));
|
||||
|
||||
/* If the page status is YNOR_POSTMARKER then it was written properly
|
||||
* so change that to 0xFF so that the rest of yaffs is happy.
|
||||
*/
|
||||
if(spare->page_status == YNOR_POSTMARKER)
|
||||
spare->page_status = 0xff;
|
||||
else if(spare->page_status != 0xff &&
|
||||
(spare->page_status | YNOR_PREMARKER) != 0xff)
|
||||
spare->page_status = YNOR_PREMARKER;
|
||||
}
|
||||
|
||||
if(ecc_result)
|
||||
*ecc_result = YAFFS_ECC_RESULT_NO_ERROR;
|
||||
|
||||
return YAFFS_OK;
|
||||
|
||||
}
|
||||
|
||||
|
||||
static int nor_drv_FormatBlock(struct yaffs_dev *dev, int blockNumber)
|
||||
{
|
||||
u32 *blockAddr = Block2Addr(dev,blockNumber);
|
||||
u32 *formatAddr = Block2FormatAddr(dev,blockNumber);
|
||||
u32 formatValue = FORMAT_VALUE;
|
||||
|
||||
nor_drv_FlashEraseBlock(blockAddr);
|
||||
nor_drv_FlashWrite32(formatAddr,&formatValue,1);
|
||||
|
||||
return YAFFS_OK;
|
||||
}
|
||||
|
||||
static int nor_drv_UnformatBlock(struct yaffs_dev *dev, int blockNumber)
|
||||
{
|
||||
u32 *formatAddr = Block2FormatAddr(dev,blockNumber);
|
||||
u32 formatValue = 0;
|
||||
|
||||
nor_drv_FlashWrite32(formatAddr,&formatValue,1);
|
||||
|
||||
return YAFFS_OK;
|
||||
}
|
||||
|
||||
static int nor_drv_IsBlockFormatted(struct yaffs_dev *dev, int blockNumber)
|
||||
{
|
||||
u32 *formatAddr = Block2FormatAddr(dev,blockNumber);
|
||||
u32 formatValue;
|
||||
|
||||
|
||||
nor_drv_FlashRead32(formatAddr,&formatValue,1);
|
||||
|
||||
return (formatValue == FORMAT_VALUE);
|
||||
}
|
||||
|
||||
static int nor_drv_EraseBlockInNAND(struct yaffs_dev *dev, int blockNumber)
|
||||
{
|
||||
|
||||
if(blockNumber < 0 || blockNumber >= BLOCKS_IN_DEVICE)
|
||||
{
|
||||
yaffs_trace(YAFFS_TRACE_ALWAYS,
|
||||
"Attempt to erase non-existant block %d\n",
|
||||
blockNumber);
|
||||
return YAFFS_FAIL;
|
||||
}
|
||||
else
|
||||
{
|
||||
nor_drv_UnformatBlock(dev,blockNumber);
|
||||
nor_drv_FormatBlock(dev,blockNumber);
|
||||
return YAFFS_OK;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static int nor_drv_InitialiseNAND(struct yaffs_dev *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
nor_drv_FlashInit();
|
||||
/* Go through the blocks formatting them if they are not formatted */
|
||||
for(i = dev->param.start_block; i <= dev->param.end_block; i++){
|
||||
if(!nor_drv_IsBlockFormatted(dev,i)){
|
||||
nor_drv_FormatBlock(dev,i);
|
||||
}
|
||||
}
|
||||
return YAFFS_OK;
|
||||
}
|
||||
|
||||
static int nor_drv_Deinitialise_flash_fn(struct yaffs_dev *dev)
|
||||
{
|
||||
dev=dev;
|
||||
|
||||
nor_drv_FlashDeinit();
|
||||
|
||||
return YAFFS_OK;
|
||||
}
|
||||
|
||||
|
||||
struct yaffs_dev *yaffs_nor_install_drv(const char *name)
|
||||
{
|
||||
|
||||
struct yaffs_dev *dev = malloc(sizeof(struct yaffs_dev));
|
||||
char *name_copy = strdup(name);
|
||||
struct yaffs_param *param;
|
||||
struct yaffs_driver *drv;
|
||||
|
||||
|
||||
if(!dev || !name_copy) {
|
||||
free(name_copy);
|
||||
free(dev);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
param = &dev->param;
|
||||
drv = &dev->drv;
|
||||
|
||||
memset(dev, 0, sizeof(*dev));
|
||||
|
||||
param->name = name_copy;
|
||||
|
||||
param->total_bytes_per_chunk = DATA_BYTES_PER_CHUNK;
|
||||
param->chunks_per_block = CHUNKS_PER_BLOCK;
|
||||
param->n_reserved_blocks = 2;
|
||||
param->start_block = 0; // Can use block 0
|
||||
param->end_block = BLOCKS_IN_DEVICE - 1; // Last block
|
||||
param->use_nand_ecc = 0; // use YAFFS's ECC
|
||||
param->disable_soft_del = 1;
|
||||
|
||||
drv->drv_write_chunk_fn = nor_drv_WriteChunkToNAND;
|
||||
drv->drv_read_chunk_fn = nor_drv_ReadChunkFromNAND;
|
||||
drv->drv_erase_fn = nor_drv_EraseBlockInNAND;
|
||||
drv->drv_initialise_fn = nor_drv_InitialiseNAND;
|
||||
drv->drv_deinitialise_fn = nor_drv_Deinitialise_flash_fn;
|
||||
|
||||
param->n_caches = 10;
|
||||
param->disable_soft_del = 1;
|
||||
|
||||
dev->driver_context = (void *) nor_sim;
|
||||
|
||||
yaffs_add_device(dev);
|
||||
|
||||
return NULL;
|
||||
}
|
25
flight/pios/posix/libraries/yaffs2/yaffs_nor_drv.h
Normal file
25
flight/pios/posix/libraries/yaffs2/yaffs_nor_drv.h
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __YAFFS_NOR_DRV_H__
|
||||
#define __YAFFS_NOR_DRV_H__
|
||||
|
||||
struct yaffs_dev;
|
||||
struct yaffs_dev *yaffs_nor_install_drv(const char *name);
|
||||
|
||||
#endif
|
||||
|
||||
|
217
flight/pios/posix/libraries/yaffs2/yaffs_osglue.c
Normal file
217
flight/pios/posix/libraries/yaffs2/yaffs_osglue.c
Normal file
@ -0,0 +1,217 @@
|
||||
/*
|
||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Example OS glue functions for running on a Linux/POSIX system.
|
||||
*/
|
||||
|
||||
#include "yaffscfg.h"
|
||||
#include "yaffs_guts.h"
|
||||
#include "yaffsfs.h"
|
||||
#include "yaffs_trace.h"
|
||||
#include <assert.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "pios_trace.h"
|
||||
#include "yaffsfs.h"
|
||||
|
||||
/*
|
||||
* yaffsfs_SetError() and yaffsfs_GetError()
|
||||
* Do whatever to set the system error.
|
||||
* yaffsfs_GetError() just fetches the last error.
|
||||
*/
|
||||
|
||||
static int yaffsfs_lastError;
|
||||
|
||||
void yaffsfs_SetError(int err)
|
||||
{
|
||||
//Do whatever to set error
|
||||
yaffsfs_lastError = err;
|
||||
errno = err;
|
||||
pios_trace(PIOS_TRACE_ERROR, "yaffsfs_SetError(%d) %s", err, yaffs_error_to_str(err) );
|
||||
}
|
||||
|
||||
int yaffsfs_GetLastError(void)
|
||||
{
|
||||
return yaffsfs_lastError;
|
||||
}
|
||||
|
||||
/*
|
||||
* yaffsfs_CheckMemRegion()
|
||||
* Check that access to an address is valid.
|
||||
* This can check memory is in bounds and is writable etc.
|
||||
*
|
||||
* Returns 0 if ok, negative if not.
|
||||
*/
|
||||
int yaffsfs_CheckMemRegion(const void *addr, size_t size, int write_request)
|
||||
{
|
||||
if(!addr)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* yaffsfs_Lock()
|
||||
* yaffsfs_Unlock()
|
||||
* A single mechanism to lock and unlock yaffs. Hook up to a mutex or whatever.
|
||||
* Here are two examples, one using POSIX pthreads, the other doing nothing.
|
||||
*
|
||||
* If we use pthreads then we also start a background gc thread.
|
||||
*/
|
||||
|
||||
#if 1
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
static pthread_mutex_t mutex1;
|
||||
static pthread_t bc_gc_thread;
|
||||
|
||||
void yaffsfs_Lock(void)
|
||||
{
|
||||
pthread_mutex_lock( &mutex1 );
|
||||
}
|
||||
|
||||
void yaffsfs_Unlock(void)
|
||||
{
|
||||
pthread_mutex_unlock( &mutex1 );
|
||||
}
|
||||
|
||||
static void *bg_gc_func(void *dummy)
|
||||
{
|
||||
struct yaffs_dev *dev;
|
||||
int urgent = 0;
|
||||
int result;
|
||||
int next_urgent;
|
||||
|
||||
/* Sleep for a bit to allow start up */
|
||||
sleep(2);
|
||||
|
||||
|
||||
while (1) {
|
||||
/* Iterate through devices, do bg gc updating ungency */
|
||||
yaffs_dev_rewind();
|
||||
next_urgent = 0;
|
||||
|
||||
while ((dev = yaffs_next_dev()) != NULL) {
|
||||
result = yaffs_do_background_gc_reldev(dev, urgent);
|
||||
if (result > 0)
|
||||
next_urgent = 1;
|
||||
}
|
||||
|
||||
urgent = next_urgent;
|
||||
|
||||
if (next_urgent)
|
||||
sleep(1);
|
||||
else
|
||||
sleep(5);
|
||||
}
|
||||
|
||||
/* Don't ever return. */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void yaffsfs_LockInit(void)
|
||||
{
|
||||
/* Initialise lock */
|
||||
pthread_mutex_init(&mutex1, NULL);
|
||||
|
||||
/* Sneak in starting a background gc thread too */
|
||||
// pthread_create(&bc_gc_thread, NULL, bg_gc_func, NULL);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void yaffsfs_Lock(void)
|
||||
{
|
||||
}
|
||||
|
||||
void yaffsfs_Unlock(void)
|
||||
{
|
||||
}
|
||||
|
||||
void yaffsfs_LockInit(void)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* yaffsfs_CurrentTime() retrns a 32-bit timestamp.
|
||||
*
|
||||
* Can return 0 if your system does not care about time.
|
||||
*/
|
||||
|
||||
u32 yaffsfs_CurrentTime(void)
|
||||
{
|
||||
return time(NULL);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* yaffsfs_malloc()
|
||||
* yaffsfs_free()
|
||||
*
|
||||
* Functions to allocate and free memory.
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_YAFFS_TEST_MALLOC
|
||||
|
||||
static int yaffs_kill_alloc = 0;
|
||||
static size_t total_malloced = 0;
|
||||
static size_t malloc_limit = 0 & 6000000;
|
||||
|
||||
void *yaffsfs_malloc(size_t size)
|
||||
{
|
||||
void * this;
|
||||
if(yaffs_kill_alloc)
|
||||
return NULL;
|
||||
if(malloc_limit && malloc_limit <(total_malloced + size) )
|
||||
return NULL;
|
||||
|
||||
this = malloc(size);
|
||||
if(this)
|
||||
total_malloced += size;
|
||||
return this;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void *yaffsfs_malloc(size_t size)
|
||||
{
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void yaffsfs_free(void *ptr)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
void yaffsfs_OSInitialisation(void)
|
||||
{
|
||||
yaffsfs_LockInit();
|
||||
}
|
||||
|
||||
/*
|
||||
* yaffs_bug_fn()
|
||||
* Function to report a bug.
|
||||
*/
|
||||
|
||||
void yaffs_bug_fn(const char *file_name, int line_no)
|
||||
{
|
||||
printf("yaffs bug detected %s:%d\n",
|
||||
file_name, line_no);
|
||||
assert(0);
|
||||
}
|
194
flight/pios/posix/libraries/yaffs2/yaffscfg2k.c
Normal file
194
flight/pios/posix/libraries/yaffs2/yaffscfg2k.c
Normal file
@ -0,0 +1,194 @@
|
||||
/*
|
||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
/*
|
||||
* yaffscfg2k.c The configuration for the "direct" use of yaffs.
|
||||
*
|
||||
* This file is intended to be modified to your requirements.
|
||||
* There is no need to redistribute this file.
|
||||
*/
|
||||
|
||||
#include "yaffscfg.h"
|
||||
#include "yaffs_guts.h"
|
||||
#include "yaffsfs.h"
|
||||
#include "yaffs_trace.h"
|
||||
#include "yaffs_osglue.h"
|
||||
|
||||
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
|
||||
|
||||
#include "pios.h"
|
||||
#include "pios_trace.h"
|
||||
#include "pios_stdio.h"
|
||||
|
||||
#include <openpilot.h>
|
||||
#include "pios_flashfs.h" /* API for flash filesystem */
|
||||
#include "pios_flashfs_logfs_priv.h"
|
||||
|
||||
#include <pios_stdio.h>
|
||||
|
||||
|
||||
unsigned yaffs_trace_mask = 0;
|
||||
//YAFFS_TRACE_ERROR |
|
||||
//YAFFS_TRACE_BUG |
|
||||
//YAFFS_TRACE_ALWAYS |
|
||||
//0;
|
||||
|
||||
int random_seed;
|
||||
int simulate_power_failure = 0;
|
||||
static unsigned int pios_flash_device_count=0;
|
||||
|
||||
|
||||
/* Configure the devices that will be used */
|
||||
|
||||
#include "yaffs_nor_drv.h"
|
||||
|
||||
// called first before device device driver setup so need to change
|
||||
int yaffs_start_up(void)
|
||||
{
|
||||
static int start_up_called = 0;
|
||||
|
||||
if(start_up_called)
|
||||
return 0;
|
||||
start_up_called = 1;
|
||||
|
||||
/* Call the OS initialisation (eg. set up lock semaphore */
|
||||
yaffsfs_OSInitialisation();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void yaffsSigHandler ( int sig)
|
||||
{
|
||||
char devicename[8];
|
||||
int fs_id;
|
||||
|
||||
pios_trace(PIOS_TRACE_TEST, "yaffsSigHandler sig=%d", sig);
|
||||
switch (sig)
|
||||
{
|
||||
case SIGQUIT:
|
||||
case SIGTERM:
|
||||
case SIGKILL:
|
||||
case SIGINT:
|
||||
|
||||
for (fs_id =0; fs_id < pios_flash_device_count; fs_id++)
|
||||
{
|
||||
snprintf(devicename,6, "/dev%01u", (unsigned) fs_id);
|
||||
|
||||
pios_umount((const char *)devicename);
|
||||
|
||||
}
|
||||
pios_flash_device_count=0;
|
||||
exit(1);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static void yaffsSigSetup
|
||||
(
|
||||
void (*sighandler)(int sig)
|
||||
)
|
||||
{
|
||||
//sigset_t block_sigusr;
|
||||
struct sigaction sa;
|
||||
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = 0;
|
||||
sa.sa_handler = sighandler;
|
||||
if (sigaction(SIGQUIT, &sa, NULL)) return;
|
||||
|
||||
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = 0;
|
||||
sa.sa_handler = sighandler;
|
||||
if (sigaction(SIGINT , &sa, NULL)) return;
|
||||
|
||||
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = 0;
|
||||
sa.sa_handler = sighandler;
|
||||
if (sigaction(SIGTERM, &sa, NULL)) return;
|
||||
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = 0;
|
||||
sa.sa_handler = sighandler;
|
||||
if (sigaction(SIGKILL, &sa, NULL)) return;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Initialize the flash object setting FS. Each call creates a yaffs device
|
||||
* @return 0 if success, -1 if failure
|
||||
*/
|
||||
int32_t PIOS_FLASHFS_Logfs_Init(
|
||||
__attribute__((unused)) uintptr_t *fs_id, // return identifier for fs device
|
||||
__attribute__((unused)) const struct flashfs_logfs_cfg *cfg, //optional - if flash
|
||||
__attribute__((unused)) const struct pios_flash_driver *driver, //optional - if flash
|
||||
__attribute__((unused)) uintptr_t flash_id) //optional - if flash
|
||||
{
|
||||
int retval;
|
||||
pios_trace(PIOS_TRACE_TEST, "PIOS_FLASHFS_Logfs_Init");
|
||||
char devicename[8];
|
||||
char logfs_path[12];
|
||||
|
||||
// Initialise the yaffs OS subsystem
|
||||
if (yaffs_start_up()) {
|
||||
PIOS_Assert(0);
|
||||
}
|
||||
|
||||
// Allocate the next device id
|
||||
*fs_id = pios_flash_device_count;
|
||||
pios_flash_device_count++;
|
||||
|
||||
snprintf(devicename,6, "/dev%01u", (unsigned) *fs_id);
|
||||
|
||||
// Simposix implementation uses a ram nor simulation which can be installed
|
||||
// as multiple instances
|
||||
yaffs_nor_install_drv(devicename);
|
||||
|
||||
|
||||
sigset_t sigset;
|
||||
sigemptyset(&sigset);
|
||||
yaffsSigSetup(yaffsSigHandler);
|
||||
|
||||
// Attempt to mount the device
|
||||
retval = pios_mount(devicename);
|
||||
if (retval < 0) {
|
||||
pios_trace(PIOS_TRACE_ERROR, "Couldn't mount %s", devicename);
|
||||
}
|
||||
else {
|
||||
|
||||
//Create a "logfs" directory on each yaffs device for use by the
|
||||
// pios_logfs API.
|
||||
snprintf(logfs_path, 12, "%s/%s", devicename, PIOS_LOGFS_DIR);
|
||||
|
||||
// Create the logfs directory if it does not already exist
|
||||
retval = pios_mkdir(logfs_path, O_CREAT);
|
||||
if (retval < 0) pios_trace(PIOS_TRACE_ERROR, "Couldn't mkdir %s", logfs_path);
|
||||
}
|
||||
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
210
flight/pios/posix/libraries/yaffs2/ynorsim.c
Normal file
210
flight/pios/posix/libraries/yaffs2/ynorsim.c
Normal file
@ -0,0 +1,210 @@
|
||||
/*
|
||||
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include "ynorsim.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "pios_trace.h"
|
||||
|
||||
#define YNORSIM_FNAME "emfile-nor"
|
||||
|
||||
/* Set YNORSIM_BIT_CHANGES to a a value from 1..30 to
|
||||
*simulate bit flipping as the programming happens.
|
||||
* A low value results in faster simulation with less chance of encountering a partially programmed
|
||||
* word.
|
||||
*/
|
||||
|
||||
//#define YNORSIM_BIT_CHANGES 15
|
||||
#define YNORSIM_BIT_CHANGES 2
|
||||
|
||||
#if 0
|
||||
/* Simulate 32MB of flash in 256k byte blocks.
|
||||
* This stuff is x32.
|
||||
*/
|
||||
|
||||
#define YNORSIM_BLOCK_SIZE_U32 (256*1024/4)
|
||||
#define YNORSIM_DEV_SIZE_U32 (32*1024 * 1024/4)
|
||||
#else
|
||||
/* Simulate 8MB of flash in 256k byte blocks.
|
||||
* This stuff is x32.
|
||||
*/
|
||||
|
||||
#define YNORSIM_BLOCK_SIZE_U32 (256*1024/4)
|
||||
#define YNORSIM_DEV_SIZE_U32 (8*1024 * 1024/4)
|
||||
#endif
|
||||
|
||||
struct nor_sim {
|
||||
int n_blocks;
|
||||
int block_size_bytes;
|
||||
int file_size;
|
||||
u32 *word;
|
||||
int initialised;
|
||||
char *fname;
|
||||
int remaining_ops;
|
||||
int nops_so_far;
|
||||
};
|
||||
|
||||
int ops_multiplier = 500;
|
||||
extern int random_seed;
|
||||
extern int simulate_power_failure;
|
||||
|
||||
static void NorError(struct nor_sim *sim)
|
||||
{
|
||||
printf("Nor error on device %s\n", sim->fname);
|
||||
while (1) {
|
||||
}
|
||||
}
|
||||
|
||||
static void ynorsim_save_image(struct nor_sim *sim)
|
||||
{
|
||||
int h;
|
||||
pios_trace(PIOS_TRACE_TEST, "ynorsim_save_image");
|
||||
h = open(sim->fname, O_RDWR | O_CREAT | O_TRUNC, S_IREAD | S_IWRITE);
|
||||
write(h, sim->word, sim->file_size);
|
||||
close(h);
|
||||
}
|
||||
|
||||
static void ynorsim_restore_image(struct nor_sim *sim)
|
||||
{
|
||||
int h;
|
||||
pios_trace(PIOS_TRACE_TEST, "ynorsim_restore_image");
|
||||
h = open(sim->fname, O_RDONLY, S_IREAD | S_IWRITE);
|
||||
memset(sim->word, 0xFF, sim->file_size);
|
||||
read(h, sim->word, sim->file_size);
|
||||
close(h);
|
||||
}
|
||||
|
||||
static void ynorsim_power_fail(struct nor_sim *sim)
|
||||
{
|
||||
ynorsim_save_image(sim);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void ynorsim_maybe_power_fail(struct nor_sim *sim)
|
||||
{
|
||||
sim->nops_so_far++;
|
||||
sim->remaining_ops--;
|
||||
if (simulate_power_failure && sim->remaining_ops < 1) {
|
||||
printf("Simulated power failure after %d operations\n",
|
||||
sim->nops_so_far);
|
||||
ynorsim_power_fail(sim);
|
||||
}
|
||||
}
|
||||
|
||||
static void ynorsim_ready(struct nor_sim *sim)
|
||||
{
|
||||
if (sim->initialised)
|
||||
return;
|
||||
srand(random_seed);
|
||||
sim->remaining_ops = 1000000000;
|
||||
sim->remaining_ops =
|
||||
(rand() % 10000) * ops_multiplier * YNORSIM_BIT_CHANGES;
|
||||
ynorsim_restore_image(sim);
|
||||
sim->initialised = 1;
|
||||
}
|
||||
|
||||
/* Public functions. */
|
||||
|
||||
void ynorsim_rd32(struct nor_sim *sim, u32 * addr, u32 * buf, int nwords)
|
||||
{
|
||||
sim = sim;
|
||||
while (nwords > 0) {
|
||||
*buf = *addr;
|
||||
buf++;
|
||||
addr++;
|
||||
nwords--;
|
||||
}
|
||||
}
|
||||
|
||||
void ynorsim_wr_one_word32(struct nor_sim *sim, u32 * addr, u32 val)
|
||||
{
|
||||
u32 tmp;
|
||||
u32 m;
|
||||
int i;
|
||||
|
||||
tmp = *addr;
|
||||
if (val & ~tmp) {
|
||||
/* Fail due to trying to change a zero into a 1 */
|
||||
printf("attempt to set a zero to one (%x)->(%x)\n", tmp, val);
|
||||
NorError(sim);
|
||||
}
|
||||
|
||||
for (i = 0; i < YNORSIM_BIT_CHANGES; i++) {
|
||||
m = 1 << (rand() & 31);
|
||||
if (!(m & val)) {
|
||||
tmp &= ~m;
|
||||
*addr = tmp;
|
||||
ynorsim_maybe_power_fail(sim);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
*addr = tmp & val;
|
||||
ynorsim_maybe_power_fail(sim);
|
||||
}
|
||||
|
||||
void ynorsim_wr32(struct nor_sim *sim, u32 * addr, u32 * buf, int nwords)
|
||||
{
|
||||
while (nwords > 0) {
|
||||
ynorsim_wr_one_word32(sim, addr, *buf);
|
||||
addr++;
|
||||
buf++;
|
||||
nwords--;
|
||||
}
|
||||
}
|
||||
|
||||
void ynorsim_erase(struct nor_sim *sim, u32 * addr)
|
||||
{
|
||||
/* Todo... bit flipping */
|
||||
pios_trace(PIOS_TRACE_TEST, "ynorsim_erase");
|
||||
memset(addr, 0xFF, sim->block_size_bytes);
|
||||
}
|
||||
|
||||
struct nor_sim *ynorsim_initialise(char *name, int n_blocks,
|
||||
int block_size_bytes)
|
||||
{
|
||||
struct nor_sim *sim;
|
||||
|
||||
sim = malloc(sizeof(*sim));
|
||||
if (!sim)
|
||||
return NULL;
|
||||
|
||||
memset(sim, 0, sizeof(*sim));
|
||||
sim->n_blocks = n_blocks;
|
||||
sim->block_size_bytes = block_size_bytes;
|
||||
sim->file_size = n_blocks * block_size_bytes;
|
||||
sim->word = malloc(sim->file_size);
|
||||
sim->fname = strdup(name);
|
||||
|
||||
if(!sim->word)
|
||||
return NULL;
|
||||
|
||||
ynorsim_ready(sim);
|
||||
return sim;
|
||||
}
|
||||
|
||||
void ynorsim_shutdown(struct nor_sim *sim)
|
||||
{
|
||||
ynorsim_save_image(sim);
|
||||
sim->initialised = 0;
|
||||
}
|
||||
|
||||
u32 *ynorsim_get_base(struct nor_sim *sim)
|
||||
{
|
||||
return sim->word;
|
||||
}
|
30
flight/pios/posix/libraries/yaffs2/ynorsim.h
Normal file
30
flight/pios/posix/libraries/yaffs2/ynorsim.h
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||
*
|
||||
* Copyright (C) 2002-2011 Aleph One Ltd.
|
||||
* for Toby Churchill Ltd and Brightstar Engineering
|
||||
*
|
||||
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||
*/
|
||||
|
||||
#ifndef __Y_NORSIM_H__
|
||||
#define __Y_NORSIM_H__
|
||||
|
||||
#include "yaffs_guts.h"
|
||||
|
||||
struct nor_sim;
|
||||
|
||||
void ynorsim_rd32(struct nor_sim *sim, u32 *addr, u32 *data, int nwords);
|
||||
void ynorsim_wr32(struct nor_sim *sim, u32 *addr, u32 *data, int nwords);
|
||||
void ynorsim_erase(struct nor_sim *sim, u32 *addr);
|
||||
void ynorsim_shutdown(struct nor_sim *sim);
|
||||
struct nor_sim *ynorsim_initialise(char *name, int n_blocks, int block_size_bytes);
|
||||
u32 * ynorsim_get_base(struct nor_sim *sim);
|
||||
|
||||
#endif
|
@ -70,6 +70,12 @@ UAVOBJSYNTHDIR = $(OUTDIR)/../uavobject-synthetics/flight
|
||||
|
||||
# optional component libraries
|
||||
include $(PIOS)/common/libraries/FreeRTOS/library.mk
|
||||
ifeq ($(USE_YAFFS),YES)
|
||||
SRC += $(PIOSCORECOMMON)/pios_logfs.c # Used for yaffs testing
|
||||
include $(PIOS)/common/libraries/yaffs2/library.mk # Only required for yaffs testing
|
||||
include $(PIOS)/posix/libraries/yaffs2/yaffs_impl.mk # Only required for yaffs testing
|
||||
endif
|
||||
|
||||
#include $(FLIGHTLIB)/PyMite/pymite.mk
|
||||
|
||||
# List C source files here. (C dependencies are automatically generated.)
|
||||
@ -103,7 +109,12 @@ SRC += $(MATHLIB)/mathmisc.c
|
||||
SRC += $(MATHLIB)/butterworth.c
|
||||
|
||||
SRC += $(PIOSCORECOMMON)/pios_task_monitor.c
|
||||
ifeq ($(USE_YAFFS),YES)
|
||||
SRC += $(PIOSCORECOMMON)/pios_logfs.c # Used for yaffs testing
|
||||
else
|
||||
SRC += $(PIOSCORECOMMON)/pios_dosfs_logfs.c
|
||||
endif
|
||||
SRC += $(PIOSCORECOMMON)/pios_trace.c
|
||||
SRC += $(PIOSCORECOMMON)/pios_debuglog.c
|
||||
SRC += $(PIOSCORECOMMON)/pios_callbackscheduler.c
|
||||
SRC += $(PIOSCORECOMMON)/pios_deltatime.c
|
||||
@ -155,7 +166,10 @@ CFLAGS += -finstrument-functions -ffixed-r10
|
||||
else
|
||||
CFLAGS += -Os
|
||||
endif
|
||||
|
||||
|
||||
# tracing
|
||||
# CFLAGS += -DPIOS_TRACE
|
||||
|
||||
# common architecture-specific flags from the device-specific library makefile
|
||||
CFLAGS += $(ARCHFLAGS)
|
||||
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include <manualcontrolsettings.h>
|
||||
#include <taskinfo.h>
|
||||
|
||||
|
||||
/*
|
||||
* Pull in the board-specific static HW definitions.
|
||||
* Including .c files is a bit ugly but this allows all of
|
||||
@ -119,13 +120,16 @@ void PIOS_Board_Init(void)
|
||||
/* Delay system */
|
||||
PIOS_DELAY_Init();
|
||||
|
||||
// Initialize dosfs fake flash logfs
|
||||
// Initialize logfs for settings.
|
||||
// If linking in yaffs for testing, this will be /dev0 with settings stored
|
||||
// via the logfs object api in /dev0/logfs/
|
||||
if (PIOS_FLASHFS_Logfs_Init(&pios_uavo_settings_fs_id, NULL, NULL, 0)) {
|
||||
PIOS_DEBUG_Assert(0);
|
||||
}
|
||||
// If linking in yaffs for testing, this will re-use the simposix yaffs /dev0 nor
|
||||
// simulation, which does not support being instanced twice.
|
||||
pios_user_fs_id = pios_uavo_settings_fs_id;
|
||||
|
||||
|
||||
/* Initialize the task monitor */
|
||||
if (PIOS_TASK_MONITOR_Initialize(TASKINFO_RUNNING_NUMELEM)) {
|
||||
PIOS_Assert(0);
|
||||
|
@ -45,7 +45,6 @@
|
||||
#if INCLUDE_TEST_TASKS
|
||||
static uint8_t sdcard_available;
|
||||
#endif
|
||||
FILEINFO File;
|
||||
char Buffer[1024];
|
||||
uint32_t Cache;
|
||||
|
||||
|
@ -29,16 +29,6 @@
|
||||
#ifndef PIOS_BOARD_H
|
||||
#define PIOS_BOARD_H
|
||||
|
||||
/**
|
||||
* glue macros for file IO
|
||||
**/
|
||||
#define FILEINFO FILE *
|
||||
#define PIOS_FOPEN_READ(filename, file) (file = fopen((char *)filename, "r")) == NULL
|
||||
#define PIOS_FOPEN_WRITE(filename, file) (file = fopen((char *)filename, "w")) == NULL
|
||||
#define PIOS_FREAD(file, bufferadr, length, resultadr) (*resultadr = fread((uint8_t *)bufferadr, 1, length, *file)) != length
|
||||
#define PIOS_FWRITE(file, bufferadr, length, resultadr) *resultadr = fwrite((uint8_t *)bufferadr, 1, length, *file)
|
||||
#define PIOS_FCLOSE(file) fclose(file)
|
||||
#define PIOS_FUNLINK(file) unlink((char *)filename)
|
||||
|
||||
// ------------------------
|
||||
// Timers and Channels Used
|
||||
|
@ -202,10 +202,6 @@ uint8_t UAVObjUpdateCRC(UAVObjHandle obj_handle, uint16_t instId, uint8_t crc);
|
||||
int32_t UAVObjSave(UAVObjHandle obj_handle, uint16_t instId);
|
||||
int32_t UAVObjLoad(UAVObjHandle obj_handle, uint16_t instId);
|
||||
int32_t UAVObjDelete(UAVObjHandle obj_handle, uint16_t instId);
|
||||
#if defined(PIOS_INCLUDE_SDCARD)
|
||||
int32_t UAVObjSaveToFile(UAVObjHandle obj_handle, uint16_t instId, FILEINFO *file);
|
||||
int32_t UAVObjLoadFromFile(UAVObjHandle obj_handle, FILEINFO *file);
|
||||
#endif
|
||||
int32_t UAVObjSaveSettings();
|
||||
int32_t UAVObjLoadSettings();
|
||||
int32_t UAVObjDeleteSettings();
|
||||
|
Loading…
Reference in New Issue
Block a user