2 * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
4 * Copyright (C) 2002-2010 Aleph One Ltd.
5 * for Toby Churchill Ltd and Brightstar Engineering
7 * Created by Charles Manning <charles@aleph1.co.uk>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
15 #include "yaffs_guts.h"
18 #include "yaffs_trace.h"
20 #include <string.h> /* for memset */
22 #define YAFFSFS_MAX_SYMLINK_DEREFERENCES 5
25 #define NULL ((void *)0)
29 /* YAFFSFS_RW_SIZE must be a power of 2 */
30 #define YAFFSFS_RW_SHIFT (13)
31 #define YAFFSFS_RW_SIZE (1<<YAFFSFS_RW_SHIFT)
33 /* Some forward references */
34 static yaffs_obj_t *yaffsfs_FindObject(yaffs_obj_t *relativeDirectory, const YCHAR *path, int symDepth, int getEquiv);
35 static void yaffsfs_RemoveObjectCallback(yaffs_obj_t *obj);
37 unsigned int yaffs_wr_attempts;
41 * There are open inodes in yaffsfs_Inode.
42 * There are open handles in yaffsfs_Handle.
44 * Things are structured this way to be like the Linux VFS model
45 * so that interactions with the yaffs guts calls are similar.
46 * That means more common code paths and less special code.
47 * That means better testing etc.
51 int count; /* Number of handles accessing this inode */
61 int inodeId:12; /* Index to corresponding yaffsfs_Inode */
62 int useCount:10; /* Use count for this handle */
63 __u32 position; /* current position in file */
66 static yaffsfs_Inode yaffsfs_inode[YAFFSFS_N_HANDLES];
67 static yaffsfs_Handle yaffsfs_handle[YAFFSFS_N_HANDLES];
68 static int yaffsfs_handlesInitialised;
72 * Inilitalise handle management on start-up.
75 static void yaffsfs_InitHandles(void)
78 if(yaffsfs_handlesInitialised)
81 memset(yaffsfs_inode,0,sizeof(yaffsfs_inode));
82 memset(yaffsfs_handle,0,sizeof(yaffsfs_handle));
83 for(i = 0; i < YAFFSFS_N_HANDLES; i++)
84 yaffsfs_handle[i].inodeId = -1;
87 yaffsfs_Handle *yaffsfs_GetHandlePointer(int h)
89 if(h < 0 || h >= YAFFSFS_N_HANDLES)
92 return &yaffsfs_handle[h];
95 yaffsfs_Inode *yaffsfs_GetInodePointer(int handle)
97 yaffsfs_Handle *h = yaffsfs_GetHandlePointer(handle);
99 if(h && h->useCount > 0 && h->inodeId >= 0 && h->inodeId < YAFFSFS_N_HANDLES)
100 return &yaffsfs_inode[h->inodeId];
105 yaffs_obj_t *yaffsfs_GetHandleObject(int handle)
107 yaffsfs_Inode *in = yaffsfs_GetInodePointer(handle);
116 * yaffsfs_FindInodeIdForObject
117 * Find the inode entry for an object, if it exists.
120 static int yaffsfs_FindInodeIdForObject(yaffs_obj_t *obj)
126 obj = yaffs_get_equivalent_obj(obj);
128 /* Look for it in open inode table*/
129 for(i = 0; i < YAFFSFS_N_HANDLES && ret < 0; i++){
130 if(yaffsfs_inode[i].iObj == obj)
137 * yaffsfs_GetInodeIdForObject
138 * Grab an inode entry when opening a new inode.
140 static int yaffsfs_GetInodeIdForObject(yaffs_obj_t *obj)
144 yaffsfs_Inode *in = NULL;
147 obj = yaffs_get_equivalent_obj(obj);
149 ret = yaffsfs_FindInodeIdForObject(obj);
151 for(i = 0; i < YAFFSFS_N_HANDLES && ret < 0; i++){
152 if(!yaffsfs_inode[i].iObj)
157 in = &yaffsfs_inode[ret];
169 static int yaffsfs_CountHandles(yaffs_obj_t *obj)
171 int i = yaffsfs_FindInodeIdForObject(obj);
174 return yaffsfs_inode[i].count;
179 static void yaffsfs_ReleaseInode(yaffsfs_Inode *in)
188 obj->my_inode = NULL;
193 static void yaffsfs_PutInode(int inodeId)
195 if(inodeId >= 0 && inodeId < YAFFSFS_N_HANDLES){
196 yaffsfs_Inode *in = & yaffsfs_inode[inodeId];
199 yaffsfs_ReleaseInode(in);
206 * Grab a handle (when opening a file)
209 static int yaffsfs_GetNewHandle(void)
214 for(i = 0; i < YAFFSFS_N_HANDLES; i++){
215 h = yaffsfs_GetHandlePointer(i);
217 /* todo bug: should never happen */
220 memset(h,0,sizeof(yaffsfs_Handle));
231 * Increase use of handle when reading/writing a file
233 static int yaffsfs_GetHandle(int handle)
235 yaffsfs_Handle *h = yaffsfs_GetHandlePointer(handle);
237 if(h && h->useCount > 0){
245 * Let go of a handle when closing a file or aborting an open or
246 * ending a read or write.
248 static int yaffsfs_PutHandle(int handle)
250 yaffsfs_Handle *h = yaffsfs_GetHandlePointer(handle);
252 if(h && h->useCount > 0){
256 yaffsfs_PutInode(h->inodeId);
268 * Stuff to search for a directory from a path
272 int yaffsfs_Match(YCHAR a, YCHAR b)
278 int yaffsfs_IsPathDivider(YCHAR ch)
280 const YCHAR *str = YAFFS_PATH_DIVIDERS;
293 YLIST_HEAD(yaffsfs_deviceList);
298 * Scan the configuration list to find the root.
299 * Curveballs: Should match paths that end in '/' too
300 * Curveball2 Might have "/x/ and "/x/y". Need to return the longest match
302 static yaffs_dev_t *yaffsfs_FindDevice(const YCHAR *path, YCHAR **restOfPath)
304 struct ylist_head *cfg;
305 const YCHAR *leftOver;
307 yaffs_dev_t *retval = NULL;
308 yaffs_dev_t *dev = NULL;
310 int longestMatch = -1;
314 * Check all configs, choose the one that:
315 * 1) Actually matches a prefix (ie /a amd /abc will not match
316 * 2) Matches the longest.
318 ylist_for_each(cfg, &yaffsfs_deviceList){
319 dev = ylist_entry(cfg, yaffs_dev_t, dev_list);
326 while(matching && *p && *leftOver){
327 /* Skip over any /s */
328 while(yaffsfs_IsPathDivider(*p))
331 /* Skip over any /s */
332 while(yaffsfs_IsPathDivider(*leftOver))
335 /* Now match the text part */
337 *p && !yaffsfs_IsPathDivider(*p) &&
338 *leftOver && !yaffsfs_IsPathDivider(*leftOver)){
339 if(yaffsfs_Match(*p,*leftOver)){
349 /* Skip over any /s in leftOver */
350 while(yaffsfs_IsPathDivider(*leftOver))
353 // Skip over any /s in p
354 while(yaffsfs_IsPathDivider(*p))
357 // p should now be at the end of the string (ie. fully matched)
361 if( matching && (thisMatchLength > longestMatch))
364 *restOfPath = (YCHAR *)leftOver;
366 longestMatch = thisMatchLength;
374 static yaffs_dev_t *yaffsfs_FindDevice(const YCHAR *path, YCHAR **restOfPath)
376 yaffsfs_DeviceConfiguration *cfg = yaffsfs_configurationList;
377 const YCHAR *leftOver;
379 yaffs_dev_t *retval = NULL;
381 int longestMatch = -1;
384 * Check all configs, choose the one that:
385 * 1) Actually matches a prefix (ie /a amd /abc will not match
386 * 2) Matches the longest.
388 while(cfg && cfg->prefix && cfg->dev){
393 while(*p && /* unmatched part of prefix */
394 !(yaffsfs_IsPathDivider(*p) && (p[1] == 0)) &&
395 *leftOver && yaffsfs_Match(*p,*leftOver)){
402 if((!*p || (yaffsfs_IsPathDivider(*p) && (p[1] == 0))) && /* end of prefix */
403 (!*leftOver || yaffsfs_IsPathDivider(*leftOver)) && /* no more in this path name part */
404 (thisMatchLength > longestMatch)){
406 *restOfPath = (YCHAR *)leftOver;
408 longestMatch = thisMatchLength;
416 static yaffs_obj_t *yaffsfs_FindRoot(const YCHAR *path, YCHAR **restOfPath)
421 dev= yaffsfs_FindDevice(path,restOfPath);
422 if(dev && dev->is_mounted){
423 return dev->root_dir;
428 static yaffs_obj_t *yaffsfs_FollowLink(yaffs_obj_t *obj,int symDepth)
432 obj = yaffs_get_equivalent_obj(obj);
434 while(obj && obj->variant_type == YAFFS_OBJECT_TYPE_SYMLINK){
435 YCHAR *alias = obj->variant.symlink_variant.alias;
437 if(yaffsfs_IsPathDivider(*alias))
438 /* Starts with a /, need to scan from root up */
439 obj = yaffsfs_FindObject(NULL,alias,symDepth++,1);
441 /* Relative to here, so use the parent of the symlink as a start */
442 obj = yaffsfs_FindObject(obj->parent,alias,symDepth++,1);
449 * yaffsfs_FindDirectory
450 * Parse a path to determine the directory and the name within the directory.
452 * eg. "/data/xx/ff" --> puts name="ff" and returns the directory "/data/xx"
454 static yaffs_obj_t *yaffsfs_DoFindDirectory(yaffs_obj_t *startDir,
455 const YCHAR *path, YCHAR **name, int symDepth)
459 YCHAR str[YAFFS_MAX_NAME_LENGTH+1];
462 if(symDepth > YAFFSFS_MAX_SYMLINK_DEREFERENCES)
467 restOfPath = (YCHAR *)path;
470 dir = yaffsfs_FindRoot(path,&restOfPath);
475 * curve ball: also throw away surplus '/'
476 * eg. "/ram/x////ff" gets treated the same as "/ram/x/ff"
478 while(yaffsfs_IsPathDivider(*restOfPath))
479 restOfPath++; /* get rid of '/' */
484 while(*restOfPath && !yaffsfs_IsPathDivider(*restOfPath)){
485 if (i < YAFFS_MAX_NAME_LENGTH){
486 str[i] = *restOfPath;
494 /* got to the end of the string */
497 if(yaffs_strcmp(str,_Y(".")) == 0)
501 else if(yaffs_strcmp(str,_Y("..")) == 0)
504 dir = yaffs_find_by_name(dir,str);
506 dir = yaffsfs_FollowLink(dir,symDepth);
508 if(dir && dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY)
513 /* directory did not exist. */
517 static yaffs_obj_t *yaffsfs_FindDirectory(yaffs_obj_t *relativeDirectory,
518 const YCHAR *path,YCHAR **name,int symDepth)
520 return yaffsfs_DoFindDirectory(relativeDirectory,path,name,symDepth);
524 * yaffsfs_FindObject turns a path for an existing object into the object
526 static yaffs_obj_t *yaffsfs_FindObject(yaffs_obj_t *relativeDirectory, const YCHAR *path,int symDepth, int getEquiv)
532 dir = yaffsfs_FindDirectory(relativeDirectory,path,&name,symDepth);
535 obj = yaffs_find_by_name(dir,name);
540 obj = yaffs_get_equivalent_obj(obj);
546 int yaffs_dup(int fd)
549 yaffsfs_Handle *oldPtr = NULL;
550 yaffsfs_Handle *newPtr = NULL;
554 oldPtr = yaffsfs_GetHandlePointer(fd);
555 if(oldPtr && oldPtr->useCount > 0)
556 newHandle = yaffsfs_GetNewHandle();
558 newPtr = yaffsfs_GetHandlePointer(newHandle);
566 yaffsfs_SetError(-EBADF);
568 yaffsfs_SetError(-ENOMEM);
576 int yaffs_open_sharing(const YCHAR *path, int oflag, int mode, int sharing)
578 yaffs_obj_t *obj = NULL;
579 yaffs_obj_t *dir = NULL;
582 yaffsfs_Handle *yh = NULL;
585 int errorReported = 0;
586 int rwflags = oflag & ( O_RDWR | O_RDONLY | O_WRONLY);
587 __u8 shareRead = (sharing & YAFFS_SHARE_READ) ? 1 : 0;
588 __u8 shareWrite = (sharing & YAFFS_SHARE_WRITE) ? 1 : 0;
589 __u8 sharedReadAllowed;
590 __u8 sharedWriteAllowed;
596 /* O_EXCL only has meaning if O_CREAT is specified */
597 if(!(oflag & O_CREAT))
600 /* O_TRUNC has no meaning if (O_CREAT | O_EXCL) is specified */
601 if( (oflag & O_CREAT) & (oflag & O_EXCL))
604 /* Todo: Are there any more flag combos to sanitise ? */
606 /* Figure out if reading or writing is requested */
608 readRequested = (rwflags == O_RDWR || rwflags == O_RDONLY) ? 1 : 0;
609 writeRequested = (rwflags == O_RDWR || rwflags == O_WRONLY) ? 1 : 0;
613 handle = yaffsfs_GetNewHandle();
617 yh = yaffsfs_GetHandlePointer(handle);
619 /* try to find the exisiting object */
620 obj = yaffsfs_FindObject(NULL,path,0,1);
622 obj = yaffsfs_FollowLink(obj,symDepth++);
625 obj->variant_type != YAFFS_OBJECT_TYPE_FILE &&
626 obj->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY)
631 /* The file already exists or it might be a directory */
633 /* If it is a directory then we can't open it as a file */
634 if(obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY){
636 yaffsfs_SetError(-EISDIR);
640 /* Open should fail if O_CREAT and O_EXCL are specified since
643 if((oflag & O_EXCL) && (oflag & O_CREAT)){
645 yaffsfs_SetError(-EEXIST);
649 /* Check file permissions */
650 if( readRequested && !(obj->yst_mode & S_IREAD))
653 if( writeRequested && !(obj->yst_mode & S_IWRITE))
656 /* Check sharing of an existing object. */
660 sharedReadAllowed = 1;
661 sharedWriteAllowed = 1;
664 for( i = 0; i < YAFFSFS_N_HANDLES; i++){
665 hx = &yaffsfs_handle[i];
666 if(hx->useCount > 0 &&
668 yaffsfs_inode[hx->inodeId].iObj == obj){
670 sharedReadAllowed = 0;
672 sharedWriteAllowed = 0;
682 if((!sharedReadAllowed && readRequested)||
683 (!shareRead && alreadyReading) ||
684 (!sharedWriteAllowed && writeRequested) ||
685 (!shareWrite && alreadyWriting)){
687 yaffsfs_SetError(-EBUSY);
692 } else if((oflag & O_CREAT)) {
693 /* Let's see if we can create this file */
694 dir = yaffsfs_FindDirectory(NULL,path,&name,0);
695 if(dir && dir->my_dev->read_only){
696 yaffsfs_SetError(-EINVAL);
699 obj = yaffs_create_file(dir,name,mode,0,0);
701 yaffsfs_SetError(-ENOTDIR);
706 if(obj && !openDenied) {
707 int inodeId = yaffsfs_GetInodeIdForObject(obj);
711 * Todo: Fix any problem if inodes run out, though that
712 * can't happen if the number of inode items >= number of handles.
716 yh->inodeId = inodeId;
717 yh->reading = readRequested;
718 yh->writing = writeRequested;
719 yh->append = (oflag & O_APPEND) ? 1 : 0;
721 yh->shareRead = shareRead;
722 yh->shareWrite = shareWrite;
724 /* Hook inode to object */
725 obj->my_inode = (void*) &yaffsfs_inode[inodeId];
727 if((oflag & O_TRUNC) && yh->writing)
728 yaffs_resize_file(obj,0);
730 yaffsfs_PutHandle(handle);
732 yaffsfs_SetError(-EACCES);
744 int yaffs_open(const YCHAR *path, int oflag, int mode)
746 return yaffs_open_sharing(path, oflag, mode, YAFFS_SHARE_READ | YAFFS_SHARE_WRITE);
749 int yaffs_Dofsync(int fd,int datasync)
751 yaffsfs_Handle *h = NULL;
756 h = yaffsfs_GetHandlePointer(fd);
758 if(h && h->useCount > 0)
760 yaffs_flush_file(yaffsfs_inode[h->inodeId].iObj,1,datasync);
763 yaffsfs_SetError(-EBADF);
772 int yaffs_fsync(int fd)
774 return yaffs_Dofsync(fd,0);
777 int yaffs_flush(int fd)
779 return yaffs_fsync(fd);
782 int yaffs_fdatasync(int fd)
784 return yaffs_Dofsync(fd,1);
787 int yaffs_close(int fd)
789 yaffsfs_Handle *h = NULL;
794 h = yaffsfs_GetHandlePointer(fd);
796 if(h && h->useCount > 0) {
798 yaffs_flush_file(yaffsfs_inode[h->inodeId].iObj,1,0);
799 yaffsfs_PutHandle(fd);
803 yaffsfs_SetError(-EBADF);
814 int yaffsfs_do_read(int fd, void *vbuf, unsigned int nbyte, int isPread, int offset)
816 yaffsfs_Handle *h = NULL;
817 yaffs_obj_t *obj = NULL;
823 unsigned int maxRead;
824 __u8 *buf = (__u8 *)vbuf;
827 h = yaffsfs_GetHandlePointer(fd);
828 obj = yaffsfs_GetHandleObject(fd);
832 yaffsfs_SetError(-EBADF);
834 } else if(!h->reading){
835 /* Not a reading handle */
836 yaffsfs_SetError(-EINVAL);
842 startPos = h->position;
846 if(yaffs_get_obj_length(obj) > pos)
847 maxRead = yaffs_get_obj_length(obj) - pos;
855 yaffsfs_GetHandle(fd);
858 nToRead = YAFFSFS_RW_SIZE - (pos & (YAFFSFS_RW_SIZE -1));
861 nRead = yaffs_file_rd(obj,buf,pos,nToRead);
872 nbyte = 0; /* no more to read */
882 yaffsfs_PutHandle(fd);
886 h->position = startPos + totalRead;
896 return (totalRead >= 0) ? totalRead : -1;
900 int yaffs_read(int fd, void *buf, unsigned int nbyte)
902 return yaffsfs_do_read(fd, buf, nbyte, 0, 0);
905 int yaffs_pread(int fd, void *buf, unsigned int nbyte, unsigned int offset)
907 return yaffsfs_do_read(fd, buf, nbyte, 1, offset);
910 int yaffsfs_do_write(int fd, const void *vbuf, unsigned int nbyte, int isPwrite, int offset)
912 yaffsfs_Handle *h = NULL;
913 yaffs_obj_t *obj = NULL;
917 int totalWritten = 0;
918 int write_trhrough = 0;
920 const __u8 *buf = (const __u8 *)vbuf;
923 h = yaffsfs_GetHandlePointer(fd);
924 obj = yaffsfs_GetHandleObject(fd);
928 yaffsfs_SetError(-EBADF);
930 } else if( h && obj && (!h->writing || obj->my_dev->read_only)){
931 yaffsfs_SetError(-EINVAL);
935 startPos = yaffs_get_obj_length(obj);
939 startPos = h->position;
941 yaffsfs_GetHandle(fd);
944 nToWrite = YAFFSFS_RW_SIZE - (pos & (YAFFSFS_RW_SIZE -1));
948 nWritten = yaffs_wr_file(obj,buf,pos,nToWrite,write_trhrough);
950 totalWritten += nWritten;
955 if(nWritten == nToWrite)
960 if(nWritten < 1 && totalWritten < 1){
961 yaffsfs_SetError(-ENOSPC);
971 yaffsfs_PutHandle(fd);
975 h->position = startPos + totalWritten;
984 return (totalWritten >= 0) ? totalWritten : -1;
987 int yaffs_write(int fd, const void *buf, unsigned int nbyte)
989 return yaffsfs_do_write(fd, buf, nbyte, 0, 0);
992 int yaffs_pwrite(int fd, const void *buf, unsigned int nbyte, unsigned int offset)
994 return yaffsfs_do_write(fd, buf, nbyte, 1, offset);
998 int yaffs_truncate(const YCHAR *path,off_t new_size)
1000 yaffs_obj_t *obj = NULL;
1001 int result = YAFFS_FAIL;
1005 obj = yaffsfs_FindObject(NULL,path,0,1);
1008 yaffsfs_SetError(-ENOENT);
1009 else if(obj->variant_type != YAFFS_OBJECT_TYPE_FILE)
1010 yaffsfs_SetError(-EISDIR);
1011 else if(obj->my_dev->read_only)
1012 yaffsfs_SetError(-EINVAL);
1014 result = yaffs_resize_file(obj,new_size);
1019 return (result) ? 0 : -1;
1022 int yaffs_ftruncate(int fd, off_t new_size)
1024 yaffsfs_Handle *h = NULL;
1025 yaffs_obj_t *obj = NULL;
1029 h = yaffsfs_GetHandlePointer(fd);
1030 obj = yaffsfs_GetHandleObject(fd);
1034 yaffsfs_SetError(-EBADF);
1035 else if(obj->my_dev->read_only)
1036 yaffsfs_SetError(-EINVAL);
1038 /* resize the file */
1039 result = yaffs_resize_file(obj,new_size);
1043 return (result) ? 0 : -1;
1047 off_t yaffs_lseek(int fd, off_t offset, int whence)
1049 yaffsfs_Handle *h = NULL;
1050 yaffs_obj_t *obj = NULL;
1055 h = yaffsfs_GetHandlePointer(fd);
1056 obj = yaffsfs_GetHandleObject(fd);
1060 yaffsfs_SetError(-EBADF);
1061 else if(whence == SEEK_SET){
1065 else if(whence == SEEK_CUR) {
1066 if( (h->position + offset) >= 0)
1067 pos = (h->position + offset);
1069 else if(whence == SEEK_END) {
1070 fSize = yaffs_get_obj_length(obj);
1071 if(fSize >= 0 && (fSize + offset) >= 0)
1072 pos = fSize + offset;
1088 int yaffsfs_DoUnlink(const YCHAR *path,int isDirectory)
1090 yaffs_obj_t *dir = NULL;
1091 yaffs_obj_t *obj = NULL;
1093 int result = YAFFS_FAIL;
1097 obj = yaffsfs_FindObject(NULL,path,0,0);
1098 dir = yaffsfs_FindDirectory(NULL,path,&name,0);
1100 yaffsfs_SetError(-ENOTDIR);
1102 yaffsfs_SetError(-ENOENT);
1103 else if(obj->my_dev->read_only)
1104 yaffsfs_SetError(-EINVAL);
1105 else if(!isDirectory && obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY)
1106 yaffsfs_SetError(-EISDIR);
1107 else if(isDirectory && obj->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY)
1108 yaffsfs_SetError(-ENOTDIR);
1110 result = yaffs_unlinker(dir,name);
1112 if(result == YAFFS_FAIL && isDirectory)
1113 yaffsfs_SetError(-ENOTEMPTY);
1120 return (result == YAFFS_FAIL) ? -1 : 0;
1124 int yaffs_rmdir(const YCHAR *path)
1126 return yaffsfs_DoUnlink(path,1);
1129 int yaffs_unlink(const YCHAR *path)
1131 return yaffsfs_DoUnlink(path,0);
1134 int yaffs_rename(const YCHAR *oldPath, const YCHAR *newPath)
1136 yaffs_obj_t *olddir = NULL;
1137 yaffs_obj_t *newdir = NULL;
1138 yaffs_obj_t *obj = NULL;
1141 int result= YAFFS_FAIL;
1142 int rename_allowed = 1;
1146 olddir = yaffsfs_FindDirectory(NULL,oldPath,&oldname,0);
1147 newdir = yaffsfs_FindDirectory(NULL,newPath,&newname,0);
1148 obj = yaffsfs_FindObject(NULL,oldPath,0,0);
1150 if(!olddir || !newdir || !obj) {
1152 yaffsfs_SetError(-EBADF);
1154 } else if(obj->my_dev->read_only){
1155 yaffsfs_SetError(-EINVAL);
1157 } else if(olddir->my_dev != newdir->my_dev) {
1158 /* oops must be on same device */
1160 yaffsfs_SetError(-EXDEV);
1162 } else if(obj && obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) {
1164 * It is a directory, check that it is not being renamed to
1165 * being its own decendent.
1166 * Do this by tracing from the new directory back to the root, checking for obj
1169 yaffs_obj_t *xx = newdir;
1171 while( rename_allowed && xx){
1177 yaffsfs_SetError(-EACCES);
1181 result = yaffs_rename_obj(olddir,oldname,newdir,newname);
1185 return (result == YAFFS_FAIL) ? -1 : 0;
1189 static int yaffsfs_DoStat(yaffs_obj_t *obj,struct yaffs_stat *buf)
1193 obj = yaffs_get_equivalent_obj(obj);
1196 buf->st_dev = (int)obj->my_dev->os_context;
1197 buf->st_ino = obj->obj_id;
1198 buf->st_mode = obj->yst_mode & ~S_IFMT; /* clear out file type bits */
1200 if(obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY)
1201 buf->st_mode |= S_IFDIR;
1202 else if(obj->variant_type == YAFFS_OBJECT_TYPE_SYMLINK)
1203 buf->st_mode |= S_IFLNK;
1204 else if(obj->variant_type == YAFFS_OBJECT_TYPE_FILE)
1205 buf->st_mode |= S_IFREG;
1207 buf->st_nlink = yaffs_get_obj_link_count(obj);
1210 buf->st_rdev = obj->yst_rdev;
1211 buf->st_size = yaffs_get_obj_length(obj);
1212 buf->st_blksize = obj->my_dev->data_bytes_per_chunk;
1213 buf->st_blocks = (buf->st_size + buf->st_blksize -1)/buf->st_blksize;
1214 #if CONFIG_YAFFS_WINCE
1215 buf->yst_wince_atime[0] = obj->win_atime[0];
1216 buf->yst_wince_atime[1] = obj->win_atime[1];
1217 buf->yst_wince_ctime[0] = obj->win_ctime[0];
1218 buf->yst_wince_ctime[1] = obj->win_ctime[1];
1219 buf->yst_wince_mtime[0] = obj->win_mtime[0];
1220 buf->yst_wince_mtime[1] = obj->win_mtime[1];
1222 buf->yst_atime = obj->yst_atime;
1223 buf->yst_ctime = obj->yst_ctime;
1224 buf->yst_mtime = obj->yst_mtime;
1231 static int yaffsfs_DoStatOrLStat(const YCHAR *path, struct yaffs_stat *buf,int doLStat)
1239 obj = yaffsfs_FindObject(NULL,path,0,1);
1242 obj = yaffsfs_FollowLink(obj,0);
1245 retVal = yaffsfs_DoStat(obj,buf);
1247 /* todo error not found */
1248 yaffsfs_SetError(-ENOENT);
1256 int yaffs_stat(const YCHAR *path, struct yaffs_stat *buf)
1258 return yaffsfs_DoStatOrLStat(path,buf,0);
1261 int yaffs_lstat(const YCHAR *path, struct yaffs_stat *buf)
1263 return yaffsfs_DoStatOrLStat(path,buf,1);
1266 int yaffs_fstat(int fd, struct yaffs_stat *buf)
1273 obj = yaffsfs_GetHandleObject(fd);
1276 retVal = yaffsfs_DoStat(obj,buf);
1279 yaffsfs_SetError(-EBADF);
1286 #ifndef CONFIG_YAFFS_WINCE
1287 /* xattrib functions */
1290 static int yaffs_do_setxattr(const YCHAR *path, const char *name, const void *data, int size, int flags, int follow)
1298 obj = yaffsfs_FindObject(NULL,path,0,1);
1301 obj = yaffsfs_FollowLink(obj,0);
1304 retVal = yaffs_set_xattrib(obj,name,data,size,flags);
1306 yaffsfs_SetError(retVal);
1310 /* todo error not found */
1311 yaffsfs_SetError(-ENOENT);
1319 int yaffs_setxattr(const YCHAR *path, const char *name, const void *data, int size, int flags)
1321 return yaffs_do_setxattr(path, name, data, size, flags, 1);
1324 int yaffs_lsetxattr(const YCHAR *path, const char *name, const void *data, int size, int flags)
1326 return yaffs_do_setxattr(path, name, data, size, flags, 0);
1331 int yaffs_fsetxattr(int fd, const char *name, const void *data, int size, int flags)
1338 obj = yaffsfs_GetHandleObject(fd);
1341 retVal = yaffs_set_xattrib(obj,name,data,size,flags);
1343 yaffsfs_SetError(retVal);
1348 yaffsfs_SetError(-EBADF);
1355 static int yaffs_do_getxattr(const YCHAR *path, const char *name, void *data, int size, int follow)
1363 obj = yaffsfs_FindObject(NULL,path,0,1);
1366 obj = yaffsfs_FollowLink(obj,0);
1369 retVal = yaffs_get_xattrib(obj,name,data,size);
1371 yaffsfs_SetError(retVal);
1375 /* todo error not found */
1376 yaffsfs_SetError(-ENOENT);
1384 int yaffs_getxattr(const YCHAR *path, const char *name, void *data, int size)
1386 return yaffs_do_getxattr( path, name, data, size, 1);
1388 int yaffs_lgetxattr(const YCHAR *path, const char *name, void *data, int size)
1390 return yaffs_do_getxattr( path, name, data, size, 0);
1395 int yaffs_fgetxattr(int fd, const char *name, void *data, int size)
1402 obj = yaffsfs_GetHandleObject(fd);
1405 retVal = yaffs_get_xattrib(obj,name,data,size);
1407 yaffsfs_SetError(retVal);
1412 yaffsfs_SetError(-EBADF);
1419 static int yaffs_do_listxattr(const YCHAR *path, char *data, int size, int follow)
1427 obj = yaffsfs_FindObject(NULL,path,0,1);
1430 obj = yaffsfs_FollowLink(obj,0);
1433 retVal = yaffs_list_xattrib(obj, data,size);
1435 yaffsfs_SetError(retVal);
1439 /* todo error not found */
1440 yaffsfs_SetError(-ENOENT);
1448 int yaffs_listxattr(const YCHAR *path, char *data, int size)
1450 return yaffs_do_listxattr(path, data, size, 1);
1453 int yaffs_llistxattr(const YCHAR *path, char *data, int size)
1455 return yaffs_do_listxattr(path, data, size, 0);
1458 int yaffs_flistxattr(int fd, char *data, int size)
1465 obj = yaffsfs_GetHandleObject(fd);
1468 retVal = yaffs_list_xattrib(obj,data,size);
1470 yaffsfs_SetError(retVal);
1475 yaffsfs_SetError(-EBADF);
1482 static int yaffs_do_removexattr(const YCHAR *path, const char *name, int follow)
1490 obj = yaffsfs_FindObject(NULL,path,0,1);
1493 obj = yaffsfs_FollowLink(obj,0);
1496 retVal = yaffs_remove_xattrib(obj,name);
1498 yaffsfs_SetError(retVal);
1502 /* todo error not found */
1503 yaffsfs_SetError(-ENOENT);
1511 int yaffs_removexattr(const YCHAR *path, const char *name)
1513 return yaffs_do_removexattr(path, name, 1);
1516 int yaffs_lremovexattr(const YCHAR *path, const char *name)
1518 return yaffs_do_removexattr(path, name, 0);
1521 int yaffs_fremovexattr(int fd, const char *name)
1528 obj = yaffsfs_GetHandleObject(fd);
1531 retVal = yaffs_remove_xattrib(obj,name);
1533 yaffsfs_SetError(retVal);
1538 yaffsfs_SetError(-EBADF);
1546 #ifdef CONFIG_YAFFS_WINCE
1547 int yaffs_get_wince_times(int fd, unsigned *wctime, unsigned *watime, unsigned *wmtime)
1554 obj = yaffsfs_GetHandleObject(fd);
1559 wctime[0] = obj->win_ctime[0];
1560 wctime[1] = obj->win_ctime[1];
1563 watime[0] = obj->win_atime[0];
1564 watime[1] = obj->win_atime[1];
1567 wmtime[0] = obj->win_mtime[0];
1568 wmtime[1] = obj->win_mtime[1];
1575 yaffsfs_SetError(-EBADF);
1583 int yaffs_set_wince_times(int fd,
1584 const unsigned *wctime,
1585 const unsigned *watime,
1586 const unsigned *wmtime)
1593 obj = yaffsfs_GetHandleObject(fd);
1598 obj->win_ctime[0] = wctime[0];
1599 obj->win_ctime[1] = wctime[1];
1602 obj->win_atime[0] = watime[0];
1603 obj->win_atime[1] = watime[1];
1606 obj->win_mtime[0] = wmtime[0];
1607 obj->win_mtime[1] = wmtime[1];
1611 result = yaffs_flush_file(obj,0,0);
1615 yaffsfs_SetError(-EBADF);
1625 static int yaffsfs_DoChMod(yaffs_obj_t *obj,mode_t mode)
1630 obj = yaffs_get_equivalent_obj(obj);
1633 obj->yst_mode = mode;
1635 result = yaffs_flush_file(obj,0,0);
1638 return result == YAFFS_OK ? 0 : -1;
1642 int yaffs_access(const YCHAR *path, int amode)
1650 obj = yaffsfs_FindObject(NULL,path,0,1);
1655 if((amode & R_OK) && !(obj->yst_mode & S_IREAD))
1657 if((amode & W_OK) && !(obj->yst_mode & S_IWRITE))
1659 if((amode & X_OK) && !(obj->yst_mode & S_IEXEC))
1663 yaffsfs_SetError(-EACCES);
1667 /* todo error not found */
1668 yaffsfs_SetError(-ENOENT);
1679 int yaffs_chmod(const YCHAR *path, mode_t mode)
1687 obj = yaffsfs_FindObject(NULL,path,0,1);
1690 yaffsfs_SetError(-ENOENT);
1691 else if(obj->my_dev->read_only)
1692 yaffsfs_SetError(-EINVAL);
1694 retVal = yaffsfs_DoChMod(obj,mode);
1703 int yaffs_fchmod(int fd, mode_t mode)
1710 obj = yaffsfs_GetHandleObject(fd);
1713 yaffsfs_SetError(-ENOENT);
1714 else if(obj->my_dev->read_only)
1715 yaffsfs_SetError(-EINVAL);
1717 retVal = yaffsfs_DoChMod(obj,mode);
1725 int yaffs_mkdir(const YCHAR *path, mode_t mode)
1727 yaffs_obj_t *parent = NULL;
1728 yaffs_obj_t *dir = NULL;
1733 parent = yaffsfs_FindDirectory(NULL,path,&name,0);
1734 if(parent && yaffs_strnlen(name,5) == 0){
1735 /* Trying to make the root itself */
1736 yaffsfs_SetError(-EEXIST);
1737 } else if(parent && parent->my_dev->read_only){
1738 yaffsfs_SetError(-EINVAL);
1741 dir = yaffs_create_dir(parent,name,mode,0,0);
1746 yaffsfs_SetError(-ENOENT); /* missing path */
1747 else if (yaffs_find_by_name(parent,name))
1748 yaffsfs_SetError(-EEXIST); /* the name already exists */
1750 yaffsfs_SetError(-ENOSPC); /* just assume no space */
1760 void * yaffs_getdev(const YCHAR *path)
1762 yaffs_dev_t *dev=NULL;
1764 dev = yaffsfs_FindDevice(path,&dummy);
1768 int yaffs_mount2(const YCHAR *path,int read_only)
1771 int result=YAFFS_FAIL;
1772 yaffs_dev_t *dev=NULL;
1775 T(YAFFS_TRACE_ALWAYS,(TSTR("yaffs: Mounting %s" TENDSTR),path));
1779 yaffsfs_InitHandles();
1781 dev = yaffsfs_FindDevice(path,&dummy);
1783 if(!dev->is_mounted){
1784 dev->read_only = read_only ? 1 : 0;
1785 result = yaffs_guts_initialise(dev);
1786 if(result == YAFFS_FAIL)
1787 /* todo error - mount failed */
1788 yaffsfs_SetError(-ENOMEM);
1789 retVal = result ? 0 : -1;
1793 /* todo error - already mounted. */
1794 yaffsfs_SetError(-EBUSY);
1796 /* todo error - no device */
1797 yaffsfs_SetError(-ENODEV);
1804 int yaffs_mount(const YCHAR *path)
1806 return yaffs_mount2(path,0);
1809 int yaffs_sync(const YCHAR *path)
1812 yaffs_dev_t *dev=NULL;
1816 dev = yaffsfs_FindDevice(path,&dummy);
1818 if(dev->is_mounted){
1820 yaffs_flush_whole_cache(dev);
1821 yaffs_checkpoint_save(dev);
1825 /* todo error - not mounted. */
1826 yaffsfs_SetError(-EINVAL);
1829 /* todo error - no device */
1830 yaffsfs_SetError(-ENODEV);
1837 int yaffs_remount(const YCHAR *path, int force, int read_only)
1840 yaffs_dev_t *dev=NULL;
1844 dev = yaffsfs_FindDevice(path,&dummy);
1846 if(dev->is_mounted){
1850 yaffs_flush_whole_cache(dev);
1852 for(i = inUse = 0; i < YAFFSFS_N_HANDLES && !inUse && !force; i++){
1853 if(yaffsfs_handle[i].useCount>0 && yaffsfs_inode[yaffsfs_handle[i].inodeId].iObj->my_dev == dev)
1854 inUse = 1; /* the device is in use, can't unmount */
1857 if(!inUse || force){
1859 yaffs_checkpoint_save(dev);
1860 dev->read_only = read_only ? 1 : 0;
1863 yaffsfs_SetError(-EBUSY);
1866 yaffsfs_SetError(-EINVAL);
1870 yaffsfs_SetError(-ENODEV);
1877 int yaffs_unmount2(const YCHAR *path, int force)
1880 yaffs_dev_t *dev=NULL;
1884 dev = yaffsfs_FindDevice(path,&dummy);
1886 if(dev->is_mounted){
1890 yaffs_flush_whole_cache(dev);
1891 yaffs_checkpoint_save(dev);
1893 for(i = inUse = 0; i < YAFFSFS_N_HANDLES && !inUse; i++){
1894 if(yaffsfs_handle[i].useCount > 0 && yaffsfs_inode[yaffsfs_handle[i].inodeId].iObj->my_dev == dev)
1895 inUse = 1; /* the device is in use, can't unmount */
1898 if(!inUse || force){
1899 yaffs_deinitialise(dev);
1903 /* todo error can't unmount as files are open */
1904 yaffsfs_SetError(-EBUSY);
1907 /* todo error - not mounted. */
1908 yaffsfs_SetError(-EINVAL);
1912 /* todo error - no device */
1913 yaffsfs_SetError(-ENODEV);
1920 int yaffs_unmount(const YCHAR *path)
1922 return yaffs_unmount2(path,0);
1925 loff_t yaffs_freespace(const YCHAR *path)
1928 yaffs_dev_t *dev=NULL;
1932 dev = yaffsfs_FindDevice(path,&dummy);
1933 if(dev && dev->is_mounted){
1934 retVal = yaffs_get_n_free_chunks(dev);
1935 retVal *= dev->data_bytes_per_chunk;
1938 yaffsfs_SetError(-EINVAL);
1944 loff_t yaffs_totalspace(const YCHAR *path)
1947 yaffs_dev_t *dev=NULL;
1951 dev = yaffsfs_FindDevice(path,&dummy);
1952 if(dev && dev->is_mounted){
1953 retVal = (dev->param.end_block - dev->param.start_block + 1) - dev->param.n_reserved_blocks;
1954 retVal *= dev->param.chunks_per_block;
1955 retVal *= dev->data_bytes_per_chunk;
1958 yaffsfs_SetError(-EINVAL);
1964 int yaffs_inodecount(const YCHAR *path)
1967 yaffs_dev_t *dev=NULL;
1971 dev = yaffsfs_FindDevice(path,&dummy);
1972 if(dev && dev->is_mounted) {
1973 int n_obj = dev->n_obj;
1974 if(n_obj > dev->n_hardlinks)
1975 retVal = n_obj - dev->n_hardlinks;
1979 yaffsfs_SetError(-EINVAL);
1986 void yaffs_add_device(yaffs_dev_t *dev)
1988 dev->is_mounted = 0;
1989 dev->param.remove_obj_fn = yaffsfs_RemoveObjectCallback;
1991 if(!dev->dev_list.next)
1992 YINIT_LIST_HEAD(&dev->dev_list);
1994 ylist_add(&dev->dev_list,&yaffsfs_deviceList);
1997 void yaffs_remove_device(yaffs_dev_t *dev)
1999 ylist_del_init(&dev->dev_list);
2005 /* Directory search stuff. */
2008 * Directory search context
2010 * NB this is an opaque structure.
2017 yaffs_dirent de; /* directory entry being used by this dsc */
2018 YCHAR name[NAME_MAX+1]; /* name of directory being searched */
2019 yaffs_obj_t *dirObj; /* ptr to directory being searched */
2020 yaffs_obj_t *nextReturn; /* obj to be returned by next readddir */
2022 struct ylist_head others;
2023 } yaffsfs_DirectorySearchContext;
2027 static struct ylist_head search_contexts;
2030 static void yaffsfs_SetDirRewound(yaffsfs_DirectorySearchContext *dsc)
2034 dsc->dirObj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY){
2038 if( ylist_empty(&dsc->dirObj->variant.dir_variant.children))
2039 dsc->nextReturn = NULL;
2041 dsc->nextReturn = ylist_entry(dsc->dirObj->variant.dir_variant.children.next,
2042 yaffs_obj_t,siblings);
2044 /* Hey someone isn't playing nice! */
2048 static void yaffsfs_DirAdvance(yaffsfs_DirectorySearchContext *dsc)
2052 dsc->dirObj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY){
2054 if( dsc->nextReturn == NULL ||
2055 ylist_empty(&dsc->dirObj->variant.dir_variant.children))
2056 dsc->nextReturn = NULL;
2058 struct ylist_head *next = dsc->nextReturn->siblings.next;
2060 if( next == &dsc->dirObj->variant.dir_variant.children)
2061 dsc->nextReturn = NULL; /* end of list */
2063 dsc->nextReturn = ylist_entry(next,yaffs_obj_t,siblings);
2066 /* Hey someone isn't playing nice! */
2070 static void yaffsfs_RemoveObjectCallback(yaffs_obj_t *obj)
2073 struct ylist_head *i;
2074 yaffsfs_DirectorySearchContext *dsc;
2076 /* if search contexts not initilised then skip */
2077 if(!search_contexts.next)
2080 /* Iterate through the directory search contexts.
2081 * If any are the one being removed, then advance the dsc to
2082 * the next one to prevent a hanging ptr.
2084 ylist_for_each(i, &search_contexts) {
2086 dsc = ylist_entry(i, yaffsfs_DirectorySearchContext,others);
2087 if(dsc->nextReturn == obj)
2088 yaffsfs_DirAdvance(dsc);
2094 yaffs_DIR *yaffs_opendir(const YCHAR *dirname)
2096 yaffs_DIR *dir = NULL;
2097 yaffs_obj_t *obj = NULL;
2098 yaffsfs_DirectorySearchContext *dsc = NULL;
2102 obj = yaffsfs_FindObject(NULL,dirname,0,1);
2104 if(obj && obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY){
2106 dsc = YMALLOC(sizeof(yaffsfs_DirectorySearchContext));
2107 dir = (yaffs_DIR *)dsc;
2110 memset(dsc,0,sizeof(yaffsfs_DirectorySearchContext));
2111 dsc->magic = YAFFS_MAGIC;
2113 yaffs_strncpy(dsc->name,dirname,NAME_MAX);
2114 YINIT_LIST_HEAD(&dsc->others);
2116 if(!search_contexts.next)
2117 YINIT_LIST_HEAD(&search_contexts);
2119 ylist_add(&dsc->others,&search_contexts);
2120 yaffsfs_SetDirRewound(dsc);
2130 struct yaffs_dirent *yaffs_readdir(yaffs_DIR *dirp)
2132 yaffsfs_DirectorySearchContext *dsc = (yaffsfs_DirectorySearchContext *)dirp;
2133 struct yaffs_dirent *retVal = NULL;
2137 if(dsc && dsc->magic == YAFFS_MAGIC){
2138 yaffsfs_SetError(0);
2139 if(dsc->nextReturn){
2140 dsc->de.d_ino = yaffs_get_equivalent_obj(dsc->nextReturn)->obj_id;
2141 dsc->de.d_dont_use = (unsigned)dsc->nextReturn;
2142 dsc->de.d_off = dsc->offset++;
2143 yaffs_get_obj_name(dsc->nextReturn,dsc->de.d_name,NAME_MAX);
2144 if(yaffs_strnlen(dsc->de.d_name,NAME_MAX+1) == 0)
2146 /* this should not happen! */
2147 yaffs_strcpy(dsc->de.d_name,_Y("zz"));
2149 dsc->de.d_reclen = sizeof(struct yaffs_dirent);
2151 yaffsfs_DirAdvance(dsc);
2155 yaffsfs_SetError(-EBADF);
2164 void yaffs_rewinddir(yaffs_DIR *dirp)
2166 yaffsfs_DirectorySearchContext *dsc = (yaffsfs_DirectorySearchContext *)dirp;
2170 yaffsfs_SetDirRewound(dsc);
2176 int yaffs_closedir(yaffs_DIR *dirp)
2178 yaffsfs_DirectorySearchContext *dsc = (yaffsfs_DirectorySearchContext *)dirp;
2182 ylist_del(&dsc->others); /* unhook from list */
2188 /* End of directory stuff */
2191 int yaffs_symlink(const YCHAR *oldpath, const YCHAR *newpath)
2193 yaffs_obj_t *parent = NULL;
2197 int mode = 0; /* ignore for now */
2200 parent = yaffsfs_FindDirectory(NULL,newpath,&name,0);
2201 if(parent && parent->my_dev->read_only)
2202 yaffsfs_SetError(-EINVAL);
2204 obj = yaffs_create_symlink(parent,name,mode,0,0,oldpath);
2208 yaffsfs_SetError(-ENOSPC); /* just assume no space for now */
2212 yaffsfs_SetError(-EINVAL);
2222 int yaffs_readlink(const YCHAR *path, YCHAR *buf, int bufsiz)
2224 yaffs_obj_t *obj = NULL;
2230 obj = yaffsfs_FindObject(NULL,path,0,1);
2233 yaffsfs_SetError(-ENOENT);
2235 } else if(obj->variant_type != YAFFS_OBJECT_TYPE_SYMLINK) {
2236 yaffsfs_SetError(-EINVAL);
2239 YCHAR *alias = obj->variant.symlink_variant.alias;
2240 memset(buf,0,bufsiz);
2241 yaffs_strncpy(buf,alias,bufsiz - 1);
2248 int yaffs_link(const YCHAR *oldpath, const YCHAR *newpath)
2250 /* Creates a link called newpath to existing oldpath */
2251 yaffs_obj_t *obj = NULL;
2252 yaffs_obj_t *target = NULL;
2254 int new_nameLength = 0;
2259 obj = yaffsfs_FindObject(NULL,oldpath,0,1);
2260 target = yaffsfs_FindObject(NULL,newpath,0,0);
2263 yaffsfs_SetError(-ENOENT);
2265 } else if(obj->my_dev->read_only){
2266 yaffsfs_SetError(-EINVAL);
2269 yaffsfs_SetError(-EEXIST);
2272 yaffs_obj_t *newdir = NULL;
2273 yaffs_obj_t *link = NULL;
2277 newdir = yaffsfs_FindDirectory(NULL,newpath,&newname,0);
2280 yaffsfs_SetError(-ENOTDIR);
2282 }else if(newdir->my_dev != obj->my_dev){
2283 yaffsfs_SetError(-EXDEV);
2287 new_nameLength = yaffs_strnlen(newname,YAFFS_MAX_NAME_LENGTH+1);
2289 if(new_nameLength == 0){
2290 yaffsfs_SetError(-ENOENT);
2292 } else if (new_nameLength > YAFFS_MAX_NAME_LENGTH){
2293 yaffsfs_SetError(-ENAMETOOLONG);
2298 link = yaffs_link_obj(newdir,newname,obj);
2302 yaffsfs_SetError(-ENOSPC);
2313 int yaffs_mknod(const YCHAR *pathname, mode_t mode, dev_t dev)
2322 * Returns number of handles attached to the object
2324 int yaffs_n_handles(const YCHAR *path)
2328 obj = yaffsfs_FindObject(NULL,path,0,1);
2330 return yaffsfs_CountHandles(obj);
2333 int yaffs_dump_dev(const YCHAR *path)
2338 yaffs_obj_t *obj = yaffsfs_FindRoot(path,&rest);
2341 yaffs_dev_t *dev = obj->my_dev;
2344 "n_page_writes.......... %d\n"
2345 "n_page_reads........... %d\n"
2346 "n_erasures....... %d\n"
2347 "n_gc_copies............ %d\n"
2348 "garbageCollections... %d\n"
2349 "passiveGarbageColl'ns %d\n"
2355 dev->garbageCollections,
2356 dev->passiveGarbageCollections