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)
432 obj = yaffs_GetEquivalentObject(obj);
434 while(obj && obj->variantType == YAFFS_OBJECT_TYPE_SYMLINK){
435 YCHAR *alias = obj->variant.symLinkVariant.alias;
437 if(yaffsfs_IsPathDivider(*alias))
438 /* Starts with a /, need to scan from root up */
439 obj = yaffsfs_FindObject(NULL,alias,symDepth++);
441 /* Relative to here, so use the parent of the symlink as a start */
442 obj = yaffsfs_FindObject(obj->parent,alias,symDepth++);
445 obj = yaffs_GetEquivalentObject(obj);
452 * yaffsfs_FindDirectory
453 * Parse a path to determine the directory and the name within the directory.
455 * eg. "/data/xx/ff" --> puts name="ff" and returns the directory "/data/xx"
457 static yaffs_Object *yaffsfs_DoFindDirectory(yaffs_Object *startDir,
458 const YCHAR *path, YCHAR **name, int symDepth)
462 YCHAR str[YAFFS_MAX_NAME_LENGTH+1];
465 if(symDepth > YAFFSFS_MAX_SYMLINK_DEREFERENCES)
470 restOfPath = (YCHAR *)path;
473 dir = yaffsfs_FindRoot(path,&restOfPath);
478 * curve ball: also throw away surplus '/'
479 * eg. "/ram/x////ff" gets treated the same as "/ram/x/ff"
481 while(yaffsfs_IsPathDivider(*restOfPath))
482 restOfPath++; /* get rid of '/' */
487 while(*restOfPath && !yaffsfs_IsPathDivider(*restOfPath)){
488 if (i < YAFFS_MAX_NAME_LENGTH){
489 str[i] = *restOfPath;
497 /* got to the end of the string */
500 if(yaffs_strcmp(str,_Y(".")) == 0)
504 else if(yaffs_strcmp(str,_Y("..")) == 0)
507 dir = yaffs_FindObjectByName(dir,str);
509 dir = yaffsfs_FollowLink(dir,symDepth);
512 if(dir && dir->variantType != YAFFS_OBJECT_TYPE_DIRECTORY)
517 /* directory did not exist. */
521 static yaffs_Object *yaffsfs_FindDirectory(yaffs_Object *relativeDirectory,
522 const YCHAR *path,YCHAR **name,int symDepth)
524 return yaffsfs_DoFindDirectory(relativeDirectory,path,name,symDepth);
528 * yaffsfs_FindObject turns a path for an existing object into the object
530 static yaffs_Object *yaffsfs_FindObject(yaffs_Object *relativeDirectory, const YCHAR *path,int symDepth)
535 dir = yaffsfs_FindDirectory(relativeDirectory,path,&name,symDepth);
538 return yaffs_FindObjectByName(dir,name);
544 int yaffs_dup(int fd)
547 yaffsfs_Handle *oldPtr = NULL;
548 yaffsfs_Handle *newPtr = NULL;
552 oldPtr = yaffsfs_GetHandlePointer(fd);
553 if(oldPtr && oldPtr->useCount > 0)
554 newHandle = yaffsfs_GetNewHandle();
556 newPtr = yaffsfs_GetHandlePointer(newHandle);
564 yaffsfs_SetError(-EBADF);
566 yaffsfs_SetError(-ENOMEM);
572 int yaffs_open_sharing(const YCHAR *path, int oflag, int mode, int sharing)
574 yaffs_Object *obj = NULL;
575 yaffs_Object *dir = NULL;
578 yaffsfs_Handle *yh = NULL;
581 int errorReported = 0;
582 __u8 shareRead = (sharing & YAFFS_SHARE_READ) ? 1 : 0;
583 __u8 shareWrite = (sharing & YAFFS_SHARE_WRITE) ? 1 : 0;
584 __u8 sharedReadAllowed;
585 __u8 sharedWriteAllowed;
591 /* O_EXCL only has meaning if O_CREAT is specified */
592 if(!(oflag & O_CREAT))
595 /* O_TRUNC has no meaning if (O_CREAT | O_EXCL) is specified */
596 if( (oflag & O_CREAT) & (oflag & O_EXCL))
599 /* Todo: Are there any more flag combos to sanitise ? */
604 handle = yaffsfs_GetNewHandle();
608 yh = yaffsfs_GetHandlePointer(handle);
610 /* try to find the exisiting object */
611 obj = yaffsfs_FindObject(NULL,path,0);
613 obj = yaffsfs_FollowLink(obj,symDepth++);
616 obj->variantType != YAFFS_OBJECT_TYPE_FILE &&
617 obj->variantType != YAFFS_OBJECT_TYPE_DIRECTORY)
622 /* The file already exists or it might be a directory */
624 /* If it is a directory then we can't open it as a file */
625 if(obj->variantType == YAFFS_OBJECT_TYPE_DIRECTORY){
627 yaffsfs_SetError(-EISDIR);
631 /* Open should fail if O_CREAT and O_EXCL are specified since
634 if((oflag & O_EXCL) && (oflag & O_CREAT)){
636 yaffsfs_SetError(-EEXIST);
640 /* Check file permissions */
641 if( (oflag & (O_RDWR | O_WRONLY)) == 0 && /* ie O_RDONLY */
642 !(obj->yst_mode & S_IREAD))
645 if( (oflag & O_RDWR) &&
646 !(obj->yst_mode & S_IREAD))
649 if( (oflag & (O_RDWR | O_WRONLY)) &&
650 !(obj->yst_mode & S_IWRITE))
653 /* Check sharing of an existing object. */
657 sharedReadAllowed = 1;
658 sharedWriteAllowed = 1;
661 for( i = 0; i < YAFFSFS_N_HANDLES; i++){
662 hx = &yaffsfs_handle[i];
663 if(hx->useCount > 0 &&
665 yaffsfs_inode[hx->inodeId].iObj == obj){
667 sharedReadAllowed = 0;
669 sharedWriteAllowed = 0;
677 readRequested = (oflag & (O_RDWR | O_RDONLY)) ? 1 : 0;
678 writeRequested = (oflag & (O_RDWR | O_WRONLY)) ? 1 : 0;
680 if((!sharedReadAllowed && readRequested)||
681 (!shareRead && alreadyReading) ||
682 (!sharedWriteAllowed && writeRequested) ||
683 (!shareWrite && alreadyWriting)){
685 yaffsfs_SetError(-EBUSY);
690 } else if((oflag & O_CREAT)) {
691 /* Let's see if we can create this file */
692 dir = yaffsfs_FindDirectory(NULL,path,&name,0);
693 if(dir && dir->myDev->readOnly){
694 yaffsfs_SetError(-EINVAL);
697 obj = yaffs_MknodFile(dir,name,mode,0,0);
699 yaffsfs_SetError(-ENOTDIR);
704 if(obj && !openDenied) {
705 int inodeId = yaffsfs_GetInodeIdForObject(obj);
709 * Todo: Fix any problem if inodes run out, though that
710 * can't happen if the number of inode items >= number of handles.
714 yh->inodeId = inodeId;
715 yh->reading = (oflag & (O_RDONLY | O_RDWR)) ? 1 : 0;
716 yh->writing = (oflag & (O_WRONLY | O_RDWR)) ? 1 : 0;
717 yh->append = (oflag & O_APPEND) ? 1 : 0;
719 yh->shareRead = shareRead;
720 yh->shareWrite = shareWrite;
722 /* Hook inode to object */
723 obj->myInode = (void*) &yaffsfs_inode[inodeId];
725 if((oflag & O_TRUNC) && yh->writing)
726 yaffs_ResizeFile(obj,0);
728 yaffsfs_PutHandle(handle);
730 yaffsfs_SetError(-EACCES);
742 int yaffs_open(const YCHAR *path, int oflag, int mode)
744 return yaffs_open_sharing(path, oflag, mode, YAFFS_SHARE_READ | YAFFS_SHARE_WRITE);
747 int yaffs_Dofsync(int fd,int datasync)
749 yaffsfs_Handle *h = NULL;
754 h = yaffsfs_GetHandlePointer(fd);
756 if(h && h->useCount > 0)
758 yaffs_FlushFile(yaffsfs_inode[h->inodeId].iObj,1,datasync);
761 yaffsfs_SetError(-EBADF);
770 int yaffs_fsync(int fd)
772 return yaffs_Dofsync(fd,0);
775 int yaffs_flush(int fd)
777 return yaffs_fsync(fd);
780 int yaffs_fdatasync(int fd)
782 return yaffs_Dofsync(fd,1);
785 int yaffs_close(int fd)
787 yaffsfs_Handle *h = NULL;
792 h = yaffsfs_GetHandlePointer(fd);
794 if(h && h->useCount > 0) {
796 yaffs_FlushFile(yaffsfs_inode[h->inodeId].iObj,1,0);
797 yaffsfs_PutHandle(fd);
801 yaffsfs_SetError(-EBADF);
812 int yaffsfs_do_read(int fd, void *vbuf, unsigned int nbyte, int isPread, int offset)
814 yaffsfs_Handle *h = NULL;
815 yaffs_Object *obj = NULL;
821 unsigned int maxRead;
822 __u8 *buf = (__u8 *)vbuf;
825 h = yaffsfs_GetHandlePointer(fd);
826 obj = yaffsfs_GetHandleObject(fd);
830 yaffsfs_SetError(-EBADF);
832 } else if( h && obj){
836 startPos = h->position;
840 if(yaffs_GetObjectFileLength(obj) > pos)
841 maxRead = yaffs_GetObjectFileLength(obj) - pos;
849 yaffsfs_GetHandle(fd);
852 nToRead = YAFFSFS_RW_SIZE - (pos & (YAFFSFS_RW_SIZE -1));
855 nRead = yaffs_ReadDataFromFile(obj,buf,pos,nToRead);
866 nbyte = 0; /* no more to read */
876 yaffsfs_PutHandle(fd);
880 h->position = startPos + totalRead;
890 return (totalRead >= 0) ? totalRead : -1;
894 int yaffs_read(int fd, void *buf, unsigned int nbyte)
896 return yaffsfs_do_read(fd, buf, nbyte, 0, 0);
899 int yaffs_pread(int fd, void *buf, unsigned int nbyte, unsigned int offset)
901 return yaffsfs_do_read(fd, buf, nbyte, 1, offset);
904 int yaffsfs_do_write(int fd, const void *vbuf, unsigned int nbyte, int isPwrite, int offset)
906 yaffsfs_Handle *h = NULL;
907 yaffs_Object *obj = NULL;
911 int totalWritten = 0;
912 int writeThrough = 0;
914 const __u8 *buf = (const __u8 *)vbuf;
917 h = yaffsfs_GetHandlePointer(fd);
918 obj = yaffsfs_GetHandleObject(fd);
922 yaffsfs_SetError(-EBADF);
924 } else if( h && obj && (!h->writing || obj->myDev->readOnly)){
925 yaffsfs_SetError(-EINVAL);
927 } else if( h && obj){
929 startPos = yaffs_GetObjectFileLength(obj);
933 startPos = h->position;
935 yaffsfs_GetHandle(fd);
938 nToWrite = YAFFSFS_RW_SIZE - (pos & (YAFFSFS_RW_SIZE -1));
942 nWritten = yaffs_WriteDataToFile(obj,buf,pos,nToWrite,writeThrough);
944 totalWritten += nWritten;
949 if(nWritten == nToWrite)
954 if(nWritten < 1 && totalWritten < 1){
955 yaffsfs_SetError(-ENOSPC);
965 yaffsfs_PutHandle(fd);
969 h->position = startPos + totalWritten;
978 return (totalWritten >= 0) ? totalWritten : -1;
981 int yaffs_write(int fd, const void *buf, unsigned int nbyte)
983 return yaffsfs_do_write(fd, buf, nbyte, 0, 0);
986 int yaffs_pwrite(int fd, const void *buf, unsigned int nbyte, unsigned int offset)
988 return yaffsfs_do_write(fd, buf, nbyte, 1, offset);
992 int yaffs_truncate(const YCHAR *path,off_t newSize)
994 yaffs_Object *obj = NULL;
995 int result = YAFFS_FAIL;
999 obj = yaffsfs_FindObject(NULL,path,0);
1001 obj = yaffs_GetEquivalentObject(obj);
1004 yaffsfs_SetError(-ENOENT);
1005 else if(obj->variantType != YAFFS_OBJECT_TYPE_FILE)
1006 yaffsfs_SetError(-EISDIR);
1007 else if(obj->myDev->readOnly)
1008 yaffsfs_SetError(-EINVAL);
1010 result = yaffs_ResizeFile(obj,newSize);
1015 return (result) ? 0 : -1;
1018 int yaffs_ftruncate(int fd, off_t newSize)
1020 yaffsfs_Handle *h = NULL;
1021 yaffs_Object *obj = NULL;
1025 h = yaffsfs_GetHandlePointer(fd);
1026 obj = yaffsfs_GetHandleObject(fd);
1030 yaffsfs_SetError(-EBADF);
1031 else if(obj->myDev->readOnly)
1032 yaffsfs_SetError(-EINVAL);
1034 /* resize the file */
1035 result = yaffs_ResizeFile(obj,newSize);
1039 return (result) ? 0 : -1;
1043 off_t yaffs_lseek(int fd, off_t offset, int whence)
1045 yaffsfs_Handle *h = NULL;
1046 yaffs_Object *obj = NULL;
1051 h = yaffsfs_GetHandlePointer(fd);
1052 obj = yaffsfs_GetHandleObject(fd);
1056 yaffsfs_SetError(-EBADF);
1057 else if(whence == SEEK_SET){
1061 else if(whence == SEEK_CUR) {
1062 if( (h->position + offset) >= 0)
1063 pos = (h->position + offset);
1065 else if(whence == SEEK_END) {
1066 fSize = yaffs_GetObjectFileLength(obj);
1067 if(fSize >= 0 && (fSize + offset) >= 0)
1068 pos = fSize + offset;
1084 int yaffsfs_DoUnlink(const YCHAR *path,int isDirectory)
1086 yaffs_Object *dir = NULL;
1087 yaffs_Object *obj = NULL;
1089 int result = YAFFS_FAIL;
1093 obj = yaffsfs_FindObject(NULL,path,0);
1094 dir = yaffsfs_FindDirectory(NULL,path,&name,0);
1096 yaffsfs_SetError(-ENOTDIR);
1098 yaffsfs_SetError(-ENOENT);
1099 else if(obj->myDev->readOnly)
1100 yaffsfs_SetError(-EINVAL);
1101 else if(!isDirectory && obj->variantType == YAFFS_OBJECT_TYPE_DIRECTORY)
1102 yaffsfs_SetError(-EISDIR);
1103 else if(isDirectory && obj->variantType != YAFFS_OBJECT_TYPE_DIRECTORY)
1104 yaffsfs_SetError(-ENOTDIR);
1106 result = yaffs_Unlink(dir,name);
1108 if(result == YAFFS_FAIL && isDirectory)
1109 yaffsfs_SetError(-ENOTEMPTY);
1116 return (result == YAFFS_FAIL) ? -1 : 0;
1120 int yaffs_rmdir(const YCHAR *path)
1122 return yaffsfs_DoUnlink(path,1);
1125 int yaffs_unlink(const YCHAR *path)
1127 return yaffsfs_DoUnlink(path,0);
1130 int yaffs_rename(const YCHAR *oldPath, const YCHAR *newPath)
1132 yaffs_Object *olddir = NULL;
1133 yaffs_Object *newdir = NULL;
1134 yaffs_Object *obj = NULL;
1137 int result= YAFFS_FAIL;
1138 int renameAllowed = 1;
1142 olddir = yaffsfs_FindDirectory(NULL,oldPath,&oldname,0);
1143 newdir = yaffsfs_FindDirectory(NULL,newPath,&newname,0);
1144 obj = yaffsfs_FindObject(NULL,oldPath,0);
1146 if(!olddir || !newdir || !obj) {
1148 yaffsfs_SetError(-EBADF);
1150 } else if(obj->myDev->readOnly){
1151 yaffsfs_SetError(-EINVAL);
1153 } else if(olddir->myDev != newdir->myDev) {
1154 /* oops must be on same device */
1156 yaffsfs_SetError(-EXDEV);
1158 } else if(obj && obj->variantType == YAFFS_OBJECT_TYPE_DIRECTORY) {
1160 * It is a directory, check that it is not being renamed to
1161 * being its own decendent.
1162 * Do this by tracing from the new directory back to the root, checking for obj
1165 yaffs_Object *xx = newdir;
1167 while( renameAllowed && xx){
1173 yaffsfs_SetError(-EACCES);
1177 result = yaffs_RenameObject(olddir,oldname,newdir,newname);
1181 return (result == YAFFS_FAIL) ? -1 : 0;
1185 static int yaffsfs_DoStat(yaffs_Object *obj,struct yaffs_stat *buf)
1190 obj = yaffs_GetEquivalentObject(obj);
1193 buf->st_dev = (int)obj->myDev->osContext;
1194 buf->st_ino = obj->objectId;
1195 buf->st_mode = obj->yst_mode & ~S_IFMT; /* clear out file type bits */
1197 if(obj->variantType == YAFFS_OBJECT_TYPE_DIRECTORY)
1198 buf->st_mode |= S_IFDIR;
1199 else if(obj->variantType == YAFFS_OBJECT_TYPE_SYMLINK)
1200 buf->st_mode |= S_IFLNK;
1201 else if(obj->variantType == YAFFS_OBJECT_TYPE_FILE)
1202 buf->st_mode |= S_IFREG;
1204 buf->st_nlink = yaffs_GetObjectLinkCount(obj);
1207 buf->st_rdev = obj->yst_rdev;
1208 buf->st_size = yaffs_GetObjectFileLength(obj);
1209 buf->st_blksize = obj->myDev->nDataBytesPerChunk;
1210 buf->st_blocks = (buf->st_size + buf->st_blksize -1)/buf->st_blksize;
1211 #if CONFIG_YAFFS_WINCE
1212 buf->yst_wince_atime[0] = obj->win_atime[0];
1213 buf->yst_wince_atime[1] = obj->win_atime[1];
1214 buf->yst_wince_ctime[0] = obj->win_ctime[0];
1215 buf->yst_wince_ctime[1] = obj->win_ctime[1];
1216 buf->yst_wince_mtime[0] = obj->win_mtime[0];
1217 buf->yst_wince_mtime[1] = obj->win_mtime[1];
1219 buf->yst_atime = obj->yst_atime;
1220 buf->yst_ctime = obj->yst_ctime;
1221 buf->yst_mtime = obj->yst_mtime;
1228 static int yaffsfs_DoStatOrLStat(const YCHAR *path, struct yaffs_stat *buf,int doLStat)
1235 obj = yaffsfs_FindObject(NULL,path,0);
1238 obj = yaffsfs_FollowLink(obj,0);
1241 retVal = yaffsfs_DoStat(obj,buf);
1243 /* todo error not found */
1244 yaffsfs_SetError(-ENOENT);
1252 int yaffs_stat(const YCHAR *path, struct yaffs_stat *buf)
1254 return yaffsfs_DoStatOrLStat(path,buf,0);
1257 int yaffs_lstat(const YCHAR *path, struct yaffs_stat *buf)
1259 return yaffsfs_DoStatOrLStat(path,buf,1);
1262 int yaffs_fstat(int fd, struct yaffs_stat *buf)
1269 obj = yaffsfs_GetHandleObject(fd);
1272 retVal = yaffsfs_DoStat(obj,buf);
1275 yaffsfs_SetError(-EBADF);
1282 #ifndef CONFIG_YAFFS_WINCE
1283 /* xattrib functions */
1286 static int yaffs_do_setxattr(const YCHAR *path, const char *name, const void *data, int size, int flags, int follow)
1293 obj = yaffsfs_FindObject(NULL,path,0);
1296 obj = yaffsfs_FollowLink(obj,0);
1299 retVal = yaffs_SetXAttribute(obj,name,data,size,flags);
1301 yaffsfs_SetError(retVal);
1305 /* todo error not found */
1306 yaffsfs_SetError(-ENOENT);
1314 int yaffs_setxattr(const YCHAR *path, const char *name, const void *data, int size, int flags)
1316 return yaffs_do_setxattr(path, name, data, size, flags, 1);
1319 int yaffs_lsetxattr(const YCHAR *path, const char *name, const void *data, int size, int flags)
1321 return yaffs_do_setxattr(path, name, data, size, flags, 0);
1326 int yaffs_fsetxattr(int fd, const char *name, const void *data, int size, int flags)
1333 obj = yaffsfs_GetHandleObject(fd);
1336 retVal = yaffs_SetXAttribute(obj,name,data,size,flags);
1338 yaffsfs_SetError(retVal);
1343 yaffsfs_SetError(-EBADF);
1350 static int yaffs_do_getxattr(const YCHAR *path, const char *name, void *data, int size, int follow)
1357 obj = yaffsfs_FindObject(NULL,path,0);
1360 obj = yaffsfs_FollowLink(obj,0);
1363 retVal = yaffs_GetXAttribute(obj,name,data,size);
1365 yaffsfs_SetError(retVal);
1369 /* todo error not found */
1370 yaffsfs_SetError(-ENOENT);
1378 int yaffs_getxattr(const YCHAR *path, const char *name, void *data, int size)
1380 return yaffs_do_getxattr( path, name, data, size, 1);
1382 int yaffs_lgetxattr(const YCHAR *path, const char *name, void *data, int size)
1384 return yaffs_do_getxattr( path, name, data, size, 0);
1389 int yaffs_fgetxattr(int fd, const char *name, void *data, int size)
1396 obj = yaffsfs_GetHandleObject(fd);
1399 retVal = yaffs_GetXAttribute(obj,name,data,size);
1401 yaffsfs_SetError(retVal);
1406 yaffsfs_SetError(-EBADF);
1413 static int yaffs_do_listxattr(const YCHAR *path, char *data, int size, int follow)
1420 obj = yaffsfs_FindObject(NULL,path,0);
1423 obj = yaffsfs_FollowLink(obj,0);
1426 retVal = yaffs_ListXAttributes(obj, data,size);
1428 yaffsfs_SetError(retVal);
1432 /* todo error not found */
1433 yaffsfs_SetError(-ENOENT);
1441 int yaffs_listxattr(const YCHAR *path, char *data, int size)
1443 return yaffs_do_listxattr(path, data, size, 1);
1446 int yaffs_llistxattr(const YCHAR *path, char *data, int size)
1448 return yaffs_do_listxattr(path, data, size, 0);
1451 int yaffs_flistxattr(int fd, char *data, int size)
1458 obj = yaffsfs_GetHandleObject(fd);
1461 retVal = yaffs_ListXAttributes(obj,data,size);
1463 yaffsfs_SetError(retVal);
1468 yaffsfs_SetError(-EBADF);
1475 static int yaffs_do_removexattr(const YCHAR *path, const char *name, int follow)
1482 obj = yaffsfs_FindObject(NULL,path,0);
1485 obj = yaffsfs_FollowLink(obj,0);
1488 retVal = yaffs_RemoveXAttribute(obj,name);
1490 yaffsfs_SetError(retVal);
1494 /* todo error not found */
1495 yaffsfs_SetError(-ENOENT);
1503 int yaffs_removexattr(const YCHAR *path, const char *name)
1505 return yaffs_do_removexattr(path, name, 1);
1508 int yaffs_lremovexattr(const YCHAR *path, const char *name)
1510 return yaffs_do_removexattr(path, name, 0);
1513 int yaffs_fremovexattr(int fd, const char *name)
1520 obj = yaffsfs_GetHandleObject(fd);
1523 retVal = yaffs_RemoveXAttribute(obj,name);
1525 yaffsfs_SetError(retVal);
1530 yaffsfs_SetError(-EBADF);
1538 #ifdef CONFIG_YAFFS_WINCE
1539 int yaffs_get_wince_times(int fd, unsigned *wctime, unsigned *watime, unsigned *wmtime)
1546 obj = yaffsfs_GetHandleObject(fd);
1551 wctime[0] = obj->win_ctime[0];
1552 wctime[1] = obj->win_ctime[1];
1555 watime[0] = obj->win_atime[0];
1556 watime[1] = obj->win_atime[1];
1559 wmtime[0] = obj->win_mtime[0];
1560 wmtime[1] = obj->win_mtime[1];
1567 yaffsfs_SetError(-EBADF);
1575 int yaffs_set_wince_times(int fd,
1576 const unsigned *wctime,
1577 const unsigned *watime,
1578 const unsigned *wmtime)
1585 obj = yaffsfs_GetHandleObject(fd);
1590 obj->win_ctime[0] = wctime[0];
1591 obj->win_ctime[1] = wctime[1];
1594 obj->win_atime[0] = watime[0];
1595 obj->win_atime[1] = watime[1];
1598 obj->win_mtime[0] = wmtime[0];
1599 obj->win_mtime[1] = wmtime[1];
1603 result = yaffs_FlushFile(obj,0,0);
1607 yaffsfs_SetError(-EBADF);
1617 static int yaffsfs_DoChMod(yaffs_Object *obj,mode_t mode)
1622 obj = yaffs_GetEquivalentObject(obj);
1625 obj->yst_mode = mode;
1627 result = yaffs_FlushFile(obj,0,0);
1630 return result == YAFFS_OK ? 0 : -1;
1634 int yaffs_access(const YCHAR *path, int amode)
1641 obj = yaffsfs_FindObject(NULL,path,0);
1646 if((amode & R_OK) && !(obj->yst_mode & S_IREAD))
1648 if((amode & W_OK) && !(obj->yst_mode & S_IWRITE))
1650 if((amode & X_OK) && !(obj->yst_mode & S_IEXEC))
1654 yaffsfs_SetError(-EACCES);
1658 /* todo error not found */
1659 yaffsfs_SetError(-ENOENT);
1670 int yaffs_chmod(const YCHAR *path, mode_t mode)
1677 obj = yaffsfs_FindObject(NULL,path,0);
1680 yaffsfs_SetError(-ENOENT);
1681 else if(obj->myDev->readOnly)
1682 yaffsfs_SetError(-EINVAL);
1684 retVal = yaffsfs_DoChMod(obj,mode);
1693 int yaffs_fchmod(int fd, mode_t mode)
1700 obj = yaffsfs_GetHandleObject(fd);
1703 yaffsfs_SetError(-ENOENT);
1704 else if(obj->myDev->readOnly)
1705 yaffsfs_SetError(-EINVAL);
1707 retVal = yaffsfs_DoChMod(obj,mode);
1715 int yaffs_mkdir(const YCHAR *path, mode_t mode)
1717 yaffs_Object *parent = NULL;
1718 yaffs_Object *dir = NULL;
1723 parent = yaffsfs_FindDirectory(NULL,path,&name,0);
1724 if(parent && yaffs_strnlen(name,5) == 0){
1725 /* Trying to make the root itself */
1726 yaffsfs_SetError(-EEXIST);
1727 } else if(parent && parent->myDev->readOnly){
1728 yaffsfs_SetError(-EINVAL);
1731 dir = yaffs_MknodDirectory(parent,name,mode,0,0);
1736 yaffsfs_SetError(-ENOENT); /* missing path */
1737 else if (yaffs_FindObjectByName(parent,name))
1738 yaffsfs_SetError(-EEXIST); /* the name already exists */
1740 yaffsfs_SetError(-ENOSPC); /* just assume no space */
1750 void * yaffs_getdev(const YCHAR *path)
1752 yaffs_Device *dev=NULL;
1754 dev = yaffsfs_FindDevice(path,&dummy);
1758 int yaffs_mount2(const YCHAR *path,int readOnly)
1761 int result=YAFFS_FAIL;
1762 yaffs_Device *dev=NULL;
1765 T(YAFFS_TRACE_ALWAYS,(TSTR("yaffs: Mounting %s" TENDSTR),path));
1769 yaffsfs_InitHandles();
1771 dev = yaffsfs_FindDevice(path,&dummy);
1773 if(!dev->isMounted){
1774 dev->readOnly = readOnly ? 1 : 0;
1775 result = yaffs_GutsInitialise(dev);
1776 if(result == YAFFS_FAIL)
1777 /* todo error - mount failed */
1778 yaffsfs_SetError(-ENOMEM);
1779 retVal = result ? 0 : -1;
1783 /* todo error - already mounted. */
1784 yaffsfs_SetError(-EBUSY);
1786 /* todo error - no device */
1787 yaffsfs_SetError(-ENODEV);
1794 int yaffs_mount(const YCHAR *path)
1796 return yaffs_mount2(path,0);
1799 int yaffs_sync(const YCHAR *path)
1802 yaffs_Device *dev=NULL;
1806 dev = yaffsfs_FindDevice(path,&dummy);
1810 yaffs_FlushEntireDeviceCache(dev);
1811 yaffs_CheckpointSave(dev);
1815 /* todo error - not mounted. */
1816 yaffsfs_SetError(-EINVAL);
1819 /* todo error - no device */
1820 yaffsfs_SetError(-ENODEV);
1827 int yaffs_remount(const YCHAR *path, int force, int readOnly)
1830 yaffs_Device *dev=NULL;
1834 dev = yaffsfs_FindDevice(path,&dummy);
1840 yaffs_FlushEntireDeviceCache(dev);
1842 for(i = inUse = 0; i < YAFFSFS_N_HANDLES && !inUse && !force; i++){
1843 if(yaffsfs_handle[i].useCount>0 && yaffsfs_inode[yaffsfs_handle[i].inodeId].iObj->myDev == dev)
1844 inUse = 1; /* the device is in use, can't unmount */
1847 if(!inUse || force){
1849 yaffs_CheckpointSave(dev);
1850 dev->readOnly = readOnly ? 1 : 0;
1853 yaffsfs_SetError(-EBUSY);
1856 yaffsfs_SetError(-EINVAL);
1860 yaffsfs_SetError(-ENODEV);
1867 int yaffs_unmount2(const YCHAR *path, int force)
1870 yaffs_Device *dev=NULL;
1874 dev = yaffsfs_FindDevice(path,&dummy);
1880 yaffs_FlushEntireDeviceCache(dev);
1881 yaffs_CheckpointSave(dev);
1883 for(i = inUse = 0; i < YAFFSFS_N_HANDLES && !inUse; i++){
1884 if(yaffsfs_handle[i].useCount > 0 && yaffsfs_inode[yaffsfs_handle[i].inodeId].iObj->myDev == dev)
1885 inUse = 1; /* the device is in use, can't unmount */
1888 if(!inUse || force){
1889 yaffs_Deinitialise(dev);
1893 /* todo error can't unmount as files are open */
1894 yaffsfs_SetError(-EBUSY);
1897 /* todo error - not mounted. */
1898 yaffsfs_SetError(-EINVAL);
1902 /* todo error - no device */
1903 yaffsfs_SetError(-ENODEV);
1910 int yaffs_unmount(const YCHAR *path)
1912 return yaffs_unmount2(path,0);
1915 loff_t yaffs_freespace(const YCHAR *path)
1918 yaffs_Device *dev=NULL;
1922 dev = yaffsfs_FindDevice(path,&dummy);
1923 if(dev && dev->isMounted){
1924 retVal = yaffs_GetNumberOfFreeChunks(dev);
1925 retVal *= dev->nDataBytesPerChunk;
1928 yaffsfs_SetError(-EINVAL);
1934 loff_t yaffs_totalspace(const YCHAR *path)
1937 yaffs_Device *dev=NULL;
1941 dev = yaffsfs_FindDevice(path,&dummy);
1942 if(dev && dev->isMounted){
1943 retVal = (dev->param.endBlock - dev->param.startBlock + 1) - dev->param.nReservedBlocks;
1944 retVal *= dev->param.nChunksPerBlock;
1945 retVal *= dev->nDataBytesPerChunk;
1948 yaffsfs_SetError(-EINVAL);
1954 int yaffs_inodecount(const YCHAR *path)
1957 yaffs_Device *dev=NULL;
1961 dev = yaffsfs_FindDevice(path,&dummy);
1962 if(dev && dev->isMounted) {
1963 int nObjects = dev->nObjects;
1964 if(nObjects > dev->nHardLinks)
1965 retVal = nObjects - dev->nHardLinks;
1969 yaffsfs_SetError(-EINVAL);
1976 void yaffs_AddDevice(yaffs_Device *dev)
1979 dev->param.removeObjectCallback = yaffsfs_RemoveObjectCallback;
1981 if(!dev->devList.next)
1982 YINIT_LIST_HEAD(&dev->devList);
1984 ylist_add(&dev->devList,&yaffsfs_deviceList);
1987 void yaffs_RemoveDevice(yaffs_Device *dev)
1989 ylist_del_init(&dev->devList);
1995 /* Directory search stuff. */
1998 * Directory search context
2000 * NB this is an opaque structure.
2007 yaffs_dirent de; /* directory entry being used by this dsc */
2008 YCHAR name[NAME_MAX+1]; /* name of directory being searched */
2009 yaffs_Object *dirObj; /* ptr to directory being searched */
2010 yaffs_Object *nextReturn; /* obj to be returned by next readddir */
2012 struct ylist_head others;
2013 } yaffsfs_DirectorySearchContext;
2017 static struct ylist_head search_contexts;
2020 static void yaffsfs_SetDirRewound(yaffsfs_DirectorySearchContext *dsc)
2024 dsc->dirObj->variantType == YAFFS_OBJECT_TYPE_DIRECTORY){
2028 if( ylist_empty(&dsc->dirObj->variant.directoryVariant.children))
2029 dsc->nextReturn = NULL;
2031 dsc->nextReturn = ylist_entry(dsc->dirObj->variant.directoryVariant.children.next,
2032 yaffs_Object,siblings);
2034 /* Hey someone isn't playing nice! */
2038 static void yaffsfs_DirAdvance(yaffsfs_DirectorySearchContext *dsc)
2042 dsc->dirObj->variantType == YAFFS_OBJECT_TYPE_DIRECTORY){
2044 if( dsc->nextReturn == NULL ||
2045 ylist_empty(&dsc->dirObj->variant.directoryVariant.children))
2046 dsc->nextReturn = NULL;
2048 struct ylist_head *next = dsc->nextReturn->siblings.next;
2050 if( next == &dsc->dirObj->variant.directoryVariant.children)
2051 dsc->nextReturn = NULL; /* end of list */
2053 dsc->nextReturn = ylist_entry(next,yaffs_Object,siblings);
2056 /* Hey someone isn't playing nice! */
2060 static void yaffsfs_RemoveObjectCallback(yaffs_Object *obj)
2063 struct ylist_head *i;
2064 yaffsfs_DirectorySearchContext *dsc;
2066 /* if search contexts not initilised then skip */
2067 if(!search_contexts.next)
2070 /* Iterate through the directory search contexts.
2071 * If any are the one being removed, then advance the dsc to
2072 * the next one to prevent a hanging ptr.
2074 ylist_for_each(i, &search_contexts) {
2076 dsc = ylist_entry(i, yaffsfs_DirectorySearchContext,others);
2077 if(dsc->nextReturn == obj)
2078 yaffsfs_DirAdvance(dsc);
2084 yaffs_DIR *yaffs_opendir(const YCHAR *dirname)
2086 yaffs_DIR *dir = NULL;
2087 yaffs_Object *obj = NULL;
2088 yaffsfs_DirectorySearchContext *dsc = NULL;
2092 obj = yaffsfs_FindObject(NULL,dirname,0);
2094 if(obj && obj->variantType == YAFFS_OBJECT_TYPE_DIRECTORY){
2096 dsc = YMALLOC(sizeof(yaffsfs_DirectorySearchContext));
2097 dir = (yaffs_DIR *)dsc;
2100 memset(dsc,0,sizeof(yaffsfs_DirectorySearchContext));
2101 dsc->magic = YAFFS_MAGIC;
2103 yaffs_strncpy(dsc->name,dirname,NAME_MAX);
2104 YINIT_LIST_HEAD(&dsc->others);
2106 if(!search_contexts.next)
2107 YINIT_LIST_HEAD(&search_contexts);
2109 ylist_add(&dsc->others,&search_contexts);
2110 yaffsfs_SetDirRewound(dsc);
2120 struct yaffs_dirent *yaffs_readdir(yaffs_DIR *dirp)
2122 yaffsfs_DirectorySearchContext *dsc = (yaffsfs_DirectorySearchContext *)dirp;
2123 struct yaffs_dirent *retVal = NULL;
2127 if(dsc && dsc->magic == YAFFS_MAGIC){
2128 yaffsfs_SetError(0);
2129 if(dsc->nextReturn){
2130 dsc->de.d_ino = yaffs_GetEquivalentObject(dsc->nextReturn)->objectId;
2131 dsc->de.d_dont_use = (unsigned)dsc->nextReturn;
2132 dsc->de.d_off = dsc->offset++;
2133 yaffs_GetObjectName(dsc->nextReturn,dsc->de.d_name,NAME_MAX);
2134 if(yaffs_strnlen(dsc->de.d_name,NAME_MAX+1) == 0)
2136 /* this should not happen! */
2137 yaffs_strcpy(dsc->de.d_name,_Y("zz"));
2139 dsc->de.d_reclen = sizeof(struct yaffs_dirent);
2141 yaffsfs_DirAdvance(dsc);
2145 yaffsfs_SetError(-EBADF);
2154 void yaffs_rewinddir(yaffs_DIR *dirp)
2156 yaffsfs_DirectorySearchContext *dsc = (yaffsfs_DirectorySearchContext *)dirp;
2160 yaffsfs_SetDirRewound(dsc);
2166 int yaffs_closedir(yaffs_DIR *dirp)
2168 yaffsfs_DirectorySearchContext *dsc = (yaffsfs_DirectorySearchContext *)dirp;
2172 ylist_del(&dsc->others); /* unhook from list */
2178 /* End of directory stuff */
2181 int yaffs_symlink(const YCHAR *oldpath, const YCHAR *newpath)
2183 yaffs_Object *parent = NULL;
2187 int mode = 0; /* ignore for now */
2190 parent = yaffsfs_FindDirectory(NULL,newpath,&name,0);
2191 if(parent && parent->myDev->readOnly)
2192 yaffsfs_SetError(-EINVAL);
2194 obj = yaffs_MknodSymLink(parent,name,mode,0,0,oldpath);
2198 yaffsfs_SetError(-ENOSPC); /* just assume no space for now */
2202 yaffsfs_SetError(-EINVAL);
2212 int yaffs_readlink(const YCHAR *path, YCHAR *buf, int bufsiz)
2214 yaffs_Object *obj = NULL;
2220 obj = yaffsfs_FindObject(NULL,path,0);
2223 yaffsfs_SetError(-ENOENT);
2225 } else if(obj->variantType != YAFFS_OBJECT_TYPE_SYMLINK) {
2226 yaffsfs_SetError(-EINVAL);
2229 YCHAR *alias = obj->variant.symLinkVariant.alias;
2230 memset(buf,0,bufsiz);
2231 yaffs_strncpy(buf,alias,bufsiz - 1);
2238 int yaffs_link(const YCHAR *oldpath, const YCHAR *newpath)
2240 /* Creates a link called newpath to existing oldpath */
2241 yaffs_Object *obj = NULL;
2242 yaffs_Object *target = NULL;
2244 int newNameLength = 0;
2249 obj = yaffsfs_FindObject(NULL,oldpath,0);
2250 target = yaffsfs_FindObject(NULL,newpath,0);
2253 yaffsfs_SetError(-ENOENT);
2255 } else if(obj->myDev->readOnly){
2256 yaffsfs_SetError(-EINVAL);
2259 yaffsfs_SetError(-EEXIST);
2262 yaffs_Object *newdir = NULL;
2263 yaffs_Object *link = NULL;
2267 newdir = yaffsfs_FindDirectory(NULL,newpath,&newname,0);
2270 yaffsfs_SetError(-ENOTDIR);
2272 }else if(newdir->myDev != obj->myDev){
2273 yaffsfs_SetError(-EXDEV);
2277 newNameLength = yaffs_strnlen(newname,YAFFS_MAX_NAME_LENGTH+1);
2279 if(newNameLength == 0){
2280 yaffsfs_SetError(-ENOENT);
2282 } else if (newNameLength > YAFFS_MAX_NAME_LENGTH){
2283 yaffsfs_SetError(-ENAMETOOLONG);
2288 link = yaffs_Link(newdir,newname,obj);
2292 yaffsfs_SetError(-ENOSPC);
2303 int yaffs_mknod(const YCHAR *pathname, mode_t mode, dev_t dev)
2312 * Returns number of handles attached to the object
2314 int yaffs_n_handles(const YCHAR *path)
2318 obj = yaffsfs_FindObject(NULL,path,0);
2320 obj = yaffs_GetEquivalentObject(obj);
2322 return yaffsfs_CountHandles(obj);
2325 int yaffs_DumpDevStruct(const YCHAR *path)
2330 yaffs_Object *obj = yaffsfs_FindRoot(path,&rest);
2333 yaffs_Device *dev = obj->myDev;
2336 "nPageWrites.......... %d\n"
2337 "nPageReads........... %d\n"
2338 "nBlockErasures....... %d\n"
2339 "nGCCopies............ %d\n"
2340 "garbageCollections... %d\n"
2341 "passiveGarbageColl'ns %d\n"
2345 dev->nBlockErasures,
2347 dev->garbageCollections,
2348 dev->passiveGarbageCollections