- uid, gid, NULL, NULL, rdev);
-}
-
-yaffs_Object *yaffs_create_symlink(yaffs_Object *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 returns the object id of the equivalent object.*/
-yaffs_Object *yaffs_Link(yaffs_Object *parent, const YCHAR *name,
- yaffs_Object *equivalentObject)
-{
- /* Get the real object in case we were fed a hard link as an equivalent object */
- equivalentObject = yaffs_get_equivalent_obj(equivalentObject);
-
- if (yaffs_create_obj
- (YAFFS_OBJECT_TYPE_HARDLINK, parent, name, 0, 0, 0,
- equivalentObject, NULL, 0)) {
- return equivalentObject;
- } else {
- return NULL;
- }
-
-}
-
-static int yaffs_change_obj_name(yaffs_Object *obj, yaffs_Object *newDir,
- const YCHAR *newName, int force, int shadows)
-{
- int unlinkOp;
- int deleteOp;
-
- yaffs_Object *existingTarget;
-
- if (newDir == NULL)
- newDir = obj->parent; /* use the old directory */
-
- if (newDir->variantType != YAFFS_OBJECT_TYPE_DIRECTORY) {
- T(YAFFS_TRACE_ALWAYS,
- (TSTR
- ("tragedy: yaffs_change_obj_name: newDir is not a directory"
- TENDSTR)));
- YBUG();
- }
-
- /* TODO: Do we need this different handling for YAFFS2 and YAFFS1?? */
- if (obj->myDev->param.isYaffs2)
- unlinkOp = (newDir == obj->myDev->unlinkedDir);
- else
- unlinkOp = (newDir == obj->myDev->unlinkedDir
- && obj->variantType == YAFFS_OBJECT_TYPE_FILE);
-
- deleteOp = (newDir == obj->myDev->deletedDir);
-
- existingTarget = yaffs_find_by_name(newDir, newName);
-
- /* If the object is a file going into the unlinked directory,
- * then it is OK to just stuff it in since duplicate names are allowed.
- * else only proceed if the new name does not exist and if we're putting
- * it into a directory.
- */
- if ((unlinkOp ||
- deleteOp ||
- force ||
- (shadows > 0) ||
- !existingTarget) &&
- newDir->variantType == YAFFS_OBJECT_TYPE_DIRECTORY) {
- yaffs_set_obj_name(obj, newName);
- obj->dirty = 1;
-
- yaffs_add_obj_to_dir(newDir, obj);
-
- if (unlinkOp)
- obj->unlinked = 1;
-
- /* If it is a deletion then we mark it as a shrink for gc purposes. */
- if (yaffs_update_oh(obj, newName, 0, deleteOp, shadows, NULL) >= 0)
- return YAFFS_OK;
- }
-
- return YAFFS_FAIL;
-}
-
-int yaffs_rename_obj(yaffs_Object *oldDir, const YCHAR *oldName,
- yaffs_Object *newDir, const YCHAR *newName)
-{
- yaffs_Object *obj = NULL;
- yaffs_Object *existingTarget = NULL;
- int force = 0;
- int result;
- yaffs_Device *dev;
-
-
- if (!oldDir || oldDir->variantType != YAFFS_OBJECT_TYPE_DIRECTORY)
- YBUG();
- if (!newDir || newDir->variantType != YAFFS_OBJECT_TYPE_DIRECTORY)
- YBUG();
-
- dev = oldDir->myDev;
-
-#ifdef CONFIG_YAFFS_CASE_INSENSITIVE
- /* Special case for case insemsitive systems (eg. WinCE).
- * While look-up is case insensitive, the name isn't.
- * Therefore we might want to change x.txt to X.txt
- */
- if (oldDir == newDir && yaffs_strcmp(oldName, newName) == 0)
- force = 1;
-#endif
-
- if(yaffs_strnlen(newName,YAFFS_MAX_NAME_LENGTH+1) > YAFFS_MAX_NAME_LENGTH)
- /* ENAMETOOLONG */
- return YAFFS_FAIL;
-
- obj = yaffs_find_by_name(oldDir, oldName);
-
- if (obj && obj->renameAllowed) {
-
- /* Now do the handling for an existing target, if there is one */
-
- existingTarget = yaffs_find_by_name(newDir, newName);
- if (existingTarget &&
- existingTarget->variantType == YAFFS_OBJECT_TYPE_DIRECTORY &&
- !ylist_empty(&existingTarget->variant.directoryVariant.children)) {
- /* There is a target that is a non-empty directory, so we fail */
- return YAFFS_FAIL; /* EEXIST or ENOTEMPTY */
- } else if (existingTarget && existingTarget != obj) {
- /* Nuke the target first, using shadowing,
- * but only if it isn't the same object.
- *
- * Note we must disable gc otherwise it can mess up the shadowing.
- *
- */
- dev->gcDisable=1;
- yaffs_change_obj_name(obj, newDir, newName, force,
- existingTarget->objectId);
- existingTarget->isShadowed = 1;
- yaffs_unlink_obj(existingTarget);
- dev->gcDisable=0;
- }
-
- result = yaffs_change_obj_name(obj, newDir, newName, 1, 0);
-
- yaffs_update_parent(oldDir);
- if(newDir != oldDir)
- yaffs_update_parent(newDir);
-
- return result;
- }
- return YAFFS_FAIL;
-}
-
-/*------------------------- Block Management and Page Allocation ----------------*/
-
-static int yaffs_init_blocks(yaffs_Device *dev)
-{
- int nBlocks = dev->internalEndBlock - dev->internalStartBlock + 1;
-
- dev->blockInfo = NULL;
- dev->chunkBits = NULL;
-
- dev->allocationBlock = -1; /* force it to get a new one */
-
- /* If the first allocation strategy fails, thry the alternate one */
- dev->blockInfo = YMALLOC(nBlocks * sizeof(yaffs_BlockInfo));
- if (!dev->blockInfo) {
- dev->blockInfo = YMALLOC_ALT(nBlocks * sizeof(yaffs_BlockInfo));
- dev->blockInfoAlt = 1;
- } else
- dev->blockInfoAlt = 0;
-
- if (dev->blockInfo) {
- /* Set up dynamic blockinfo stuff. */
- dev->chunkBitmapStride = (dev->param.nChunksPerBlock + 7) / 8; /* round up bytes */
- dev->chunkBits = YMALLOC(dev->chunkBitmapStride * nBlocks);
- if (!dev->chunkBits) {
- dev->chunkBits = YMALLOC_ALT(dev->chunkBitmapStride * nBlocks);
- dev->chunkBitsAlt = 1;
- } else
- dev->chunkBitsAlt = 0;
- }
-
- if (dev->blockInfo && dev->chunkBits) {
- memset(dev->blockInfo, 0, nBlocks * sizeof(yaffs_BlockInfo));
- memset(dev->chunkBits, 0, dev->chunkBitmapStride * nBlocks);
- return YAFFS_OK;
- }
-
- return YAFFS_FAIL;
-}
-
-static void yaffs_deinit_blocks(yaffs_Device *dev)
-{
- if (dev->blockInfoAlt && dev->blockInfo)
- YFREE_ALT(dev->blockInfo);
- else if (dev->blockInfo)
- YFREE(dev->blockInfo);
-
- dev->blockInfoAlt = 0;
-
- dev->blockInfo = NULL;
-
- if (dev->chunkBitsAlt && dev->chunkBits)
- YFREE_ALT(dev->chunkBits);
- else if (dev->chunkBits)
- YFREE(dev->chunkBits);
- dev->chunkBitsAlt = 0;
- dev->chunkBits = NULL;
-}
-
-void yaffs_block_became_dirty(yaffs_Device *dev, int blockNo)
-{
- yaffs_BlockInfo *bi = yaffs_get_block_info(dev, blockNo);
-
- int erasedOk = 0;
-
- /* If the block is still healthy erase it and mark as clean.
- * If the block has had a data failure, then retire it.
- */
-
- T(YAFFS_TRACE_GC | YAFFS_TRACE_ERASE,
- (TSTR("yaffs_block_became_dirty block %d state %d %s"TENDSTR),
- blockNo, bi->blockState, (bi->needsRetiring) ? "needs retiring" : ""));
-
- yaffs2_clear_oldest_dirty_seq(dev,bi);
-
- bi->blockState = YAFFS_BLOCK_STATE_DIRTY;
-
- /* If this is the block being garbage collected then stop gc'ing this block */
- if(blockNo == dev->gcBlock)
- dev->gcBlock = 0;
-
- /* If this block is currently the best candidate for gc then drop as a candidate */
- if(blockNo == dev->gcDirtiest){
- dev->gcDirtiest = 0;
- dev->gcPagesInUse = 0;
- }
-
- if (!bi->needsRetiring) {
- yaffs2_checkpt_invalidate(dev);
- erasedOk = yaffs_erase_block(dev, blockNo);
- if (!erasedOk) {
- dev->nErasureFailures++;
- T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,
- (TSTR("**>> Erasure failed %d" TENDSTR), blockNo));
- }
- }
-
- if (erasedOk &&
- ((yaffs_trace_mask & YAFFS_TRACE_ERASE) || !yaffs_skip_verification(dev))) {
- int i;
- for (i = 0; i < dev->param.nChunksPerBlock; i++) {
- if (!yaffs_check_chunk_erased
- (dev, blockNo * dev->param.nChunksPerBlock + i)) {
- T(YAFFS_TRACE_ERROR,
- (TSTR
- (">>Block %d erasure supposedly OK, but chunk %d not erased"
- TENDSTR), blockNo, i));
- }
- }
- }
-
- if (erasedOk) {
- /* Clean it up... */
- bi->blockState = YAFFS_BLOCK_STATE_EMPTY;
- bi->sequenceNumber = 0;
- dev->nErasedBlocks++;
- bi->pagesInUse = 0;
- bi->softDeletions = 0;
- bi->hasShrinkHeader = 0;
- bi->skipErasedCheck = 1; /* This is clean, so no need to check */
- bi->gcPrioritise = 0;
- yaffs_clear_chunk_bits(dev, blockNo);
-
- T(YAFFS_TRACE_ERASE,
- (TSTR("Erased block %d" TENDSTR), blockNo));
- } else {
- dev->nFreeChunks -= dev->param.nChunksPerBlock; /* We lost a block of free space */
-
- yaffs_retire_block(dev, blockNo);
- T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,
- (TSTR("**>> Block %d retired" TENDSTR), blockNo));
- }
-}
-
-static int yaffs_find_alloc_block(yaffs_Device *dev)
-{
- int i;
-
- yaffs_BlockInfo *bi;
-
- if (dev->nErasedBlocks < 1) {
- /* Hoosterman we've got a problem.
- * Can't get space to gc
- */
- T(YAFFS_TRACE_ERROR,
- (TSTR("yaffs tragedy: no more erased blocks" TENDSTR)));
-
- return -1;
- }
-
- /* Find an empty block. */
-
- for (i = dev->internalStartBlock; i <= dev->internalEndBlock; i++) {
- dev->allocationBlockFinder++;
- if (dev->allocationBlockFinder < dev->internalStartBlock
- || dev->allocationBlockFinder > dev->internalEndBlock) {
- dev->allocationBlockFinder = dev->internalStartBlock;
- }
-
- bi = yaffs_get_block_info(dev, dev->allocationBlockFinder);
-
- if (bi->blockState == YAFFS_BLOCK_STATE_EMPTY) {
- bi->blockState = YAFFS_BLOCK_STATE_ALLOCATING;
- dev->sequenceNumber++;
- bi->sequenceNumber = dev->sequenceNumber;
- dev->nErasedBlocks--;
- T(YAFFS_TRACE_ALLOCATE,
- (TSTR("Allocated block %d, seq %d, %d left" TENDSTR),
- dev->allocationBlockFinder, dev->sequenceNumber,
- dev->nErasedBlocks));
- return dev->allocationBlockFinder;
- }
- }
-
- T(YAFFS_TRACE_ALWAYS,
- (TSTR
- ("yaffs tragedy: no more erased blocks, but there should have been %d"
- TENDSTR), dev->nErasedBlocks));
-
- return -1;