|  | /* | 
|  | * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. | 
|  | * | 
|  | * Copyright (C) 2002-2007 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. | 
|  | */ | 
|  |  | 
|  | /* XXX U-BOOT XXX */ | 
|  | #include <common.h> | 
|  | #include <malloc.h> | 
|  |  | 
|  | #include "yaffsfs.h" | 
|  | #include "yaffs_guts.h" | 
|  | #include "yaffscfg.h" | 
|  | #include "yportenv.h" | 
|  |  | 
|  | /* XXX U-BOOT XXX */ | 
|  | #if 0 | 
|  | #include <string.h> // for memset | 
|  | #endif | 
|  |  | 
|  | #define YAFFSFS_MAX_SYMLINK_DEREFERENCES 5 | 
|  |  | 
|  | #ifndef NULL | 
|  | #define NULL ((void *)0) | 
|  | #endif | 
|  |  | 
|  |  | 
|  | const char *yaffsfs_c_version="$Id: yaffsfs.c,v 1.18 2007/07/18 19:40:38 charles Exp $"; | 
|  |  | 
|  | // configurationList is the list of devices that are supported | 
|  | static yaffsfs_DeviceConfiguration *yaffsfs_configurationList; | 
|  |  | 
|  |  | 
|  | /* Some forward references */ | 
|  | static yaffs_Object *yaffsfs_FindObject(yaffs_Object *relativeDirectory, const char *path, int symDepth); | 
|  | static void yaffsfs_RemoveObjectCallback(yaffs_Object *obj); | 
|  |  | 
|  |  | 
|  | // Handle management. | 
|  | // | 
|  |  | 
|  |  | 
|  | unsigned int yaffs_wr_attempts; | 
|  |  | 
|  | typedef struct | 
|  | { | 
|  | __u8  inUse:1;		// this handle is in use | 
|  | __u8  readOnly:1;	// this handle is read only | 
|  | __u8  append:1;		// append only | 
|  | __u8  exclusive:1;	// exclusive | 
|  | __u32 position;		// current position in file | 
|  | yaffs_Object *obj;	// the object | 
|  | }yaffsfs_Handle; | 
|  |  | 
|  |  | 
|  | static yaffsfs_Handle yaffsfs_handle[YAFFSFS_N_HANDLES]; | 
|  |  | 
|  | // yaffsfs_InitHandle | 
|  | /// Inilitalise handles on start-up. | 
|  | // | 
|  | static int yaffsfs_InitHandles(void) | 
|  | { | 
|  | int i; | 
|  | for(i = 0; i < YAFFSFS_N_HANDLES; i++) | 
|  | { | 
|  | yaffsfs_handle[i].inUse = 0; | 
|  | yaffsfs_handle[i].obj = NULL; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | yaffsfs_Handle *yaffsfs_GetHandlePointer(int h) | 
|  | { | 
|  | if(h < 0 || h >= YAFFSFS_N_HANDLES) | 
|  | { | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | return &yaffsfs_handle[h]; | 
|  | } | 
|  |  | 
|  | yaffs_Object *yaffsfs_GetHandleObject(int handle) | 
|  | { | 
|  | yaffsfs_Handle *h = yaffsfs_GetHandlePointer(handle); | 
|  |  | 
|  | if(h && h->inUse) | 
|  | { | 
|  | return h->obj; | 
|  | } | 
|  |  | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  |  | 
|  | //yaffsfs_GetHandle | 
|  | // Grab a handle (when opening a file) | 
|  | // | 
|  |  | 
|  | static int yaffsfs_GetHandle(void) | 
|  | { | 
|  | int i; | 
|  | yaffsfs_Handle *h; | 
|  |  | 
|  | for(i = 0; i < YAFFSFS_N_HANDLES; i++) | 
|  | { | 
|  | h = yaffsfs_GetHandlePointer(i); | 
|  | if(!h) | 
|  | { | 
|  | // todo bug: should never happen | 
|  | } | 
|  | if(!h->inUse) | 
|  | { | 
|  | memset(h,0,sizeof(yaffsfs_Handle)); | 
|  | h->inUse=1; | 
|  | return i; | 
|  | } | 
|  | } | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | // yaffs_PutHandle | 
|  | // Let go of a handle (when closing a file) | 
|  | // | 
|  | static int yaffsfs_PutHandle(int handle) | 
|  | { | 
|  | yaffsfs_Handle *h = yaffsfs_GetHandlePointer(handle); | 
|  |  | 
|  | if(h) | 
|  | { | 
|  | h->inUse = 0; | 
|  | h->obj = NULL; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  |  | 
|  | // Stuff to search for a directory from a path | 
|  |  | 
|  |  | 
|  | int yaffsfs_Match(char a, char b) | 
|  | { | 
|  | // case sensitive | 
|  | return (a == b); | 
|  | } | 
|  |  | 
|  | // yaffsfs_FindDevice | 
|  | // yaffsfs_FindRoot | 
|  | // Scan the configuration list to find the root. | 
|  | // Curveballs: Should match paths that end in '/' too | 
|  | // Curveball2 Might have "/x/ and "/x/y". Need to return the longest match | 
|  | static yaffs_Device *yaffsfs_FindDevice(const char *path, char **restOfPath) | 
|  | { | 
|  | yaffsfs_DeviceConfiguration *cfg = yaffsfs_configurationList; | 
|  | const char *leftOver; | 
|  | const char *p; | 
|  | yaffs_Device *retval = NULL; | 
|  | int thisMatchLength; | 
|  | int longestMatch = -1; | 
|  |  | 
|  | // Check all configs, choose the one that: | 
|  | // 1) Actually matches a prefix (ie /a amd /abc will not match | 
|  | // 2) Matches the longest. | 
|  | while(cfg && cfg->prefix && cfg->dev) | 
|  | { | 
|  | leftOver = path; | 
|  | p = cfg->prefix; | 
|  | thisMatchLength = 0; | 
|  |  | 
|  | while(*p &&  //unmatched part of prefix | 
|  | strcmp(p,"/") && // the rest of the prefix is not / (to catch / at end) | 
|  | *leftOver && | 
|  | yaffsfs_Match(*p,*leftOver)) | 
|  | { | 
|  | p++; | 
|  | leftOver++; | 
|  | thisMatchLength++; | 
|  | } | 
|  | if((!*p || strcmp(p,"/") == 0) &&      // end of prefix | 
|  | (!*leftOver || *leftOver == '/') && // no more in this path name part | 
|  | (thisMatchLength > longestMatch)) | 
|  | { | 
|  | // Matched prefix | 
|  | *restOfPath = (char *)leftOver; | 
|  | retval = cfg->dev; | 
|  | longestMatch = thisMatchLength; | 
|  | } | 
|  | cfg++; | 
|  | } | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | static yaffs_Object *yaffsfs_FindRoot(const char *path, char **restOfPath) | 
|  | { | 
|  |  | 
|  | yaffs_Device *dev; | 
|  |  | 
|  | dev= yaffsfs_FindDevice(path,restOfPath); | 
|  | if(dev && dev->isMounted) | 
|  | { | 
|  | return dev->rootDir; | 
|  | } | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | static yaffs_Object *yaffsfs_FollowLink(yaffs_Object *obj,int symDepth) | 
|  | { | 
|  |  | 
|  | while(obj && obj->variantType == YAFFS_OBJECT_TYPE_SYMLINK) | 
|  | { | 
|  | char *alias = obj->variant.symLinkVariant.alias; | 
|  |  | 
|  | if(*alias == '/') | 
|  | { | 
|  | // Starts with a /, need to scan from root up | 
|  | obj = yaffsfs_FindObject(NULL,alias,symDepth++); | 
|  | } | 
|  | else | 
|  | { | 
|  | // Relative to here, so use the parent of the symlink as a start | 
|  | obj = yaffsfs_FindObject(obj->parent,alias,symDepth++); | 
|  | } | 
|  | } | 
|  | 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 yaffs_Object *yaffsfs_DoFindDirectory(yaffs_Object *startDir,const char *path,char **name,int symDepth) | 
|  | { | 
|  | yaffs_Object *dir; | 
|  | char *restOfPath; | 
|  | char str[YAFFS_MAX_NAME_LENGTH+1]; | 
|  | int i; | 
|  |  | 
|  | if(symDepth > YAFFSFS_MAX_SYMLINK_DEREFERENCES) | 
|  | { | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | if(startDir) | 
|  | { | 
|  | dir = startDir; | 
|  | restOfPath = (char *)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(*restOfPath == '/') | 
|  | { | 
|  | restOfPath++; // get rid of '/' | 
|  | } | 
|  |  | 
|  | *name = restOfPath; | 
|  | i = 0; | 
|  |  | 
|  | while(*restOfPath && *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(strcmp(str,".") == 0) | 
|  | { | 
|  | // Do nothing | 
|  | } | 
|  | else if(strcmp(str,"..") == 0) | 
|  | { | 
|  | dir = dir->parent; | 
|  | } | 
|  | else | 
|  | { | 
|  | dir = yaffs_FindObjectByName(dir,str); | 
|  |  | 
|  | while(dir && dir->variantType == YAFFS_OBJECT_TYPE_SYMLINK) | 
|  | { | 
|  |  | 
|  | dir = yaffsfs_FollowLink(dir,symDepth); | 
|  |  | 
|  | } | 
|  |  | 
|  | if(dir && dir->variantType != YAFFS_OBJECT_TYPE_DIRECTORY) | 
|  | { | 
|  | dir = NULL; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | // directory did not exist. | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | static yaffs_Object *yaffsfs_FindDirectory(yaffs_Object *relativeDirectory,const char *path,char **name,int symDepth) | 
|  | { | 
|  | return yaffsfs_DoFindDirectory(relativeDirectory,path,name,symDepth); | 
|  | } | 
|  |  | 
|  | // yaffsfs_FindObject turns a path for an existing object into the object | 
|  | // | 
|  | static yaffs_Object *yaffsfs_FindObject(yaffs_Object *relativeDirectory, const char *path,int symDepth) | 
|  | { | 
|  | yaffs_Object *dir; | 
|  | char *name; | 
|  |  | 
|  | dir = yaffsfs_FindDirectory(relativeDirectory,path,&name,symDepth); | 
|  |  | 
|  | if(dir && *name) | 
|  | { | 
|  | return yaffs_FindObjectByName(dir,name); | 
|  | } | 
|  |  | 
|  | return dir; | 
|  | } | 
|  |  | 
|  |  | 
|  |  | 
|  | int yaffs_open(const char *path, int oflag, int mode) | 
|  | { | 
|  | yaffs_Object *obj = NULL; | 
|  | yaffs_Object *dir = NULL; | 
|  | char *name; | 
|  | int handle = -1; | 
|  | yaffsfs_Handle *h = NULL; | 
|  | int alreadyOpen = 0; | 
|  | int alreadyExclusive = 0; | 
|  | int openDenied = 0; | 
|  | int symDepth = 0; | 
|  | int errorReported = 0; | 
|  |  | 
|  | int i; | 
|  |  | 
|  |  | 
|  | // todo sanity check oflag (eg. can't have O_TRUNC without WRONLY or RDWR | 
|  |  | 
|  |  | 
|  | yaffsfs_Lock(); | 
|  |  | 
|  | handle = yaffsfs_GetHandle(); | 
|  |  | 
|  | if(handle >= 0) | 
|  | { | 
|  |  | 
|  | h = yaffsfs_GetHandlePointer(handle); | 
|  |  | 
|  |  | 
|  | // try to find the exisiting object | 
|  | obj = yaffsfs_FindObject(NULL,path,0); | 
|  |  | 
|  | if(obj && obj->variantType == YAFFS_OBJECT_TYPE_SYMLINK) | 
|  | { | 
|  |  | 
|  | obj = yaffsfs_FollowLink(obj,symDepth++); | 
|  | } | 
|  |  | 
|  | if(obj) | 
|  | { | 
|  | // Check if the object is already in use | 
|  | alreadyOpen = alreadyExclusive = 0; | 
|  |  | 
|  | for(i = 0; i <= YAFFSFS_N_HANDLES; i++) | 
|  | { | 
|  |  | 
|  | if(i != handle && | 
|  | yaffsfs_handle[i].inUse && | 
|  | obj == yaffsfs_handle[i].obj) | 
|  | { | 
|  | alreadyOpen = 1; | 
|  | if(yaffsfs_handle[i].exclusive) | 
|  | { | 
|  | alreadyExclusive = 1; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if(((oflag & O_EXCL) && alreadyOpen) || alreadyExclusive) | 
|  | { | 
|  | openDenied = 1; | 
|  | } | 
|  |  | 
|  | // Open should fail if O_CREAT and O_EXCL are specified | 
|  | if((oflag & O_EXCL) && (oflag & O_CREAT)) | 
|  | { | 
|  | openDenied = 1; | 
|  | yaffsfs_SetError(-EEXIST); | 
|  | errorReported = 1; | 
|  | } | 
|  |  | 
|  | // Check file permissions | 
|  | if( (oflag & (O_RDWR | O_WRONLY)) == 0 &&     // ie O_RDONLY | 
|  | !(obj->yst_mode & S_IREAD)) | 
|  | { | 
|  | openDenied = 1; | 
|  | } | 
|  |  | 
|  | if( (oflag & O_RDWR) && | 
|  | !(obj->yst_mode & S_IREAD)) | 
|  | { | 
|  | openDenied = 1; | 
|  | } | 
|  |  | 
|  | if( (oflag & (O_RDWR | O_WRONLY)) && | 
|  | !(obj->yst_mode & S_IWRITE)) | 
|  | { | 
|  | openDenied = 1; | 
|  | } | 
|  |  | 
|  | } | 
|  |  | 
|  | else if((oflag & O_CREAT)) | 
|  | { | 
|  | // Let's see if we can create this file | 
|  | dir = yaffsfs_FindDirectory(NULL,path,&name,0); | 
|  | if(dir) | 
|  | { | 
|  | obj = yaffs_MknodFile(dir,name,mode,0,0); | 
|  | } | 
|  | else | 
|  | { | 
|  | yaffsfs_SetError(-ENOTDIR); | 
|  | } | 
|  | } | 
|  |  | 
|  | if(obj && !openDenied) | 
|  | { | 
|  | h->obj = obj; | 
|  | h->inUse = 1; | 
|  | h->readOnly = (oflag & (O_WRONLY | O_RDWR)) ? 0 : 1; | 
|  | h->append =  (oflag & O_APPEND) ? 1 : 0; | 
|  | h->exclusive = (oflag & O_EXCL) ? 1 : 0; | 
|  | h->position = 0; | 
|  |  | 
|  | obj->inUse++; | 
|  | if((oflag & O_TRUNC) && !h->readOnly) | 
|  | { | 
|  | //todo truncate | 
|  | yaffs_ResizeFile(obj,0); | 
|  | } | 
|  |  | 
|  | } | 
|  | else | 
|  | { | 
|  | yaffsfs_PutHandle(handle); | 
|  | if(!errorReported) | 
|  | { | 
|  | yaffsfs_SetError(-EACCESS); | 
|  | errorReported = 1; | 
|  | } | 
|  | handle = -1; | 
|  | } | 
|  |  | 
|  | } | 
|  |  | 
|  | yaffsfs_Unlock(); | 
|  |  | 
|  | return handle; | 
|  | } | 
|  |  | 
|  | int yaffs_close(int fd) | 
|  | { | 
|  | yaffsfs_Handle *h = NULL; | 
|  | int retVal = 0; | 
|  |  | 
|  | yaffsfs_Lock(); | 
|  |  | 
|  | h = yaffsfs_GetHandlePointer(fd); | 
|  |  | 
|  | if(h && h->inUse) | 
|  | { | 
|  | // clean up | 
|  | yaffs_FlushFile(h->obj,1); | 
|  | h->obj->inUse--; | 
|  | if(h->obj->inUse <= 0 && h->obj->unlinked) | 
|  | { | 
|  | yaffs_DeleteFile(h->obj); | 
|  | } | 
|  | yaffsfs_PutHandle(fd); | 
|  | retVal = 0; | 
|  | } | 
|  | else | 
|  | { | 
|  | // bad handle | 
|  | yaffsfs_SetError(-EBADF); | 
|  | retVal = -1; | 
|  | } | 
|  |  | 
|  | yaffsfs_Unlock(); | 
|  |  | 
|  | return retVal; | 
|  | } | 
|  |  | 
|  | int yaffs_read(int fd, void *buf, unsigned int nbyte) | 
|  | { | 
|  | yaffsfs_Handle *h = NULL; | 
|  | yaffs_Object *obj = NULL; | 
|  | int pos = 0; | 
|  | int nRead = -1; | 
|  | int maxRead; | 
|  |  | 
|  | yaffsfs_Lock(); | 
|  | h = yaffsfs_GetHandlePointer(fd); | 
|  | obj = yaffsfs_GetHandleObject(fd); | 
|  |  | 
|  | if(!h || !obj) | 
|  | { | 
|  | // bad handle | 
|  | yaffsfs_SetError(-EBADF); | 
|  | } | 
|  | else if( h && obj) | 
|  | { | 
|  | pos=  h->position; | 
|  | if(yaffs_GetObjectFileLength(obj) > pos) | 
|  | { | 
|  | maxRead = yaffs_GetObjectFileLength(obj) - pos; | 
|  | } | 
|  | else | 
|  | { | 
|  | maxRead = 0; | 
|  | } | 
|  |  | 
|  | if(nbyte > maxRead) | 
|  | { | 
|  | nbyte = maxRead; | 
|  | } | 
|  |  | 
|  |  | 
|  | if(nbyte > 0) | 
|  | { | 
|  | nRead = yaffs_ReadDataFromFile(obj,buf,pos,nbyte); | 
|  | if(nRead >= 0) | 
|  | { | 
|  | h->position = pos + nRead; | 
|  | } | 
|  | else | 
|  | { | 
|  | //todo error | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | nRead = 0; | 
|  | } | 
|  |  | 
|  | } | 
|  |  | 
|  | yaffsfs_Unlock(); | 
|  |  | 
|  |  | 
|  | return (nRead >= 0) ? nRead : -1; | 
|  |  | 
|  | } | 
|  |  | 
|  | int yaffs_write(int fd, const void *buf, unsigned int nbyte) | 
|  | { | 
|  | yaffsfs_Handle *h = NULL; | 
|  | yaffs_Object *obj = NULL; | 
|  | int pos = 0; | 
|  | int nWritten = -1; | 
|  | int writeThrough = 0; | 
|  |  | 
|  | yaffsfs_Lock(); | 
|  | h = yaffsfs_GetHandlePointer(fd); | 
|  | obj = yaffsfs_GetHandleObject(fd); | 
|  |  | 
|  | if(!h || !obj) | 
|  | { | 
|  | // bad handle | 
|  | yaffsfs_SetError(-EBADF); | 
|  | } | 
|  | else if( h && obj && h->readOnly) | 
|  | { | 
|  | // todo error | 
|  | } | 
|  | else if( h && obj) | 
|  | { | 
|  | if(h->append) | 
|  | { | 
|  | pos =  yaffs_GetObjectFileLength(obj); | 
|  | } | 
|  | else | 
|  | { | 
|  | pos = h->position; | 
|  | } | 
|  |  | 
|  | nWritten = yaffs_WriteDataToFile(obj,buf,pos,nbyte,writeThrough); | 
|  |  | 
|  | if(nWritten >= 0) | 
|  | { | 
|  | h->position = pos + nWritten; | 
|  | } | 
|  | else | 
|  | { | 
|  | //todo error | 
|  | } | 
|  |  | 
|  | } | 
|  |  | 
|  | yaffsfs_Unlock(); | 
|  |  | 
|  |  | 
|  | return (nWritten >= 0) ? nWritten : -1; | 
|  |  | 
|  | } | 
|  |  | 
|  | int yaffs_truncate(int fd, off_t newSize) | 
|  | { | 
|  | yaffsfs_Handle *h = NULL; | 
|  | yaffs_Object *obj = NULL; | 
|  | int result = 0; | 
|  |  | 
|  | yaffsfs_Lock(); | 
|  | h = yaffsfs_GetHandlePointer(fd); | 
|  | obj = yaffsfs_GetHandleObject(fd); | 
|  |  | 
|  | if(!h || !obj) | 
|  | { | 
|  | // bad handle | 
|  | yaffsfs_SetError(-EBADF); | 
|  | } | 
|  | else | 
|  | { | 
|  | // resize the file | 
|  | result = yaffs_ResizeFile(obj,newSize); | 
|  | } | 
|  | yaffsfs_Unlock(); | 
|  |  | 
|  |  | 
|  | return (result) ? 0 : -1; | 
|  |  | 
|  | } | 
|  |  | 
|  | off_t yaffs_lseek(int fd, off_t offset, int whence) | 
|  | { | 
|  | yaffsfs_Handle *h = NULL; | 
|  | yaffs_Object *obj = NULL; | 
|  | int pos = -1; | 
|  | int fSize = -1; | 
|  |  | 
|  | yaffsfs_Lock(); | 
|  | h = yaffsfs_GetHandlePointer(fd); | 
|  | obj = yaffsfs_GetHandleObject(fd); | 
|  |  | 
|  | if(!h || !obj) | 
|  | { | 
|  | // bad handle | 
|  | yaffsfs_SetError(-EBADF); | 
|  | } | 
|  | else if(whence == SEEK_SET) | 
|  | { | 
|  | if(offset >= 0) | 
|  | { | 
|  | pos = offset; | 
|  | } | 
|  | } | 
|  | else if(whence == SEEK_CUR) | 
|  | { | 
|  | if( (h->position + offset) >= 0) | 
|  | { | 
|  | pos = (h->position + offset); | 
|  | } | 
|  | } | 
|  | else if(whence == SEEK_END) | 
|  | { | 
|  | fSize = yaffs_GetObjectFileLength(obj); | 
|  | if(fSize >= 0 && (fSize + offset) >= 0) | 
|  | { | 
|  | pos = fSize + offset; | 
|  | } | 
|  | } | 
|  |  | 
|  | if(pos >= 0) | 
|  | { | 
|  | h->position = pos; | 
|  | } | 
|  | else | 
|  | { | 
|  | // todo error | 
|  | } | 
|  |  | 
|  |  | 
|  | yaffsfs_Unlock(); | 
|  |  | 
|  | return pos; | 
|  | } | 
|  |  | 
|  |  | 
|  | int yaffsfs_DoUnlink(const char *path,int isDirectory) | 
|  | { | 
|  | yaffs_Object *dir = NULL; | 
|  | yaffs_Object *obj = NULL; | 
|  | char *name; | 
|  | int result = YAFFS_FAIL; | 
|  |  | 
|  | yaffsfs_Lock(); | 
|  |  | 
|  | obj = yaffsfs_FindObject(NULL,path,0); | 
|  | dir = yaffsfs_FindDirectory(NULL,path,&name,0); | 
|  | if(!dir) | 
|  | { | 
|  | yaffsfs_SetError(-ENOTDIR); | 
|  | } | 
|  | else if(!obj) | 
|  | { | 
|  | yaffsfs_SetError(-ENOENT); | 
|  | } | 
|  | else if(!isDirectory && obj->variantType == YAFFS_OBJECT_TYPE_DIRECTORY) | 
|  | { | 
|  | yaffsfs_SetError(-EISDIR); | 
|  | } | 
|  | else if(isDirectory && obj->variantType != YAFFS_OBJECT_TYPE_DIRECTORY) | 
|  | { | 
|  | yaffsfs_SetError(-ENOTDIR); | 
|  | } | 
|  | else | 
|  | { | 
|  | result = yaffs_Unlink(dir,name); | 
|  |  | 
|  | if(result == YAFFS_FAIL && isDirectory) | 
|  | { | 
|  | yaffsfs_SetError(-ENOTEMPTY); | 
|  | } | 
|  | } | 
|  |  | 
|  | yaffsfs_Unlock(); | 
|  |  | 
|  | // todo error | 
|  |  | 
|  | return (result == YAFFS_FAIL) ? -1 : 0; | 
|  | } | 
|  | int yaffs_rmdir(const char *path) | 
|  | { | 
|  | return yaffsfs_DoUnlink(path,1); | 
|  | } | 
|  |  | 
|  | int yaffs_unlink(const char *path) | 
|  | { | 
|  | return yaffsfs_DoUnlink(path,0); | 
|  | } | 
|  |  | 
|  | int yaffs_rename(const char *oldPath, const char *newPath) | 
|  | { | 
|  | yaffs_Object *olddir = NULL; | 
|  | yaffs_Object *newdir = NULL; | 
|  | yaffs_Object *obj = NULL; | 
|  | char *oldname; | 
|  | char *newname; | 
|  | int result= YAFFS_FAIL; | 
|  | int renameAllowed = 1; | 
|  |  | 
|  | yaffsfs_Lock(); | 
|  |  | 
|  | olddir = yaffsfs_FindDirectory(NULL,oldPath,&oldname,0); | 
|  | newdir = yaffsfs_FindDirectory(NULL,newPath,&newname,0); | 
|  | obj = yaffsfs_FindObject(NULL,oldPath,0); | 
|  |  | 
|  | if(!olddir || !newdir || !obj) | 
|  | { | 
|  | // bad file | 
|  | yaffsfs_SetError(-EBADF); | 
|  | renameAllowed = 0; | 
|  | } | 
|  | else if(olddir->myDev != newdir->myDev) | 
|  | { | 
|  | // oops must be on same device | 
|  | // todo error | 
|  | yaffsfs_SetError(-EXDEV); | 
|  | renameAllowed = 0; | 
|  | } | 
|  | else if(obj && obj->variantType == 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 | 
|  |  | 
|  | yaffs_Object *xx = newdir; | 
|  |  | 
|  | while( renameAllowed && xx) | 
|  | { | 
|  | if(xx == obj) | 
|  | { | 
|  | renameAllowed = 0; | 
|  | } | 
|  | xx = xx->parent; | 
|  | } | 
|  | if(!renameAllowed) yaffsfs_SetError(-EACCESS); | 
|  | } | 
|  |  | 
|  | if(renameAllowed) | 
|  | { | 
|  | result = yaffs_RenameObject(olddir,oldname,newdir,newname); | 
|  | } | 
|  |  | 
|  | yaffsfs_Unlock(); | 
|  |  | 
|  | return (result == YAFFS_FAIL) ? -1 : 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int yaffsfs_DoStat(yaffs_Object *obj,struct yaffs_stat *buf) | 
|  | { | 
|  | int retVal = -1; | 
|  |  | 
|  | if(obj) | 
|  | { | 
|  | obj = yaffs_GetEquivalentObject(obj); | 
|  | } | 
|  |  | 
|  | if(obj && buf) | 
|  | { | 
|  | buf->st_dev = (int)obj->myDev->genericDevice; | 
|  | buf->st_ino = obj->objectId; | 
|  | buf->st_mode = obj->yst_mode & ~S_IFMT; // clear out file type bits | 
|  |  | 
|  | if(obj->variantType == YAFFS_OBJECT_TYPE_DIRECTORY) | 
|  | { | 
|  | buf->st_mode |= S_IFDIR; | 
|  | } | 
|  | else if(obj->variantType == YAFFS_OBJECT_TYPE_SYMLINK) | 
|  | { | 
|  | buf->st_mode |= S_IFLNK; | 
|  | } | 
|  | else if(obj->variantType == YAFFS_OBJECT_TYPE_FILE) | 
|  | { | 
|  | buf->st_mode |= S_IFREG; | 
|  | } | 
|  |  | 
|  | buf->st_nlink = yaffs_GetObjectLinkCount(obj); | 
|  | buf->st_uid = 0; | 
|  | buf->st_gid = 0;; | 
|  | buf->st_rdev = obj->yst_rdev; | 
|  | buf->st_size = yaffs_GetObjectFileLength(obj); | 
|  | buf->st_blksize = obj->myDev->nDataBytesPerChunk; | 
|  | buf->st_blocks = (buf->st_size + buf->st_blksize -1)/buf->st_blksize; | 
|  | buf->yst_atime = obj->yst_atime; | 
|  | buf->yst_ctime = obj->yst_ctime; | 
|  | buf->yst_mtime = obj->yst_mtime; | 
|  | retVal = 0; | 
|  | } | 
|  | return retVal; | 
|  | } | 
|  |  | 
|  | static int yaffsfs_DoStatOrLStat(const char *path, struct yaffs_stat *buf,int doLStat) | 
|  | { | 
|  | yaffs_Object *obj; | 
|  |  | 
|  | int retVal = -1; | 
|  |  | 
|  | yaffsfs_Lock(); | 
|  | obj = yaffsfs_FindObject(NULL,path,0); | 
|  |  | 
|  | if(!doLStat && obj) | 
|  | { | 
|  | obj = yaffsfs_FollowLink(obj,0); | 
|  | } | 
|  |  | 
|  | if(obj) | 
|  | { | 
|  | retVal = yaffsfs_DoStat(obj,buf); | 
|  | } | 
|  | else | 
|  | { | 
|  | // todo error not found | 
|  | yaffsfs_SetError(-ENOENT); | 
|  | } | 
|  |  | 
|  | yaffsfs_Unlock(); | 
|  |  | 
|  | return retVal; | 
|  |  | 
|  | } | 
|  |  | 
|  | int yaffs_stat(const char *path, struct yaffs_stat *buf) | 
|  | { | 
|  | return yaffsfs_DoStatOrLStat(path,buf,0); | 
|  | } | 
|  |  | 
|  | int yaffs_lstat(const char *path, struct yaffs_stat *buf) | 
|  | { | 
|  | return yaffsfs_DoStatOrLStat(path,buf,1); | 
|  | } | 
|  |  | 
|  | int yaffs_fstat(int fd, struct yaffs_stat *buf) | 
|  | { | 
|  | yaffs_Object *obj; | 
|  |  | 
|  | int retVal = -1; | 
|  |  | 
|  | yaffsfs_Lock(); | 
|  | obj = yaffsfs_GetHandleObject(fd); | 
|  |  | 
|  | if(obj) | 
|  | { | 
|  | retVal = yaffsfs_DoStat(obj,buf); | 
|  | } | 
|  | else | 
|  | { | 
|  | // bad handle | 
|  | yaffsfs_SetError(-EBADF); | 
|  | } | 
|  |  | 
|  | yaffsfs_Unlock(); | 
|  |  | 
|  | return retVal; | 
|  | } | 
|  |  | 
|  | static int yaffsfs_DoChMod(yaffs_Object *obj,mode_t mode) | 
|  | { | 
|  | int result = YAFFS_FAIL; | 
|  |  | 
|  | if(obj) | 
|  | { | 
|  | obj = yaffs_GetEquivalentObject(obj); | 
|  | } | 
|  |  | 
|  | if(obj) | 
|  | { | 
|  | obj->yst_mode = mode; | 
|  | obj->dirty = 1; | 
|  | result = yaffs_FlushFile(obj,0); | 
|  | } | 
|  |  | 
|  | return result == YAFFS_OK ? 0 : -1; | 
|  | } | 
|  |  | 
|  |  | 
|  | int yaffs_chmod(const char *path, mode_t mode) | 
|  | { | 
|  | yaffs_Object *obj; | 
|  |  | 
|  | int retVal = -1; | 
|  |  | 
|  | yaffsfs_Lock(); | 
|  | obj = yaffsfs_FindObject(NULL,path,0); | 
|  |  | 
|  | if(obj) | 
|  | { | 
|  | retVal = yaffsfs_DoChMod(obj,mode); | 
|  | } | 
|  | else | 
|  | { | 
|  | // todo error not found | 
|  | yaffsfs_SetError(-ENOENT); | 
|  | } | 
|  |  | 
|  | yaffsfs_Unlock(); | 
|  |  | 
|  | return retVal; | 
|  |  | 
|  | } | 
|  |  | 
|  |  | 
|  | int yaffs_fchmod(int fd, mode_t mode) | 
|  | { | 
|  | yaffs_Object *obj; | 
|  |  | 
|  | int retVal = -1; | 
|  |  | 
|  | yaffsfs_Lock(); | 
|  | obj = yaffsfs_GetHandleObject(fd); | 
|  |  | 
|  | if(obj) | 
|  | { | 
|  | retVal = yaffsfs_DoChMod(obj,mode); | 
|  | } | 
|  | else | 
|  | { | 
|  | // bad handle | 
|  | yaffsfs_SetError(-EBADF); | 
|  | } | 
|  |  | 
|  | yaffsfs_Unlock(); | 
|  |  | 
|  | return retVal; | 
|  | } | 
|  |  | 
|  |  | 
|  | int yaffs_mkdir(const char *path, mode_t mode) | 
|  | { | 
|  | yaffs_Object *parent = NULL; | 
|  | yaffs_Object *dir = NULL; | 
|  | char *name; | 
|  | int retVal= -1; | 
|  |  | 
|  | yaffsfs_Lock(); | 
|  | parent = yaffsfs_FindDirectory(NULL,path,&name,0); | 
|  | if(parent) | 
|  | dir = yaffs_MknodDirectory(parent,name,mode,0,0); | 
|  | if(dir) | 
|  | { | 
|  | retVal = 0; | 
|  | } | 
|  | else | 
|  | { | 
|  | yaffsfs_SetError(-ENOSPC); // just assume no space for now | 
|  | retVal = -1; | 
|  | } | 
|  |  | 
|  | yaffsfs_Unlock(); | 
|  |  | 
|  | return retVal; | 
|  | } | 
|  |  | 
|  | int yaffs_mount(const char *path) | 
|  | { | 
|  | int retVal=-1; | 
|  | int result=YAFFS_FAIL; | 
|  | yaffs_Device *dev=NULL; | 
|  | char *dummy; | 
|  |  | 
|  | T(YAFFS_TRACE_ALWAYS,("yaffs: Mounting %s\n",path)); | 
|  |  | 
|  | yaffsfs_Lock(); | 
|  | dev = yaffsfs_FindDevice(path,&dummy); | 
|  | if(dev) | 
|  | { | 
|  | if(!dev->isMounted) | 
|  | { | 
|  | result = yaffs_GutsInitialise(dev); | 
|  | if(result == YAFFS_FAIL) | 
|  | { | 
|  | // todo error - mount failed | 
|  | yaffsfs_SetError(-ENOMEM); | 
|  | } | 
|  | retVal = result ? 0 : -1; | 
|  |  | 
|  | } | 
|  | else | 
|  | { | 
|  | //todo error - already mounted. | 
|  | yaffsfs_SetError(-EBUSY); | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | // todo error - no device | 
|  | yaffsfs_SetError(-ENODEV); | 
|  | } | 
|  | yaffsfs_Unlock(); | 
|  | return retVal; | 
|  |  | 
|  | } | 
|  |  | 
|  | int yaffs_unmount(const char *path) | 
|  | { | 
|  | int retVal=-1; | 
|  | yaffs_Device *dev=NULL; | 
|  | char *dummy; | 
|  |  | 
|  | yaffsfs_Lock(); | 
|  | dev = yaffsfs_FindDevice(path,&dummy); | 
|  | if(dev) | 
|  | { | 
|  | if(dev->isMounted) | 
|  | { | 
|  | int i; | 
|  | int inUse; | 
|  |  | 
|  | yaffs_FlushEntireDeviceCache(dev); | 
|  | yaffs_CheckpointSave(dev); | 
|  |  | 
|  | for(i = inUse = 0; i < YAFFSFS_N_HANDLES && !inUse; i++) | 
|  | { | 
|  | if(yaffsfs_handle[i].inUse && yaffsfs_handle[i].obj->myDev == dev) | 
|  | { | 
|  | inUse = 1; // the device is in use, can't unmount | 
|  | } | 
|  | } | 
|  |  | 
|  | if(!inUse) | 
|  | { | 
|  | yaffs_Deinitialise(dev); | 
|  |  | 
|  | retVal = 0; | 
|  | } | 
|  | else | 
|  | { | 
|  | // todo error can't unmount as files are open | 
|  | yaffsfs_SetError(-EBUSY); | 
|  | } | 
|  |  | 
|  | } | 
|  | else | 
|  | { | 
|  | //todo error - not mounted. | 
|  | yaffsfs_SetError(-EINVAL); | 
|  |  | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | // todo error - no device | 
|  | yaffsfs_SetError(-ENODEV); | 
|  | } | 
|  | yaffsfs_Unlock(); | 
|  | return retVal; | 
|  |  | 
|  | } | 
|  |  | 
|  | loff_t yaffs_freespace(const char *path) | 
|  | { | 
|  | loff_t retVal=-1; | 
|  | yaffs_Device *dev=NULL; | 
|  | char *dummy; | 
|  |  | 
|  | yaffsfs_Lock(); | 
|  | dev = yaffsfs_FindDevice(path,&dummy); | 
|  | if(dev  && dev->isMounted) | 
|  | { | 
|  | retVal = yaffs_GetNumberOfFreeChunks(dev); | 
|  | retVal *= dev->nDataBytesPerChunk; | 
|  |  | 
|  | } | 
|  | else | 
|  | { | 
|  | yaffsfs_SetError(-EINVAL); | 
|  | } | 
|  |  | 
|  | yaffsfs_Unlock(); | 
|  | return retVal; | 
|  | } | 
|  |  | 
|  |  | 
|  |  | 
|  | void yaffs_initialise(yaffsfs_DeviceConfiguration *cfgList) | 
|  | { | 
|  |  | 
|  | yaffsfs_DeviceConfiguration *cfg; | 
|  |  | 
|  | yaffsfs_configurationList = cfgList; | 
|  |  | 
|  | yaffsfs_InitHandles(); | 
|  |  | 
|  | cfg = yaffsfs_configurationList; | 
|  |  | 
|  | while(cfg && cfg->prefix && cfg->dev) | 
|  | { | 
|  | cfg->dev->isMounted = 0; | 
|  | cfg->dev->removeObjectCallback = yaffsfs_RemoveObjectCallback; | 
|  | cfg++; | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | // | 
|  | // Directory search stuff. | 
|  |  | 
|  | // | 
|  | // Directory search context | 
|  | // | 
|  | // NB this is an opaque structure. | 
|  |  | 
|  |  | 
|  | typedef struct | 
|  | { | 
|  | __u32 magic; | 
|  | yaffs_dirent de;		/* directory entry being used by this dsc */ | 
|  | char name[NAME_MAX+1];		/* name of directory being searched */ | 
|  | yaffs_Object *dirObj;		/* ptr to directory being searched */ | 
|  | yaffs_Object *nextReturn;	/* obj to be returned by next readddir */ | 
|  | int offset; | 
|  | struct list_head others; | 
|  | } yaffsfs_DirectorySearchContext; | 
|  |  | 
|  |  | 
|  |  | 
|  | static struct list_head search_contexts; | 
|  |  | 
|  |  | 
|  | static void yaffsfs_SetDirRewound(yaffsfs_DirectorySearchContext *dsc) | 
|  | { | 
|  | if(dsc && | 
|  | dsc->dirObj && | 
|  | dsc->dirObj->variantType == YAFFS_OBJECT_TYPE_DIRECTORY){ | 
|  |  | 
|  | dsc->offset = 0; | 
|  |  | 
|  | if( list_empty(&dsc->dirObj->variant.directoryVariant.children)){ | 
|  | dsc->nextReturn = NULL; | 
|  | } else { | 
|  | dsc->nextReturn = list_entry(dsc->dirObj->variant.directoryVariant.children.next, | 
|  | yaffs_Object,siblings); | 
|  | } | 
|  | } else { | 
|  | /* Hey someone isn't playing nice! */ | 
|  | } | 
|  | } | 
|  |  | 
|  | static void yaffsfs_DirAdvance(yaffsfs_DirectorySearchContext *dsc) | 
|  | { | 
|  | if(dsc && | 
|  | dsc->dirObj && | 
|  | dsc->dirObj->variantType == YAFFS_OBJECT_TYPE_DIRECTORY){ | 
|  |  | 
|  | if( dsc->nextReturn == NULL || | 
|  | list_empty(&dsc->dirObj->variant.directoryVariant.children)){ | 
|  | dsc->nextReturn = NULL; | 
|  | } else { | 
|  | struct list_head *next = dsc->nextReturn->siblings.next; | 
|  |  | 
|  | if( next == &dsc->dirObj->variant.directoryVariant.children) | 
|  | dsc->nextReturn = NULL; /* end of list */ | 
|  | else | 
|  | dsc->nextReturn = list_entry(next,yaffs_Object,siblings); | 
|  | } | 
|  | } else { | 
|  | /* Hey someone isn't playing nice! */ | 
|  | } | 
|  | } | 
|  |  | 
|  | static void yaffsfs_RemoveObjectCallback(yaffs_Object *obj) | 
|  | { | 
|  |  | 
|  | struct list_head *i; | 
|  | yaffsfs_DirectorySearchContext *dsc; | 
|  |  | 
|  | /* if search contexts not initilised then skip */ | 
|  | if(!search_contexts.next) | 
|  | return; | 
|  |  | 
|  | /* Iteratethrough 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, yaffsfs_DirectorySearchContext,others); | 
|  | if(dsc->nextReturn == obj) | 
|  | yaffsfs_DirAdvance(dsc); | 
|  | } | 
|  | } | 
|  |  | 
|  | } | 
|  |  | 
|  | yaffs_DIR *yaffs_opendir(const char *dirname) | 
|  | { | 
|  | yaffs_DIR *dir = NULL; | 
|  | yaffs_Object *obj = NULL; | 
|  | yaffsfs_DirectorySearchContext *dsc = NULL; | 
|  |  | 
|  | yaffsfs_Lock(); | 
|  |  | 
|  | obj = yaffsfs_FindObject(NULL,dirname,0); | 
|  |  | 
|  | if(obj && obj->variantType == YAFFS_OBJECT_TYPE_DIRECTORY) | 
|  | { | 
|  |  | 
|  | dsc = YMALLOC(sizeof(yaffsfs_DirectorySearchContext)); | 
|  | dir = (yaffs_DIR *)dsc; | 
|  | if(dsc) | 
|  | { | 
|  | memset(dsc,0,sizeof(yaffsfs_DirectorySearchContext)); | 
|  | dsc->magic = YAFFS_MAGIC; | 
|  | dsc->dirObj = obj; | 
|  | 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);		} | 
|  |  | 
|  | } | 
|  |  | 
|  | yaffsfs_Unlock(); | 
|  |  | 
|  | return dir; | 
|  | } | 
|  |  | 
|  | struct yaffs_dirent *yaffs_readdir(yaffs_DIR *dirp) | 
|  | { | 
|  | yaffsfs_DirectorySearchContext *dsc = (yaffsfs_DirectorySearchContext *)dirp; | 
|  | struct yaffs_dirent *retVal = NULL; | 
|  |  | 
|  | yaffsfs_Lock(); | 
|  |  | 
|  | if(dsc && dsc->magic == YAFFS_MAGIC){ | 
|  | yaffsfs_SetError(0); | 
|  | if(dsc->nextReturn){ | 
|  | dsc->de.d_ino = yaffs_GetEquivalentObject(dsc->nextReturn)->objectId; | 
|  | dsc->de.d_dont_use = (unsigned)dsc->nextReturn; | 
|  | dsc->de.d_off = dsc->offset++; | 
|  | yaffs_GetObjectName(dsc->nextReturn,dsc->de.d_name,NAME_MAX); | 
|  | if(strlen(dsc->de.d_name) == 0) | 
|  | { | 
|  | // this should not happen! | 
|  | strcpy(dsc->de.d_name,"zz"); | 
|  | } | 
|  | dsc->de.d_reclen = sizeof(struct yaffs_dirent); | 
|  | retVal = &dsc->de; | 
|  | yaffsfs_DirAdvance(dsc); | 
|  | } else | 
|  | retVal = NULL; | 
|  | } | 
|  | else | 
|  | { | 
|  | yaffsfs_SetError(-EBADF); | 
|  | } | 
|  |  | 
|  | yaffsfs_Unlock(); | 
|  |  | 
|  | return retVal; | 
|  |  | 
|  | } | 
|  |  | 
|  |  | 
|  | void yaffs_rewinddir(yaffs_DIR *dirp) | 
|  | { | 
|  | yaffsfs_DirectorySearchContext *dsc = (yaffsfs_DirectorySearchContext *)dirp; | 
|  |  | 
|  | yaffsfs_Lock(); | 
|  |  | 
|  | yaffsfs_SetDirRewound(dsc); | 
|  |  | 
|  | yaffsfs_Unlock(); | 
|  | } | 
|  |  | 
|  |  | 
|  | int yaffs_closedir(yaffs_DIR *dirp) | 
|  | { | 
|  | yaffsfs_DirectorySearchContext *dsc = (yaffsfs_DirectorySearchContext *)dirp; | 
|  |  | 
|  | yaffsfs_Lock(); | 
|  | dsc->magic = 0; | 
|  | list_del(&dsc->others); /* unhook from list */ | 
|  | YFREE(dsc); | 
|  | yaffsfs_Unlock(); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | // end of directory stuff | 
|  |  | 
|  |  | 
|  | int yaffs_symlink(const char *oldpath, const char *newpath) | 
|  | { | 
|  | yaffs_Object *parent = NULL; | 
|  | yaffs_Object *obj; | 
|  | char *name; | 
|  | int retVal= -1; | 
|  | int mode = 0; // ignore for now | 
|  |  | 
|  | yaffsfs_Lock(); | 
|  | parent = yaffsfs_FindDirectory(NULL,newpath,&name,0); | 
|  | obj = yaffs_MknodSymLink(parent,name,mode,0,0,oldpath); | 
|  | if(obj) | 
|  | { | 
|  | retVal = 0; | 
|  | } | 
|  | else | 
|  | { | 
|  | yaffsfs_SetError(-ENOSPC); // just assume no space for now | 
|  | retVal = -1; | 
|  | } | 
|  |  | 
|  | yaffsfs_Unlock(); | 
|  |  | 
|  | return retVal; | 
|  |  | 
|  | } | 
|  |  | 
|  | int yaffs_readlink(const char *path, char *buf, int bufsiz) | 
|  | { | 
|  | yaffs_Object *obj = NULL; | 
|  | int retVal; | 
|  |  | 
|  |  | 
|  | yaffsfs_Lock(); | 
|  |  | 
|  | obj = yaffsfs_FindObject(NULL,path,0); | 
|  |  | 
|  | if(!obj) | 
|  | { | 
|  | yaffsfs_SetError(-ENOENT); | 
|  | retVal = -1; | 
|  | } | 
|  | else if(obj->variantType != YAFFS_OBJECT_TYPE_SYMLINK) | 
|  | { | 
|  | yaffsfs_SetError(-EINVAL); | 
|  | retVal = -1; | 
|  | } | 
|  | else | 
|  | { | 
|  | char *alias = obj->variant.symLinkVariant.alias; | 
|  | memset(buf,0,bufsiz); | 
|  | strncpy(buf,alias,bufsiz - 1); | 
|  | retVal = 0; | 
|  | } | 
|  | yaffsfs_Unlock(); | 
|  | return retVal; | 
|  | } | 
|  |  | 
|  | int yaffs_link(const char *oldpath, const char *newpath) | 
|  | { | 
|  | // Creates a link called newpath to existing oldpath | 
|  | yaffs_Object *obj = NULL; | 
|  | yaffs_Object *target = NULL; | 
|  | int retVal = 0; | 
|  |  | 
|  |  | 
|  | yaffsfs_Lock(); | 
|  |  | 
|  | obj = yaffsfs_FindObject(NULL,oldpath,0); | 
|  | target = yaffsfs_FindObject(NULL,newpath,0); | 
|  |  | 
|  | if(!obj) | 
|  | { | 
|  | yaffsfs_SetError(-ENOENT); | 
|  | retVal = -1; | 
|  | } | 
|  | else if(target) | 
|  | { | 
|  | yaffsfs_SetError(-EEXIST); | 
|  | retVal = -1; | 
|  | } | 
|  | else | 
|  | { | 
|  | yaffs_Object *newdir = NULL; | 
|  | yaffs_Object *link = NULL; | 
|  |  | 
|  | char *newname; | 
|  |  | 
|  | newdir = yaffsfs_FindDirectory(NULL,newpath,&newname,0); | 
|  |  | 
|  | if(!newdir) | 
|  | { | 
|  | yaffsfs_SetError(-ENOTDIR); | 
|  | retVal = -1; | 
|  | } | 
|  | else if(newdir->myDev != obj->myDev) | 
|  | { | 
|  | yaffsfs_SetError(-EXDEV); | 
|  | retVal = -1; | 
|  | } | 
|  | if(newdir && strlen(newname) > 0) | 
|  | { | 
|  | link = yaffs_Link(newdir,newname,obj); | 
|  | if(link) | 
|  | retVal = 0; | 
|  | else | 
|  | { | 
|  | yaffsfs_SetError(-ENOSPC); | 
|  | retVal = -1; | 
|  | } | 
|  |  | 
|  | } | 
|  | } | 
|  | yaffsfs_Unlock(); | 
|  |  | 
|  | return retVal; | 
|  | } | 
|  |  | 
|  | int yaffs_mknod(const char *pathname, mode_t mode, dev_t dev); | 
|  |  | 
|  | int yaffs_DumpDevStruct(const char *path) | 
|  | { | 
|  | char *rest; | 
|  |  | 
|  | yaffs_Object *obj = yaffsfs_FindRoot(path,&rest); | 
|  |  | 
|  | if(obj) | 
|  | { | 
|  | yaffs_Device *dev = obj->myDev; | 
|  |  | 
|  | printf("\n" | 
|  | "nPageWrites.......... %d\n" | 
|  | "nPageReads........... %d\n" | 
|  | "nBlockErasures....... %d\n" | 
|  | "nGCCopies............ %d\n" | 
|  | "garbageCollections... %d\n" | 
|  | "passiveGarbageColl'ns %d\n" | 
|  | "\n", | 
|  | dev->nPageWrites, | 
|  | dev->nPageReads, | 
|  | dev->nBlockErasures, | 
|  | dev->nGCCopies, | 
|  | dev->garbageCollections, | 
|  | dev->passiveGarbageCollections | 
|  | ); | 
|  |  | 
|  | } | 
|  | return 0; | 
|  | } |