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_Object *yaffsfs_FindObject(yaffs_Object *relativeDirectory, const YCHAR *path, int symDepth);
35 static void yaffsfs_RemoveObjectCallback(yaffs_Object *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_Object *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_Object *obj)
126 obj = yaffs_GetEquivalentObject(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_Object *obj)
144 yaffsfs_Inode *in = NULL;
147 obj = yaffs_GetEquivalentObject(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_Object *obj)
171 int i = yaffsfs_FindInodeIdForObject(obj);
174 return yaffsfs_inode[i].count;
179 static void yaffsfs_ReleaseInode(yaffsfs_Inode *in)
186 yaffs_DeleteObject(obj);
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_Device *yaffsfs_FindDevice(const YCHAR *path, YCHAR **restOfPath)
304 struct ylist_head *cfg;
305 const YCHAR *leftOver;
307 yaffs_Device *retval = NULL;
308 yaffs_Device *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_Device, devList);
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_Device *yaffsfs_FindDevice(const YCHAR *path, YCHAR **restOfPath)
376 yaffsfs_DeviceConfiguration *cfg = yaffsfs_configurationList;
377 const YCHAR *leftOver;
379 yaffs_Device *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_Object *yaffsfs_FindRoot(const YCHAR *path, YCHAR **restOfPath)
421 dev= yaffsfs_FindDevice(path,restOfPath);
422 if(dev && dev->isMounted){
428 static yaffs_Object *yaffsfs_FollowLink(yaffs_Object *obj,int symDepth)
431 while(obj && obj->variantType == YAFFS_OBJECT_TYPE_SYMLINK){
432 YCHAR *alias = obj->variant.symLinkVariant.alias;
434 if(yaffsfs_IsPathDivider(*alias))
435 /* Starts with a /, need to scan from root up */
436 obj = yaffsfs_FindObject(NULL,alias,symDepth++);
438 /* Relative to here, so use the parent of the symlink as a start */
439 obj = yaffsfs_FindObject(obj->parent,alias,symDepth++);
446 * yaffsfs_FindDirectory
447 * Parse a path to determine the directory and the name within the directory.
449 * eg. "/data/xx/ff" --> puts name="ff" and returns the directory "/data/xx"
451 static yaffs_Object *yaffsfs_DoFindDirectory(yaffs_Object *startDir,
452 const YCHAR *path, YCHAR **name, int symDepth)
456 YCHAR str[YAFFS_MAX_NAME_LENGTH+1];
459 if(symDepth > YAFFSFS_MAX_SYMLINK_DEREFERENCES)
464 restOfPath = (YCHAR *)path;
467 dir = yaffsfs_FindRoot(path,&restOfPath);
472 * curve ball: also throw away surplus '/'
473 * eg. "/ram/x////ff" gets treated the same as "/ram/x/ff"
475 while(yaffsfs_IsPathDivider(*restOfPath))
476 restOfPath++; /* get rid of '/' */
481 while(*restOfPath && !yaffsfs_IsPathDivider(*restOfPath)){
482 if (i < YAFFS_MAX_NAME_LENGTH){
483 str[i] = *restOfPath;
491 /* got to the end of the string */
494 if(yaffs_strcmp(str,_Y(".")) == 0)
498 else if(yaffs_strcmp(str,_Y("..")) == 0)
501 dir = yaffs_FindObjectByName(dir,str);
503 while(dir && dir->variantType == YAFFS_OBJECT_TYPE_SYMLINK)
504 dir = yaffsfs_FollowLink(dir,symDepth);
507 if(dir && dir->variantType != YAFFS_OBJECT_TYPE_DIRECTORY)
512 /* directory did not exist. */
516 static yaffs_Object *yaffsfs_FindDirectory(yaffs_Object *relativeDirectory,
517 const YCHAR *path,YCHAR **name,int symDepth)
519 return yaffsfs_DoFindDirectory(relativeDirectory,path,name,symDepth);
523 * yaffsfs_FindObject turns a path for an existing object into the object
525 static yaffs_Object *yaffsfs_FindObject(yaffs_Object *relativeDirectory, const YCHAR *path,int symDepth)
530 dir = yaffsfs_FindDirectory(relativeDirectory,path,&name,symDepth);
533 return yaffs_FindObjectByName(dir,name);
539 int yaffs_dup(int fd)
542 yaffsfs_Handle *oldPtr = NULL;
543 yaffsfs_Handle *newPtr = NULL;
547 oldPtr = yaffsfs_GetHandlePointer(fd);
548 if(oldPtr && oldPtr->useCount > 0)
549 newHandle = yaffsfs_GetNewHandle();
551 newPtr = yaffsfs_GetHandlePointer(newHandle);
559 yaffsfs_SetError(-EBADF);
561 yaffsfs_SetError(-ENOMEM);
567 int yaffs_open_sharing(const YCHAR *path, int oflag, int mode, int sharing)
569 yaffs_Object *obj = NULL;
570 yaffs_Object *dir = NULL;
573 yaffsfs_Handle *h = NULL;
576 int errorReported = 0;
577 __u8 shareRead = (sharing & YAFFS_SHARE_READ) ? 1 : 0;
578 __u8 shareWrite = (sharing & YAFFS_SHARE_WRITE) ? 1 : 0;
579 __u8 sharedReadAllowed;
580 __u8 sharedWriteAllowed;
586 /* O_EXCL only has meaning if O_CREAT is specified */
587 if(!(oflag & O_CREAT))
590 /* O_TRUNC has no meaning if (O_CREAT | O_EXCL) is specified */
591 if( (oflag & O_CREAT) & (oflag & O_EXCL))
594 /* Todo: Are there any more flag combos to sanitise ? */
599 handle = yaffsfs_GetNewHandle();
603 h = yaffsfs_GetHandlePointer(handle);
605 /* try to find the exisiting object */
606 obj = yaffsfs_FindObject(NULL,path,0);
608 if(obj && obj->variantType == YAFFS_OBJECT_TYPE_SYMLINK)
609 obj = yaffsfs_FollowLink(obj,symDepth++);
612 obj = yaffs_GetEquivalentObject(obj);
615 obj->variantType != YAFFS_OBJECT_TYPE_FILE &&
616 obj->variantType != YAFFS_OBJECT_TYPE_DIRECTORY)
621 /* The file already exists or it might be a directory */
623 /* If it is a directory then we can't open it as a file */
624 if(obj->variantType == YAFFS_OBJECT_TYPE_DIRECTORY){
626 yaffsfs_SetError(-EISDIR);
630 /* Open should fail if O_CREAT and O_EXCL are specified since
633 if((oflag & O_EXCL) && (oflag & O_CREAT)){
635 yaffsfs_SetError(-EEXIST);
639 /* Check file permissions */
640 if( (oflag & (O_RDWR | O_WRONLY)) == 0 && /* ie O_RDONLY */
641 !(obj->yst_mode & S_IREAD))
644 if( (oflag & O_RDWR) &&
645 !(obj->yst_mode & S_IREAD))
648 if( (oflag & (O_RDWR | O_WRONLY)) &&
649 !(obj->yst_mode & S_IWRITE))
652 /* Check sharing of an existing object. */
656 sharedReadAllowed = 1;
657 sharedWriteAllowed = 1;
660 for( i = 0; i < YAFFSFS_N_HANDLES; i++){
661 h = &yaffsfs_handle[i];
662 if(h->useCount > 0 &&
664 yaffsfs_inode[h->inodeId].iObj == obj){
666 sharedReadAllowed = 0;
668 sharedWriteAllowed = 0;
676 readRequested = (oflag & (O_RDWR | O_RDONLY)) ? 1 : 0;
677 writeRequested = (oflag & (O_RDWR | O_WRONLY)) ? 1 : 0;
679 if((!sharedReadAllowed && readRequested)||
680 (!shareRead && alreadyReading) ||
681 (!sharedWriteAllowed && writeRequested) ||
682 (!shareWrite && alreadyWriting)){
684 yaffsfs_SetError(-EBUSY);
689 } else if((oflag & O_CREAT)) {
690 /* Let's see if we can create this file */
691 dir = yaffsfs_FindDirectory(NULL,path,&name,0);
692 if(dir && dir->myDev->readOnly){
693 yaffsfs_SetError(-EINVAL);
696 obj = yaffs_MknodFile(dir,name,mode,0,0);
698 yaffsfs_SetError(-ENOTDIR);
703 if(obj && !openDenied) {
704 int inodeId = yaffsfs_GetInodeIdForObject(obj);
708 * Todo: Fix any problem if inodes run out, though that
709 * can't happen if the number of inode items >= number of handles.
713 h->inodeId = inodeId;
714 h->reading = (oflag & (O_RDONLY | O_RDWR)) ? 1 : 0;
715 h->writing = (oflag & (O_WRONLY | O_RDWR)) ? 1 : 0;
716 h->append = (oflag & O_APPEND) ? 1 : 0;
718 h->shareRead = shareRead;
719 h->shareWrite = shareWrite;
721 /* Hook inode to object */
722 obj->myInode = (void*) &yaffsfs_inode[inodeId];
724 if((oflag & O_TRUNC) && h->writing)
725 yaffs_ResizeFile(obj,0);
727 yaffsfs_PutHandle(handle);
729 yaffsfs_SetError(-EACCES);
741 int yaffs_open(const YCHAR *path, int oflag, int mode)
743 return yaffs_open_sharing(path, oflag, mode, YAFFS_SHARE_READ | YAFFS_SHARE_WRITE);
746 int yaffs_Dofsync(int fd,int datasync)
748 yaffsfs_Handle *h = NULL;
753 h = yaffsfs_GetHandlePointer(fd);
755 if(h && h->useCount > 0)
757 yaffs_FlushFile(yaffsfs_inode[h->inodeId].iObj,1,datasync);
760 yaffsfs_SetError(-EBADF);
769 int yaffs_fsync(int fd)
771 return yaffs_Dofsync(fd,0);
774 int yaffs_flush(int fd)
776 return yaffs_fsync(fd);
779 int yaffs_fdatasync(int fd)
781 return yaffs_Dofsync(fd,1);
784 int yaffs_close(int fd)
786 yaffsfs_Handle *h = NULL;
791 h = yaffsfs_GetHandlePointer(fd);
793 if(h && h->useCount > 0) {
795 yaffs_FlushFile(yaffsfs_inode[h->inodeId].iObj,1,0);
796 yaffsfs_PutHandle(fd);
800 yaffsfs_SetError(-EBADF);
811 int yaffsfs_do_read(int fd, void *vbuf, unsigned int nbyte, int isPread, int offset)
813 yaffsfs_Handle *h = NULL;
814 yaffs_Object *obj = NULL;
820 unsigned int maxRead;
821 __u8 *buf = (__u8 *)vbuf;
824 h = yaffsfs_GetHandlePointer(fd);
825 obj = yaffsfs_GetHandleObject(fd);
829 yaffsfs_SetError(-EBADF);
831 } else if( h && obj){
835 startPos = h->position;
839 if(yaffs_GetObjectFileLength(obj) > pos)
840 maxRead = yaffs_GetObjectFileLength(obj) - pos;
848 yaffsfs_GetHandle(fd);
851 nToRead = YAFFSFS_RW_SIZE - (pos & (YAFFSFS_RW_SIZE -1));
854 nRead = yaffs_ReadDataFromFile(obj,buf,pos,nToRead);
865 nbyte = 0; /* no more to read */
875 yaffsfs_PutHandle(fd);
879 h->position = startPos + totalRead;
889 return (totalRead >= 0) ? totalRead : -1;
893 int yaffs_read(int fd, void *buf, unsigned int nbyte)
895 return yaffsfs_do_read(fd, buf, nbyte, 0, 0);
898 int yaffs_pread(int fd, void *buf, unsigned int nbyte, unsigned int offset)
900 return yaffsfs_do_read(fd, buf, nbyte, 1, offset);
903 int yaffsfs_do_write(int fd, const void *vbuf, unsigned int nbyte, int isPwrite, int offset)
905 yaffsfs_Handle *h = NULL;
906 yaffs_Object *obj = NULL;
910 int totalWritten = 0;
911 int writeThrough = 0;
913 const __u8 *buf = (const __u8 *)vbuf;
916 h = yaffsfs_GetHandlePointer(fd);
917 obj = yaffsfs_GetHandleObject(fd);
921 yaffsfs_SetError(-EBADF);
923 } else if( h && obj && (!h->writing || obj->myDev->readOnly)){
924 yaffsfs_SetError(-EINVAL);
926 } else if( h && obj){
928 startPos = yaffs_GetObjectFileLength(obj);
932 startPos = h->position;
934 yaffsfs_GetHandle(fd);
937 nToWrite = YAFFSFS_RW_SIZE - (pos & (YAFFSFS_RW_SIZE -1));
941 nWritten = yaffs_WriteDataToFile(obj,buf,pos,nToWrite,writeThrough);
943 totalWritten += nWritten;
948 if(nWritten == nToWrite)
953 if(nWritten < 1 && totalWritten < 1){
954 yaffsfs_SetError(-ENOSPC);
964 yaffsfs_PutHandle(fd);
968 h->position = startPos + totalWritten;
977 return (totalWritten >= 0) ? totalWritten : -1;
980 int yaffs_write(int fd, const void *buf, unsigned int nbyte)
982 return yaffsfs_do_write(fd, buf, nbyte, 0, 0);
985 int yaffs_pwrite(int fd, const void *buf, unsigned int nbyte, unsigned int offset)
987 return yaffsfs_do_write(fd, buf, nbyte, 1, offset);
991 int yaffs_truncate(const YCHAR *path,off_t newSize)
993 yaffs_Object *obj = NULL;
994 int result = YAFFS_FAIL;
998 obj = yaffsfs_FindObject(NULL,path,0);
1000 obj = yaffs_GetEquivalentObject(obj);
1003 yaffsfs_SetError(-ENOENT);
1004 else if(obj->variantType != YAFFS_OBJECT_TYPE_FILE)
1005 yaffsfs_SetError(-EISDIR);
1006 else if(obj->myDev->readOnly)
1007 yaffsfs_SetError(-EINVAL);
1009 result = yaffs_ResizeFile(obj,newSize);
1014 return (result) ? 0 : -1;
1017 int yaffs_ftruncate(int fd, off_t newSize)
1019 yaffsfs_Handle *h = NULL;
1020 yaffs_Object *obj = NULL;
1024 h = yaffsfs_GetHandlePointer(fd);
1025 obj = yaffsfs_GetHandleObject(fd);
1029 yaffsfs_SetError(-EBADF);
1030 else if(obj->myDev->readOnly)
1031 yaffsfs_SetError(-EINVAL);
1033 /* resize the file */
1034 result = yaffs_ResizeFile(obj,newSize);
1038 return (result) ? 0 : -1;
1042 off_t yaffs_lseek(int fd, off_t offset, int whence)
1044 yaffsfs_Handle *h = NULL;
1045 yaffs_Object *obj = NULL;
1050 h = yaffsfs_GetHandlePointer(fd);
1051 obj = yaffsfs_GetHandleObject(fd);
1055 yaffsfs_SetError(-EBADF);
1056 else if(whence == SEEK_SET){
1060 else if(whence == SEEK_CUR) {
1061 if( (h->position + offset) >= 0)
1062 pos = (h->position + offset);
1064 else if(whence == SEEK_END) {
1065 fSize = yaffs_GetObjectFileLength(obj);
1066 if(fSize >= 0 && (fSize + offset) >= 0)
1067 pos = fSize + offset;
1083 int yaffsfs_DoUnlink(const YCHAR *path,int isDirectory)
1085 yaffs_Object *dir = NULL;
1086 yaffs_Object *obj = NULL;
1088 int result = YAFFS_FAIL;
1092 obj = yaffsfs_FindObject(NULL,path,0);
1093 dir = yaffsfs_FindDirectory(NULL,path,&name,0);
1095 yaffsfs_SetError(-ENOTDIR);
1097 yaffsfs_SetError(-ENOENT);
1098 else if(obj->myDev->readOnly)
1099 yaffsfs_SetError(-EINVAL);
1100 else if(!isDirectory && obj->variantType == YAFFS_OBJECT_TYPE_DIRECTORY)
1101 yaffsfs_SetError(-EISDIR);
1102 else if(isDirectory && obj->variantType != YAFFS_OBJECT_TYPE_DIRECTORY)
1103 yaffsfs_SetError(-ENOTDIR);
1105 result = yaffs_Unlink(dir,name);
1107 if(result == YAFFS_FAIL && isDirectory)
1108 yaffsfs_SetError(-ENOTEMPTY);
1115 return (result == YAFFS_FAIL) ? -1 : 0;
1119 int yaffs_rmdir(const YCHAR *path)
1121 return yaffsfs_DoUnlink(path,1);
1124 int yaffs_unlink(const YCHAR *path)
1126 return yaffsfs_DoUnlink(path,0);
1129 int yaffs_rename(const YCHAR *oldPath, const YCHAR *newPath)
1131 yaffs_Object *olddir = NULL;
1132 yaffs_Object *newdir = NULL;
1133 yaffs_Object *obj = NULL;
1136 int result= YAFFS_FAIL;
1137 int renameAllowed = 1;
1141 olddir = yaffsfs_FindDirectory(NULL,oldPath,&oldname,0);
1142 newdir = yaffsfs_FindDirectory(NULL,newPath,&newname,0);
1143 obj = yaffsfs_FindObject(NULL,oldPath,0);
1145 if(!olddir || !newdir || !obj) {
1147 yaffsfs_SetError(-EBADF);
1149 } else if(obj->myDev->readOnly){
1150 yaffsfs_SetError(-EINVAL);
1152 } else if(olddir->myDev != newdir->myDev) {
1153 /* oops must be on same device */
1155 yaffsfs_SetError(-EXDEV);
1157 } else if(obj && obj->variantType == YAFFS_OBJECT_TYPE_DIRECTORY) {
1159 * It is a directory, check that it is not being renamed to
1160 * being its own decendent.
1161 * Do this by tracing from the new directory back to the root, checking for obj
1164 yaffs_Object *xx = newdir;
1166 while( renameAllowed && xx){
1172 yaffsfs_SetError(-EACCES);
1176 result = yaffs_RenameObject(olddir,oldname,newdir,newname);
1180 return (result == YAFFS_FAIL) ? -1 : 0;
1184 static int yaffsfs_DoStat(yaffs_Object *obj,struct yaffs_stat *buf)
1189 obj = yaffs_GetEquivalentObject(obj);
1192 buf->st_dev = (int)obj->myDev->osContext;
1193 buf->st_ino = obj->objectId;
1194 buf->st_mode = obj->yst_mode & ~S_IFMT; /* clear out file type bits */
1196 if(obj->variantType == YAFFS_OBJECT_TYPE_DIRECTORY)
1197 buf->st_mode |= S_IFDIR;
1198 else if(obj->variantType == YAFFS_OBJECT_TYPE_SYMLINK)
1199 buf->st_mode |= S_IFLNK;
1200 else if(obj->variantType == YAFFS_OBJECT_TYPE_FILE)
1201 buf->st_mode |= S_IFREG;
1203 buf->st_nlink = yaffs_GetObjectLinkCount(obj);
1206 buf->st_rdev = obj->yst_rdev;
1207 buf->st_size = yaffs_GetObjectFileLength(obj);
1208 buf->st_blksize = obj->myDev->nDataBytesPerChunk;
1209 buf->st_blocks = (buf->st_size + buf->st_blksize -1)/buf->st_blksize;
1210 #if CONFIG_YAFFS_WINCE
1211 buf->yst_wince_atime[0] = obj->win_atime[0];
1212 buf->yst_wince_atime[1] = obj->win_atime[1];
1213 buf->yst_wince_ctime[0] = obj->win_ctime[0];
1214 buf->yst_wince_ctime[1] = obj->win_ctime[1];
1215 buf->yst_wince_mtime[0] = obj->win_mtime[0];
1216 buf->yst_wince_mtime[1] = obj->win_mtime[1];
1218 buf->yst_atime = obj->yst_atime;
1219 buf->yst_ctime = obj->yst_ctime;
1220 buf->yst_mtime = obj->yst_mtime;
1227 static int yaffsfs_DoStatOrLStat(const YCHAR *path, struct yaffs_stat *buf,int doLStat)
1234 obj = yaffsfs_FindObject(NULL,path,0);
1237 obj = yaffsfs_FollowLink(obj,0);
1240 retVal = yaffsfs_DoStat(obj,buf);
1242 /* todo error not found */
1243 yaffsfs_SetError(-ENOENT);
1251 int yaffs_stat(const YCHAR *path, struct yaffs_stat *buf)
1253 return yaffsfs_DoStatOrLStat(path,buf,0);
1256 int yaffs_lstat(const YCHAR *path, struct yaffs_stat *buf)
1258 return yaffsfs_DoStatOrLStat(path,buf,1);
1261 int yaffs_fstat(int fd, struct yaffs_stat *buf)
1268 obj = yaffsfs_GetHandleObject(fd);
1271 retVal = yaffsfs_DoStat(obj,buf);
1274 yaffsfs_SetError(-EBADF);
1281 #ifndef CONFIG_YAFFS_WINCE
1282 /* xattrib functions */
1285 static int yaffs_do_setxattr(const YCHAR *path, const char *name, const void *data, int size, int flags, int follow)
1292 obj = yaffsfs_FindObject(NULL,path,0);
1295 obj = yaffsfs_FollowLink(obj,0);
1298 retVal = yaffs_SetXAttribute(obj,name,data,size,flags);
1300 yaffsfs_SetError(retVal);
1304 /* todo error not found */
1305 yaffsfs_SetError(-ENOENT);
1313 int yaffs_setxattr(const YCHAR *path, const char *name, const void *data, int size, int flags)
1315 return yaffs_do_setxattr(path, name, data, size, flags, 1);
1318 int yaffs_lsetxattr(const YCHAR *path, const char *name, const void *data, int size, int flags)
1320 return yaffs_do_setxattr(path, name, data, size, flags, 0);
1325 int yaffs_fsetxattr(int fd, const char *name, const void *data, int size, int flags)
1332 obj = yaffsfs_GetHandleObject(fd);
1335 retVal = yaffs_SetXAttribute(obj,name,data,size,flags);
1337 yaffsfs_SetError(retVal);
1342 yaffsfs_SetError(-EBADF);
1349 static int yaffs_do_getxattr(const YCHAR *path, const char *name, void *data, int size, int follow)
1356 obj = yaffsfs_FindObject(NULL,path,0);
1359 obj = yaffsfs_FollowLink(obj,0);
1362 retVal = yaffs_GetXAttribute(obj,name,data,size);
1364 yaffsfs_SetError(retVal);
1368 /* todo error not found */
1369 yaffsfs_SetError(-ENOENT);
1377 int yaffs_getxattr(const YCHAR *path, const char *name, void *data, int size)
1379 return yaffs_do_getxattr( path, name, data, size, 1);
1381 int yaffs_lgetxattr(const YCHAR *path, const char *name, void *data, int size)
1383 return yaffs_do_getxattr( path, name, data, size, 0);
1388 int yaffs_fgetxattr(int fd, const char *name, void *data, int size)
1395 obj = yaffsfs_GetHandleObject(fd);
1398 retVal = yaffs_GetXAttribute(obj,name,data,size);
1400 yaffsfs_SetError(retVal);
1405 yaffsfs_SetError(-EBADF);
1412 static int yaffs_do_listxattr(const YCHAR *path, char *data, int size, int follow)
1419 obj = yaffsfs_FindObject(NULL,path,0);
1422 obj = yaffsfs_FollowLink(obj,0);
1425 retVal = yaffs_ListXAttributes(obj, data,size);
1427 yaffsfs_SetError(retVal);
1431 /* todo error not found */
1432 yaffsfs_SetError(-ENOENT);
1440 int yaffs_listxattr(const YCHAR *path, char *data, int size)
1442 return yaffs_do_listxattr(path, data, size, 1);
1445 int yaffs_llistxattr(const YCHAR *path, char *data, int size)
1447 return yaffs_do_listxattr(path, data, size, 0);
1450 int yaffs_flistxattr(int fd, char *data, int size)
1457 obj = yaffsfs_GetHandleObject(fd);
1460 retVal = yaffs_ListXAttributes(obj,data,size);
1462 yaffsfs_SetError(retVal);
1467 yaffsfs_SetError(-EBADF);
1474 static int yaffs_do_removexattr(const YCHAR *path, const char *name, int follow)
1481 obj = yaffsfs_FindObject(NULL,path,0);
1484 obj = yaffsfs_FollowLink(obj,0);
1487 retVal = yaffs_RemoveXAttribute(obj,name);
1489 yaffsfs_SetError(retVal);
1493 /* todo error not found */
1494 yaffsfs_SetError(-ENOENT);
1502 int yaffs_removexattr(const YCHAR *path, const char *name)
1504 return yaffs_do_removexattr(path, name, 1);
1507 int yaffs_lremovexattr(const YCHAR *path, const char *name)
1509 return yaffs_do_removexattr(path, name, 0);
1512 int yaffs_fremovexattr(int fd, const char *name)
1519 obj = yaffsfs_GetHandleObject(fd);
1522 retVal = yaffs_RemoveXAttribute(obj,name);
1524 yaffsfs_SetError(retVal);
1529 yaffsfs_SetError(-EBADF);
1537 #ifdef CONFIG_YAFFS_WINCE
1538 int yaffs_get_wince_times(int fd, unsigned *wctime, unsigned *watime, unsigned *wmtime)
1545 obj = yaffsfs_GetHandleObject(fd);
1550 wctime[0] = obj->win_ctime[0];
1551 wctime[1] = obj->win_ctime[1];
1554 watime[0] = obj->win_atime[0];
1555 watime[1] = obj->win_atime[1];
1558 wmtime[0] = obj->win_mtime[0];
1559 wmtime[1] = obj->win_mtime[1];
1566 yaffsfs_SetError(-EBADF);
1574 int yaffs_set_wince_times(int fd,
1575 const unsigned *wctime,
1576 const unsigned *watime,
1577 const unsigned *wmtime)
1584 obj = yaffsfs_GetHandleObject(fd);
1589 obj->win_ctime[0] = wctime[0];
1590 obj->win_ctime[1] = wctime[1];
1593 obj->win_atime[0] = watime[0];
1594 obj->win_atime[1] = watime[1];
1597 obj->win_mtime[0] = wmtime[0];
1598 obj->win_mtime[1] = wmtime[1];
1602 result = yaffs_FlushFile(obj,0,0);
1606 yaffsfs_SetError(-EBADF);
1616 static int yaffsfs_DoChMod(yaffs_Object *obj,mode_t mode)
1621 obj = yaffs_GetEquivalentObject(obj);
1624 obj->yst_mode = mode;
1626 result = yaffs_FlushFile(obj,0,0);
1629 return result == YAFFS_OK ? 0 : -1;
1633 int yaffs_access(const YCHAR *path, int amode)
1640 obj = yaffsfs_FindObject(NULL,path,0);
1645 if((amode & R_OK) && !(obj->yst_mode & S_IREAD))
1647 if((amode & W_OK) && !(obj->yst_mode & S_IWRITE))
1649 if((amode & X_OK) && !(obj->yst_mode & S_IEXEC))
1653 yaffsfs_SetError(-EACCES);
1657 /* todo error not found */
1658 yaffsfs_SetError(-ENOENT);
1669 int yaffs_chmod(const YCHAR *path, mode_t mode)
1676 obj = yaffsfs_FindObject(NULL,path,0);
1679 yaffsfs_SetError(-ENOENT);
1680 else if(obj->myDev->readOnly)
1681 yaffsfs_SetError(-EINVAL);
1683 retVal = yaffsfs_DoChMod(obj,mode);
1692 int yaffs_fchmod(int fd, mode_t mode)
1699 obj = yaffsfs_GetHandleObject(fd);
1702 yaffsfs_SetError(-ENOENT);
1703 else if(obj->myDev->readOnly)
1704 yaffsfs_SetError(-EINVAL);
1706 retVal = yaffsfs_DoChMod(obj,mode);
1714 int yaffs_mkdir(const YCHAR *path, mode_t mode)
1716 yaffs_Object *parent = NULL;
1717 yaffs_Object *dir = NULL;
1722 parent = yaffsfs_FindDirectory(NULL,path,&name,0);
1723 if(parent && yaffs_strnlen(name,5) == 0){
1724 /* Trying to make the root itself */
1725 yaffsfs_SetError(-EEXIST);
1726 } else if(parent && parent->myDev->readOnly){
1727 yaffsfs_SetError(-EINVAL);
1730 dir = yaffs_MknodDirectory(parent,name,mode,0,0);
1735 yaffsfs_SetError(-ENOENT); /* missing path */
1736 else if (yaffs_FindObjectByName(parent,name))
1737 yaffsfs_SetError(-EEXIST); /* the name already exists */
1739 yaffsfs_SetError(-ENOSPC); /* just assume no space */
1749 void * yaffs_getdev(const YCHAR *path)
1751 yaffs_Device *dev=NULL;
1753 dev = yaffsfs_FindDevice(path,&dummy);
1757 int yaffs_mount2(const YCHAR *path,int readOnly)
1760 int result=YAFFS_FAIL;
1761 yaffs_Device *dev=NULL;
1764 T(YAFFS_TRACE_ALWAYS,(TSTR("yaffs: Mounting %s" TENDSTR),path));
1768 yaffsfs_InitHandles();
1770 dev = yaffsfs_FindDevice(path,&dummy);
1772 if(!dev->isMounted){
1773 dev->readOnly = readOnly ? 1 : 0;
1774 result = yaffs_GutsInitialise(dev);
1775 if(result == YAFFS_FAIL)
1776 /* todo error - mount failed */
1777 yaffsfs_SetError(-ENOMEM);
1778 retVal = result ? 0 : -1;
1782 /* todo error - already mounted. */
1783 yaffsfs_SetError(-EBUSY);
1785 /* todo error - no device */
1786 yaffsfs_SetError(-ENODEV);
1793 int yaffs_mount(const YCHAR *path)
1795 return yaffs_mount2(path,0);
1798 int yaffs_sync(const YCHAR *path)
1801 yaffs_Device *dev=NULL;
1805 dev = yaffsfs_FindDevice(path,&dummy);
1809 yaffs_FlushEntireDeviceCache(dev);
1810 yaffs_CheckpointSave(dev);
1814 /* todo error - not mounted. */
1815 yaffsfs_SetError(-EINVAL);
1818 /* todo error - no device */
1819 yaffsfs_SetError(-ENODEV);
1826 int yaffs_remount(const YCHAR *path, int force, int readOnly)
1829 yaffs_Device *dev=NULL;
1833 dev = yaffsfs_FindDevice(path,&dummy);
1839 yaffs_FlushEntireDeviceCache(dev);
1841 for(i = inUse = 0; i < YAFFSFS_N_HANDLES && !inUse && !force; i++){
1842 if(yaffsfs_handle[i].useCount>0 && yaffsfs_inode[yaffsfs_handle[i].inodeId].iObj->myDev == dev)
1843 inUse = 1; /* the device is in use, can't unmount */
1846 if(!inUse || force){
1848 yaffs_CheckpointSave(dev);
1849 dev->readOnly = readOnly ? 1 : 0;
1852 yaffsfs_SetError(-EBUSY);
1855 yaffsfs_SetError(-EINVAL);
1859 yaffsfs_SetError(-ENODEV);
1866 int yaffs_unmount2(const YCHAR *path, int force)
1869 yaffs_Device *dev=NULL;
1873 dev = yaffsfs_FindDevice(path,&dummy);
1879 yaffs_FlushEntireDeviceCache(dev);
1880 yaffs_CheckpointSave(dev);
1882 for(i = inUse = 0; i < YAFFSFS_N_HANDLES && !inUse; i++){
1883 if(yaffsfs_handle[i].useCount > 0 && yaffsfs_inode[yaffsfs_handle[i].inodeId].iObj->myDev == dev)
1884 inUse = 1; /* the device is in use, can't unmount */
1887 if(!inUse || force){
1888 yaffs_Deinitialise(dev);
1892 /* todo error can't unmount as files are open */
1893 yaffsfs_SetError(-EBUSY);
1896 /* todo error - not mounted. */
1897 yaffsfs_SetError(-EINVAL);
1901 /* todo error - no device */
1902 yaffsfs_SetError(-ENODEV);
1909 int yaffs_unmount(const YCHAR *path)
1911 return yaffs_unmount2(path,0);
1914 loff_t yaffs_freespace(const YCHAR *path)
1917 yaffs_Device *dev=NULL;
1921 dev = yaffsfs_FindDevice(path,&dummy);
1922 if(dev && dev->isMounted){
1923 retVal = yaffs_GetNumberOfFreeChunks(dev);
1924 retVal *= dev->nDataBytesPerChunk;
1927 yaffsfs_SetError(-EINVAL);
1933 loff_t yaffs_totalspace(const YCHAR *path)
1936 yaffs_Device *dev=NULL;
1940 dev = yaffsfs_FindDevice(path,&dummy);
1941 if(dev && dev->isMounted){
1942 retVal = (dev->param.endBlock - dev->param.startBlock + 1) - dev->param.nReservedBlocks;
1943 retVal *= dev->param.nChunksPerBlock;
1944 retVal *= dev->nDataBytesPerChunk;
1947 yaffsfs_SetError(-EINVAL);
1953 int yaffs_inodecount(const YCHAR *path)
1956 yaffs_Device *dev=NULL;
1960 dev = yaffsfs_FindDevice(path,&dummy);
1961 if(dev && dev->isMounted) {
1962 int nObjects = dev->nObjects;
1963 if(nObjects > dev->nHardLinks)
1964 retVal = nObjects - dev->nHardLinks;
1968 yaffsfs_SetError(-EINVAL);
1975 void yaffs_AddDevice(yaffs_Device *dev)
1978 dev->param.removeObjectCallback = yaffsfs_RemoveObjectCallback;
1980 if(!dev->devList.next)
1981 YINIT_LIST_HEAD(&dev->devList);
1983 ylist_add(&dev->devList,&yaffsfs_deviceList);
1986 void yaffs_RemoveDevice(yaffs_Device *dev)
1988 ylist_del_init(&dev->devList);
1994 /* Directory search stuff. */
1997 * Directory search context
1999 * NB this is an opaque structure.
2006 yaffs_dirent de; /* directory entry being used by this dsc */
2007 YCHAR name[NAME_MAX+1]; /* name of directory being searched */
2008 yaffs_Object *dirObj; /* ptr to directory being searched */
2009 yaffs_Object *nextReturn; /* obj to be returned by next readddir */
2011 struct ylist_head others;
2012 } yaffsfs_DirectorySearchContext;
2016 static struct ylist_head search_contexts;
2019 static void yaffsfs_SetDirRewound(yaffsfs_DirectorySearchContext *dsc)
2023 dsc->dirObj->variantType == YAFFS_OBJECT_TYPE_DIRECTORY){
2027 if( ylist_empty(&dsc->dirObj->variant.directoryVariant.children))
2028 dsc->nextReturn = NULL;
2030 dsc->nextReturn = ylist_entry(dsc->dirObj->variant.directoryVariant.children.next,
2031 yaffs_Object,siblings);
2033 /* Hey someone isn't playing nice! */
2037 static void yaffsfs_DirAdvance(yaffsfs_DirectorySearchContext *dsc)
2041 dsc->dirObj->variantType == YAFFS_OBJECT_TYPE_DIRECTORY){
2043 if( dsc->nextReturn == NULL ||
2044 ylist_empty(&dsc->dirObj->variant.directoryVariant.children))
2045 dsc->nextReturn = NULL;
2047 struct ylist_head *next = dsc->nextReturn->siblings.next;
2049 if( next == &dsc->dirObj->variant.directoryVariant.children)
2050 dsc->nextReturn = NULL; /* end of list */
2052 dsc->nextReturn = ylist_entry(next,yaffs_Object,siblings);
2055 /* Hey someone isn't playing nice! */
2059 static void yaffsfs_RemoveObjectCallback(yaffs_Object *obj)
2062 struct ylist_head *i;
2063 yaffsfs_DirectorySearchContext *dsc;
2065 /* if search contexts not initilised then skip */
2066 if(!search_contexts.next)
2069 /* Iterate through the directory search contexts.
2070 * If any are the one being removed, then advance the dsc to
2071 * the next one to prevent a hanging ptr.
2073 ylist_for_each(i, &search_contexts) {
2075 dsc = ylist_entry(i, yaffsfs_DirectorySearchContext,others);
2076 if(dsc->nextReturn == obj)
2077 yaffsfs_DirAdvance(dsc);
2083 yaffs_DIR *yaffs_opendir(const YCHAR *dirname)
2085 yaffs_DIR *dir = NULL;
2086 yaffs_Object *obj = NULL;
2087 yaffsfs_DirectorySearchContext *dsc = NULL;
2091 obj = yaffsfs_FindObject(NULL,dirname,0);
2093 if(obj && obj->variantType == YAFFS_OBJECT_TYPE_DIRECTORY){
2095 dsc = YMALLOC(sizeof(yaffsfs_DirectorySearchContext));
2096 dir = (yaffs_DIR *)dsc;
2099 memset(dsc,0,sizeof(yaffsfs_DirectorySearchContext));
2100 dsc->magic = YAFFS_MAGIC;
2102 yaffs_strncpy(dsc->name,dirname,NAME_MAX);
2103 YINIT_LIST_HEAD(&dsc->others);
2105 if(!search_contexts.next)
2106 YINIT_LIST_HEAD(&search_contexts);
2108 ylist_add(&dsc->others,&search_contexts);
2109 yaffsfs_SetDirRewound(dsc);
2119 struct yaffs_dirent *yaffs_readdir(yaffs_DIR *dirp)
2121 yaffsfs_DirectorySearchContext *dsc = (yaffsfs_DirectorySearchContext *)dirp;
2122 struct yaffs_dirent *retVal = NULL;
2126 if(dsc && dsc->magic == YAFFS_MAGIC){
2127 yaffsfs_SetError(0);
2128 if(dsc->nextReturn){
2129 dsc->de.d_ino = yaffs_GetEquivalentObject(dsc->nextReturn)->objectId;
2130 dsc->de.d_dont_use = (unsigned)dsc->nextReturn;
2131 dsc->de.d_off = dsc->offset++;
2132 yaffs_GetObjectName(dsc->nextReturn,dsc->de.d_name,NAME_MAX);
2133 if(yaffs_strnlen(dsc->de.d_name,NAME_MAX+1) == 0)
2135 /* this should not happen! */
2136 yaffs_strcpy(dsc->de.d_name,_Y("zz"));
2138 dsc->de.d_reclen = sizeof(struct yaffs_dirent);
2140 yaffsfs_DirAdvance(dsc);
2144 yaffsfs_SetError(-EBADF);
2153 void yaffs_rewinddir(yaffs_DIR *dirp)
2155 yaffsfs_DirectorySearchContext *dsc = (yaffsfs_DirectorySearchContext *)dirp;
2159 yaffsfs_SetDirRewound(dsc);
2165 int yaffs_closedir(yaffs_DIR *dirp)
2167 yaffsfs_DirectorySearchContext *dsc = (yaffsfs_DirectorySearchContext *)dirp;
2171 ylist_del(&dsc->others); /* unhook from list */
2177 /* End of directory stuff */
2180 int yaffs_symlink(const YCHAR *oldpath, const YCHAR *newpath)
2182 yaffs_Object *parent = NULL;
2186 int mode = 0; /* ignore for now */
2189 parent = yaffsfs_FindDirectory(NULL,newpath,&name,0);
2190 if(parent && parent->myDev->readOnly)
2191 yaffsfs_SetError(-EINVAL);
2193 obj = yaffs_MknodSymLink(parent,name,mode,0,0,oldpath);
2197 yaffsfs_SetError(-ENOSPC); /* just assume no space for now */
2201 yaffsfs_SetError(-EINVAL);
2211 int yaffs_readlink(const YCHAR *path, YCHAR *buf, int bufsiz)
2213 yaffs_Object *obj = NULL;
2219 obj = yaffsfs_FindObject(NULL,path,0);
2222 yaffsfs_SetError(-ENOENT);
2224 } else if(obj->variantType != YAFFS_OBJECT_TYPE_SYMLINK) {
2225 yaffsfs_SetError(-EINVAL);
2228 YCHAR *alias = obj->variant.symLinkVariant.alias;
2229 memset(buf,0,bufsiz);
2230 yaffs_strncpy(buf,alias,bufsiz - 1);
2237 int yaffs_link(const YCHAR *oldpath, const YCHAR *newpath)
2239 /* Creates a link called newpath to existing oldpath */
2240 yaffs_Object *obj = NULL;
2241 yaffs_Object *target = NULL;
2243 int newNameLength = 0;
2248 obj = yaffsfs_FindObject(NULL,oldpath,0);
2249 target = yaffsfs_FindObject(NULL,newpath,0);
2252 yaffsfs_SetError(-ENOENT);
2254 } else if(obj->myDev->readOnly){
2255 yaffsfs_SetError(-EINVAL);
2258 yaffsfs_SetError(-EEXIST);
2261 yaffs_Object *newdir = NULL;
2262 yaffs_Object *link = NULL;
2266 newdir = yaffsfs_FindDirectory(NULL,newpath,&newname,0);
2269 yaffsfs_SetError(-ENOTDIR);
2271 }else if(newdir->myDev != obj->myDev){
2272 yaffsfs_SetError(-EXDEV);
2276 newNameLength = yaffs_strnlen(newname,YAFFS_MAX_NAME_LENGTH+1);
2278 if(newNameLength == 0){
2279 yaffsfs_SetError(-ENOENT);
2281 } else if (newNameLength > YAFFS_MAX_NAME_LENGTH){
2282 yaffsfs_SetError(-ENAMETOOLONG);
2287 link = yaffs_Link(newdir,newname,obj);
2291 yaffsfs_SetError(-ENOSPC);
2302 int yaffs_mknod(const YCHAR *pathname, mode_t mode, dev_t dev)
2311 * Returns number of handles attached to the object
2313 int yaffs_n_handles(const YCHAR *path)
2317 obj = yaffsfs_FindObject(NULL,path,0);
2319 obj = yaffs_GetEquivalentObject(obj);
2321 return yaffsfs_CountHandles(obj);
2324 int yaffs_DumpDevStruct(const YCHAR *path)
2329 yaffs_Object *obj = yaffsfs_FindRoot(path,&rest);
2332 yaffs_Device *dev = obj->myDev;
2335 "nPageWrites.......... %d\n"
2336 "nPageReads........... %d\n"
2337 "nBlockErasures....... %d\n"
2338 "nGCCopies............ %d\n"
2339 "garbageCollections... %d\n"
2340 "passiveGarbageColl'ns %d\n"
2344 dev->nBlockErasures,
2346 dev->garbageCollections,
2347 dev->passiveGarbageCollections