1/*
2 * YAFFS: Yet another FFS. A NAND-flash specific file system.
3 * yaffsfs.c  The interface functions for using YAFFS via a "direct" interface.
4 *
5 * Copyright (C) 2002 Aleph One Ltd.
6 *
7 * Created by Charles Manning <charles@aleph1.co.uk>
8 *
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.
12 *
13 */
14
15#include "yaffsfs.h"
16#include "yaffs_guts.h"
17#include "yaffscfg.h"
18#include <string.h> // for memset
19#include "yportenv.h"
20
21#define YAFFSFS_MAX_SYMLINK_DEREFERENCES 5
22
23#ifndef NULL
24#define NULL ((void *)0)
25#endif
26
27
28const char *yaffsfs_c_version="$Id: yaffsfs.c,v 1.13 2006/10/03 10:13:03 charles Exp $";
29
30// configurationList is the list of devices that are supported
31static yaffsfs_DeviceConfiguration *yaffsfs_configurationList;
32
33
34/* Some forward references */
35static yaffs_Object *yaffsfs_FindObject(yaffs_Object *relativeDirectory, const char *path, int symDepth);
36static void yaffsfs_RemoveObjectCallback(yaffs_Object *obj);
37
38
39// Handle management.
40//
41
42typedef struct
43{
44	__u8  inUse:1;		// this handle is in use
45	__u8  readOnly:1;	// this handle is read only
46	__u8  append:1;		// append only
47	__u8  exclusive:1;	// exclusive
48	__u32 position;		// current position in file
49	yaffs_Object *obj;	// the object
50}yaffsfs_Handle;
51
52
53static yaffsfs_Handle yaffsfs_handle[YAFFSFS_N_HANDLES];
54
55// yaffsfs_InitHandle
56/// Inilitalise handles on start-up.
57//
58static int yaffsfs_InitHandles(void)
59{
60	int i;
61	for(i = 0; i < YAFFSFS_N_HANDLES; i++)
62	{
63		yaffsfs_handle[i].inUse = 0;
64		yaffsfs_handle[i].obj = NULL;
65	}
66	return 0;
67}
68
69yaffsfs_Handle *yaffsfs_GetHandlePointer(int h)
70{
71	if(h < 0 || h >= YAFFSFS_N_HANDLES)
72	{
73		return NULL;
74	}
75
76	return &yaffsfs_handle[h];
77}
78
79yaffs_Object *yaffsfs_GetHandleObject(int handle)
80{
81	yaffsfs_Handle *h = yaffsfs_GetHandlePointer(handle);
82
83	if(h && h->inUse)
84	{
85		return h->obj;
86	}
87
88	return NULL;
89}
90
91
92//yaffsfs_GetHandle
93// Grab a handle (when opening a file)
94//
95
96static int yaffsfs_GetHandle(void)
97{
98	int i;
99	yaffsfs_Handle *h;
100
101	for(i = 0; i < YAFFSFS_N_HANDLES; i++)
102	{
103		h = yaffsfs_GetHandlePointer(i);
104		if(!h)
105		{
106			// todo bug: should never happen
107		}
108		if(!h->inUse)
109		{
110			memset(h,0,sizeof(yaffsfs_Handle));
111			h->inUse=1;
112			return i;
113		}
114	}
115	return -1;
116}
117
118// yaffs_PutHandle
119// Let go of a handle (when closing a file)
120//
121static int yaffsfs_PutHandle(int handle)
122{
123	yaffsfs_Handle *h = yaffsfs_GetHandlePointer(handle);
124
125	if(h)
126	{
127		h->inUse = 0;
128		h->obj = NULL;
129	}
130	return 0;
131}
132
133
134
135// Stuff to search for a directory from a path
136
137
138int yaffsfs_Match(char a, char b)
139{
140	// case sensitive
141	return (a == b);
142}
143
144// yaffsfs_FindDevice
145// yaffsfs_FindRoot
146// Scan the configuration list to find the root.
147// Curveballs: Should match paths that end in '/' too
148// Curveball2 Might have "/x/ and "/x/y". Need to return the longest match
149static yaffs_Device *yaffsfs_FindDevice(const char *path, char **restOfPath)
150{
151	yaffsfs_DeviceConfiguration *cfg = yaffsfs_configurationList;
152	const char *leftOver;
153	const char *p;
154	yaffs_Device *retval = NULL;
155	int thisMatchLength;
156	int longestMatch = -1;
157
158	// Check all configs, choose the one that:
159	// 1) Actually matches a prefix (ie /a amd /abc will not match
160	// 2) Matches the longest.
161	while(cfg && cfg->prefix && cfg->dev)
162	{
163		leftOver = path;
164		p = cfg->prefix;
165		thisMatchLength = 0;
166
167		while(*p &&  //unmatched part of prefix
168		      strcmp(p,"/") && // the rest of the prefix is not / (to catch / at end)
169		      *leftOver &&
170		      yaffsfs_Match(*p,*leftOver))
171		{
172			p++;
173			leftOver++;
174			thisMatchLength++;
175		}
176		if((!*p || strcmp(p,"/") == 0) &&      // end of prefix
177		   (!*leftOver || *leftOver == '/') && // no more in this path name part
178		   (thisMatchLength > longestMatch))
179		{
180			// Matched prefix
181			*restOfPath = (char *)leftOver;
182			retval = cfg->dev;
183			longestMatch = thisMatchLength;
184		}
185		cfg++;
186	}
187	return retval;
188}
189
190static yaffs_Object *yaffsfs_FindRoot(const char *path, char **restOfPath)
191{
192
193	yaffs_Device *dev;
194
195	dev= yaffsfs_FindDevice(path,restOfPath);
196	if(dev && dev->isMounted)
197	{
198		return dev->rootDir;
199	}
200	return NULL;
201}
202
203static yaffs_Object *yaffsfs_FollowLink(yaffs_Object *obj,int symDepth)
204{
205
206	while(obj && obj->variantType == YAFFS_OBJECT_TYPE_SYMLINK)
207	{
208		char *alias = obj->variant.symLinkVariant.alias;
209
210		if(*alias == '/')
211		{
212			// Starts with a /, need to scan from root up
213			obj = yaffsfs_FindObject(NULL,alias,symDepth++);
214		}
215		else
216		{
217			// Relative to here, so use the parent of the symlink as a start
218			obj = yaffsfs_FindObject(obj->parent,alias,symDepth++);
219		}
220	}
221	return obj;
222}
223
224
225// yaffsfs_FindDirectory
226// Parse a path to determine the directory and the name within the directory.
227//
228// eg. "/data/xx/ff" --> puts name="ff" and returns the directory "/data/xx"
229static yaffs_Object *yaffsfs_DoFindDirectory(yaffs_Object *startDir,const char *path,char **name,int symDepth)
230{
231	yaffs_Object *dir;
232	char *restOfPath;
233	char str[YAFFS_MAX_NAME_LENGTH+1];
234	int i;
235
236	if(symDepth > YAFFSFS_MAX_SYMLINK_DEREFERENCES)
237	{
238		return NULL;
239	}
240
241	if(startDir)
242	{
243		dir = startDir;
244		restOfPath = (char *)path;
245	}
246	else
247	{
248		dir = yaffsfs_FindRoot(path,&restOfPath);
249	}
250
251	while(dir)
252	{
253		// parse off /.
254		// curve ball: also throw away surplus '/'
255		// eg. "/ram/x////ff" gets treated the same as "/ram/x/ff"
256		while(*restOfPath == '/')
257		{
258			restOfPath++; // get rid of '/'
259		}
260
261		*name = restOfPath;
262		i = 0;
263
264		while(*restOfPath && *restOfPath != '/')
265		{
266			if (i < YAFFS_MAX_NAME_LENGTH)
267			{
268				str[i] = *restOfPath;
269				str[i+1] = '\0';
270				i++;
271			}
272			restOfPath++;
273		}
274
275		if(!*restOfPath)
276		{
277			// got to the end of the string
278			return dir;
279		}
280		else
281		{
282			if(strcmp(str,".") == 0)
283			{
284				// Do nothing
285			}
286			else if(strcmp(str,"..") == 0)
287			{
288				dir = dir->parent;
289			}
290			else
291			{
292				dir = yaffs_FindObjectByName(dir,str);
293
294				while(dir && dir->variantType == YAFFS_OBJECT_TYPE_SYMLINK)
295				{
296
297					dir = yaffsfs_FollowLink(dir,symDepth);
298
299				}
300
301				if(dir && dir->variantType != YAFFS_OBJECT_TYPE_DIRECTORY)
302				{
303					dir = NULL;
304				}
305			}
306		}
307	}
308	// directory did not exist.
309	return NULL;
310}
311
312static yaffs_Object *yaffsfs_FindDirectory(yaffs_Object *relativeDirectory,const char *path,char **name,int symDepth)
313{
314	return yaffsfs_DoFindDirectory(relativeDirectory,path,name,symDepth);
315}
316
317// yaffsfs_FindObject turns a path for an existing object into the object
318//
319static yaffs_Object *yaffsfs_FindObject(yaffs_Object *relativeDirectory, const char *path,int symDepth)
320{
321	yaffs_Object *dir;
322	char *name;
323
324	dir = yaffsfs_FindDirectory(relativeDirectory,path,&name,symDepth);
325
326	if(dir && *name)
327	{
328		return yaffs_FindObjectByName(dir,name);
329	}
330
331	return dir;
332}
333
334
335
336int yaffs_open(const char *path, int oflag, int mode)
337{
338	yaffs_Object *obj = NULL;
339	yaffs_Object *dir = NULL;
340	char *name;
341	int handle = -1;
342	yaffsfs_Handle *h = NULL;
343	int alreadyOpen = 0;
344	int alreadyExclusive = 0;
345	int openDenied = 0;
346	int symDepth = 0;
347	int errorReported = 0;
348
349	int i;
350
351
352	// todo sanity check oflag (eg. can't have O_TRUNC without WRONLY or RDWR
353
354
355	yaffsfs_Lock();
356
357	handle = yaffsfs_GetHandle();
358
359	if(handle >= 0)
360	{
361
362		h = yaffsfs_GetHandlePointer(handle);
363
364
365		// try to find the exisiting object
366		obj = yaffsfs_FindObject(NULL,path,0);
367
368		if(obj && obj->variantType == YAFFS_OBJECT_TYPE_SYMLINK)
369		{
370
371			obj = yaffsfs_FollowLink(obj,symDepth++);
372		}
373
374		if(obj)
375		{
376			// Check if the object is already in use
377			alreadyOpen = alreadyExclusive = 0;
378
379			for(i = 0; i <= YAFFSFS_N_HANDLES; i++)
380			{
381
382				if(i != handle &&
383				   yaffsfs_handle[i].inUse &&
384				    obj == yaffsfs_handle[i].obj)
385				 {
386				 	alreadyOpen = 1;
387					if(yaffsfs_handle[i].exclusive)
388					{
389						alreadyExclusive = 1;
390					}
391				 }
392			}
393
394			if(((oflag & O_EXCL) && alreadyOpen) || alreadyExclusive)
395			{
396				openDenied = 1;
397			}
398
399			// Open should fail if O_CREAT and O_EXCL are specified
400			if((oflag & O_EXCL) && (oflag & O_CREAT))
401			{
402				openDenied = 1;
403				yaffsfs_SetError(-EEXIST);
404				errorReported = 1;
405			}
406
407			// Check file permissions
408			if( (oflag & (O_RDWR | O_WRONLY)) == 0 &&     // ie O_RDONLY
409			   !(obj->yst_mode & S_IREAD))
410			{
411				openDenied = 1;
412			}
413
414			if( (oflag & O_RDWR) &&
415			   !(obj->yst_mode & S_IREAD))
416			{
417				openDenied = 1;
418			}
419
420			if( (oflag & (O_RDWR | O_WRONLY)) &&
421			   !(obj->yst_mode & S_IWRITE))
422			{
423				openDenied = 1;
424			}
425
426		}
427
428		else if((oflag & O_CREAT))
429		{
430			// Let's see if we can create this file
431			dir = yaffsfs_FindDirectory(NULL,path,&name,0);
432			if(dir)
433			{
434				obj = yaffs_MknodFile(dir,name,mode,0,0);
435			}
436			else
437			{
438				yaffsfs_SetError(-ENOTDIR);
439			}
440		}
441
442		if(obj && !openDenied)
443		{
444			h->obj = obj;
445			h->inUse = 1;
446	    	h->readOnly = (oflag & (O_WRONLY | O_RDWR)) ? 0 : 1;
447			h->append =  (oflag & O_APPEND) ? 1 : 0;
448			h->exclusive = (oflag & O_EXCL) ? 1 : 0;
449			h->position = 0;
450
451			obj->inUse++;
452			if((oflag & O_TRUNC) && !h->readOnly)
453			{
454				//todo truncate
455				yaffs_ResizeFile(obj,0);
456			}
457
458		}
459		else
460		{
461			yaffsfs_PutHandle(handle);
462			if(!errorReported)
463			{
464				yaffsfs_SetError(-EACCESS);
465				errorReported = 1;
466			}
467			handle = -1;
468		}
469
470	}
471
472	yaffsfs_Unlock();
473
474	return handle;
475}
476
477int yaffs_close(int fd)
478{
479	yaffsfs_Handle *h = NULL;
480	int retVal = 0;
481
482	yaffsfs_Lock();
483
484	h = yaffsfs_GetHandlePointer(fd);
485
486	if(h && h->inUse)
487	{
488		// clean up
489		yaffs_FlushFile(h->obj,1);
490		h->obj->inUse--;
491		if(h->obj->inUse <= 0 && h->obj->unlinked)
492		{
493			yaffs_DeleteFile(h->obj);
494		}
495		yaffsfs_PutHandle(fd);
496		retVal = 0;
497	}
498	else
499	{
500		// bad handle
501		yaffsfs_SetError(-EBADF);
502		retVal = -1;
503	}
504
505	yaffsfs_Unlock();
506
507	return retVal;
508}
509
510int yaffs_read(int fd, void *buf, unsigned int nbyte)
511{
512	yaffsfs_Handle *h = NULL;
513	yaffs_Object *obj = NULL;
514	int pos = 0;
515	int nRead = -1;
516	int maxRead;
517
518	yaffsfs_Lock();
519	h = yaffsfs_GetHandlePointer(fd);
520	obj = yaffsfs_GetHandleObject(fd);
521
522	if(!h || !obj)
523	{
524		// bad handle
525		yaffsfs_SetError(-EBADF);
526	}
527	else if( h && obj)
528	{
529		pos=  h->position;
530		if(yaffs_GetObjectFileLength(obj) > pos)
531		{
532			maxRead = yaffs_GetObjectFileLength(obj) - pos;
533		}
534		else
535		{
536			maxRead = 0;
537		}
538
539		if(nbyte > maxRead)
540		{
541			nbyte = maxRead;
542		}
543
544
545		if(nbyte > 0)
546		{
547			nRead = yaffs_ReadDataFromFile(obj,buf,pos,nbyte);
548			if(nRead >= 0)
549			{
550				h->position = pos + nRead;
551			}
552			else
553			{
554				//todo error
555			}
556		}
557		else
558		{
559			nRead = 0;
560		}
561
562	}
563
564	yaffsfs_Unlock();
565
566
567	return (nRead >= 0) ? nRead : -1;
568
569}
570
571int yaffs_write(int fd, const void *buf, unsigned int nbyte)
572{
573	yaffsfs_Handle *h = NULL;
574	yaffs_Object *obj = NULL;
575	int pos = 0;
576	int nWritten = -1;
577	int writeThrough = 0;
578
579	yaffsfs_Lock();
580	h = yaffsfs_GetHandlePointer(fd);
581	obj = yaffsfs_GetHandleObject(fd);
582
583	if(!h || !obj)
584	{
585		// bad handle
586		yaffsfs_SetError(-EBADF);
587	}
588	else if( h && obj && h->readOnly)
589	{
590		// todo error
591	}
592	else if( h && obj)
593	{
594		if(h->append)
595		{
596			pos =  yaffs_GetObjectFileLength(obj);
597		}
598		else
599		{
600			pos = h->position;
601		}
602
603		nWritten = yaffs_WriteDataToFile(obj,buf,pos,nbyte,writeThrough);
604
605		if(nWritten >= 0)
606		{
607			h->position = pos + nWritten;
608		}
609		else
610		{
611			//todo error
612		}
613
614	}
615
616	yaffsfs_Unlock();
617
618
619	return (nWritten >= 0) ? nWritten : -1;
620
621}
622
623int yaffs_truncate(int fd, off_t newSize)
624{
625	yaffsfs_Handle *h = NULL;
626	yaffs_Object *obj = NULL;
627	int result = 0;
628
629	yaffsfs_Lock();
630	h = yaffsfs_GetHandlePointer(fd);
631	obj = yaffsfs_GetHandleObject(fd);
632
633	if(!h || !obj)
634	{
635		// bad handle
636		yaffsfs_SetError(-EBADF);
637	}
638	else
639	{
640		// resize the file
641		result = yaffs_ResizeFile(obj,newSize);
642	}
643	yaffsfs_Unlock();
644
645
646	return (result) ? 0 : -1;
647
648}
649
650off_t yaffs_lseek(int fd, off_t offset, int whence)
651{
652	yaffsfs_Handle *h = NULL;
653	yaffs_Object *obj = NULL;
654	int pos = -1;
655	int fSize = -1;
656
657	yaffsfs_Lock();
658	h = yaffsfs_GetHandlePointer(fd);
659	obj = yaffsfs_GetHandleObject(fd);
660
661	if(!h || !obj)
662	{
663		// bad handle
664		yaffsfs_SetError(-EBADF);
665	}
666	else if(whence == SEEK_SET)
667	{
668		if(offset >= 0)
669		{
670			pos = offset;
671		}
672	}
673	else if(whence == SEEK_CUR)
674	{
675		if( (h->position + offset) >= 0)
676		{
677			pos = (h->position + offset);
678		}
679	}
680	else if(whence == SEEK_END)
681	{
682		fSize = yaffs_GetObjectFileLength(obj);
683		if(fSize >= 0 && (fSize + offset) >= 0)
684		{
685			pos = fSize + offset;
686		}
687	}
688
689	if(pos >= 0)
690	{
691		h->position = pos;
692	}
693	else
694	{
695		// todo error
696	}
697
698
699	yaffsfs_Unlock();
700
701	return pos;
702}
703
704
705int yaffsfs_DoUnlink(const char *path,int isDirectory)
706{
707	yaffs_Object *dir = NULL;
708	yaffs_Object *obj = NULL;
709	char *name;
710	int result = YAFFS_FAIL;
711
712	yaffsfs_Lock();
713
714	obj = yaffsfs_FindObject(NULL,path,0);
715	dir = yaffsfs_FindDirectory(NULL,path,&name,0);
716	if(!dir)
717	{
718		yaffsfs_SetError(-ENOTDIR);
719	}
720	else if(!obj)
721	{
722		yaffsfs_SetError(-ENOENT);
723	}
724	else if(!isDirectory && obj->variantType == YAFFS_OBJECT_TYPE_DIRECTORY)
725	{
726		yaffsfs_SetError(-EISDIR);
727	}
728	else if(isDirectory && obj->variantType != YAFFS_OBJECT_TYPE_DIRECTORY)
729	{
730		yaffsfs_SetError(-ENOTDIR);
731	}
732	else if(isDirectory && obj->variantType != YAFFS_OBJECT_TYPE_DIRECTORY)
733	{
734		yaffsfs_SetError(-ENOTDIR);
735	}
736	else
737	{
738		result = yaffs_Unlink(dir,name);
739
740		if(result == YAFFS_FAIL && isDirectory)
741		{
742			yaffsfs_SetError(-ENOTEMPTY);
743		}
744	}
745
746	yaffsfs_Unlock();
747
748	// todo error
749
750	return (result == YAFFS_FAIL) ? -1 : 0;
751}
752int yaffs_rmdir(const char *path)
753{
754	return yaffsfs_DoUnlink(path,1);
755}
756
757int yaffs_unlink(const char *path)
758{
759	return yaffsfs_DoUnlink(path,0);
760}
761
762int yaffs_rename(const char *oldPath, const char *newPath)
763{
764	yaffs_Object *olddir = NULL;
765	yaffs_Object *newdir = NULL;
766	yaffs_Object *obj = NULL;
767	char *oldname;
768	char *newname;
769	int result= YAFFS_FAIL;
770	int renameAllowed = 1;
771
772	yaffsfs_Lock();
773
774	olddir = yaffsfs_FindDirectory(NULL,oldPath,&oldname,0);
775	newdir = yaffsfs_FindDirectory(NULL,newPath,&newname,0);
776	obj = yaffsfs_FindObject(NULL,oldPath,0);
777
778	if(!olddir || !newdir || !obj)
779	{
780		// bad file
781		yaffsfs_SetError(-EBADF);
782		renameAllowed = 0;
783	}
784	else if(olddir->myDev != newdir->myDev)
785	{
786		// oops must be on same device
787		// todo error
788		yaffsfs_SetError(-EXDEV);
789		renameAllowed = 0;
790	}
791	else if(obj && obj->variantType == YAFFS_OBJECT_TYPE_DIRECTORY)
792	{
793		// It is a directory, check that it is not being renamed to
794		// being its own decendent.
795		// Do this by tracing from the new directory back to the root, checking for obj
796
797		yaffs_Object *xx = newdir;
798
799		while( renameAllowed && xx)
800		{
801			if(xx == obj)
802			{
803				renameAllowed = 0;
804			}
805			xx = xx->parent;
806		}
807		if(!renameAllowed) yaffsfs_SetError(-EACCESS);
808	}
809
810	if(renameAllowed)
811	{
812		result = yaffs_RenameObject(olddir,oldname,newdir,newname);
813	}
814
815	yaffsfs_Unlock();
816
817	return (result == YAFFS_FAIL) ? -1 : 0;
818}
819
820
821static int yaffsfs_DoStat(yaffs_Object *obj,struct yaffs_stat *buf)
822{
823	int retVal = -1;
824
825	if(obj)
826	{
827		obj = yaffs_GetEquivalentObject(obj);
828	}
829
830	if(obj && buf)
831	{
832    	buf->st_dev = (int)obj->myDev->genericDevice;
833    	buf->st_ino = obj->objectId;
834    	buf->st_mode = obj->yst_mode & ~S_IFMT; // clear out file type bits
835
836		if(obj->variantType == YAFFS_OBJECT_TYPE_DIRECTORY)
837		{
838			buf->st_mode |= S_IFDIR;
839		}
840		else if(obj->variantType == YAFFS_OBJECT_TYPE_SYMLINK)
841		{
842			buf->st_mode |= S_IFLNK;
843		}
844		else if(obj->variantType == YAFFS_OBJECT_TYPE_FILE)
845		{
846			buf->st_mode |= S_IFREG;
847		}
848
849    	buf->st_nlink = yaffs_GetObjectLinkCount(obj);
850    	buf->st_uid = 0;
851    	buf->st_gid = 0;;
852    	buf->st_rdev = obj->yst_rdev;
853    	buf->st_size = yaffs_GetObjectFileLength(obj);
854		buf->st_blksize = obj->myDev->nDataBytesPerChunk;
855    	buf->st_blocks = (buf->st_size + buf->st_blksize -1)/buf->st_blksize;
856    	buf->yst_atime = obj->yst_atime;
857    	buf->yst_ctime = obj->yst_ctime;
858    	buf->yst_mtime = obj->yst_mtime;
859		retVal = 0;
860	}
861	return retVal;
862}
863
864static int yaffsfs_DoStatOrLStat(const char *path, struct yaffs_stat *buf,int doLStat)
865{
866	yaffs_Object *obj;
867
868	int retVal = -1;
869
870	yaffsfs_Lock();
871	obj = yaffsfs_FindObject(NULL,path,0);
872
873	if(!doLStat && obj)
874	{
875		obj = yaffsfs_FollowLink(obj,0);
876	}
877
878	if(obj)
879	{
880		retVal = yaffsfs_DoStat(obj,buf);
881	}
882	else
883	{
884		// todo error not found
885		yaffsfs_SetError(-ENOENT);
886	}
887
888	yaffsfs_Unlock();
889
890	return retVal;
891
892}
893
894int yaffs_stat(const char *path, struct yaffs_stat *buf)
895{
896	return yaffsfs_DoStatOrLStat(path,buf,0);
897}
898
899int yaffs_lstat(const char *path, struct yaffs_stat *buf)
900{
901	return yaffsfs_DoStatOrLStat(path,buf,1);
902}
903
904int yaffs_fstat(int fd, struct yaffs_stat *buf)
905{
906	yaffs_Object *obj;
907
908	int retVal = -1;
909
910	yaffsfs_Lock();
911	obj = yaffsfs_GetHandleObject(fd);
912
913	if(obj)
914	{
915		retVal = yaffsfs_DoStat(obj,buf);
916	}
917	else
918	{
919		// bad handle
920		yaffsfs_SetError(-EBADF);
921	}
922
923	yaffsfs_Unlock();
924
925	return retVal;
926}
927
928static int yaffsfs_DoChMod(yaffs_Object *obj,mode_t mode)
929{
930	int result;
931
932	if(obj)
933	{
934		obj = yaffs_GetEquivalentObject(obj);
935	}
936
937	if(obj)
938	{
939		obj->yst_mode = mode;
940		obj->dirty = 1;
941		result = yaffs_FlushFile(obj,0);
942	}
943
944	return result == YAFFS_OK ? 0 : -1;
945}
946
947
948int yaffs_chmod(const char *path, mode_t mode)
949{
950	yaffs_Object *obj;
951
952	int retVal = -1;
953
954	yaffsfs_Lock();
955	obj = yaffsfs_FindObject(NULL,path,0);
956
957	if(obj)
958	{
959		retVal = yaffsfs_DoChMod(obj,mode);
960	}
961	else
962	{
963		// todo error not found
964		yaffsfs_SetError(-ENOENT);
965	}
966
967	yaffsfs_Unlock();
968
969	return retVal;
970
971}
972
973
974int yaffs_fchmod(int fd, mode_t mode)
975{
976	yaffs_Object *obj;
977
978	int retVal = -1;
979
980	yaffsfs_Lock();
981	obj = yaffsfs_GetHandleObject(fd);
982
983	if(obj)
984	{
985		retVal = yaffsfs_DoChMod(obj,mode);
986	}
987	else
988	{
989		// bad handle
990		yaffsfs_SetError(-EBADF);
991	}
992
993	yaffsfs_Unlock();
994
995	return retVal;
996}
997
998
999int yaffs_mkdir(const char *path, mode_t mode)
1000{
1001	yaffs_Object *parent = NULL;
1002	yaffs_Object *dir = NULL;
1003	char *name;
1004	int retVal= -1;
1005
1006	yaffsfs_Lock();
1007	parent = yaffsfs_FindDirectory(NULL,path,&name,0);
1008	if(parent)
1009		dir = yaffs_MknodDirectory(parent,name,mode,0,0);
1010	if(dir)
1011	{
1012		retVal = 0;
1013	}
1014	else
1015	{
1016		yaffsfs_SetError(-ENOSPC); // just assume no space for now
1017		retVal = -1;
1018	}
1019
1020	yaffsfs_Unlock();
1021
1022	return retVal;
1023}
1024
1025int yaffs_mount(const char *path)
1026{
1027	int retVal=-1;
1028	int result=YAFFS_FAIL;
1029	yaffs_Device *dev=NULL;
1030	char *dummy;
1031
1032	T(YAFFS_TRACE_ALWAYS,("yaffs: Mounting %s\n",path));
1033
1034	yaffsfs_Lock();
1035	dev = yaffsfs_FindDevice(path,&dummy);
1036	if(dev)
1037	{
1038		if(!dev->isMounted)
1039		{
1040			result = yaffs_GutsInitialise(dev);
1041			if(result == YAFFS_FAIL)
1042			{
1043				// todo error - mount failed
1044				yaffsfs_SetError(-ENOMEM);
1045			}
1046			retVal = result ? 0 : -1;
1047
1048		}
1049		else
1050		{
1051			//todo error - already mounted.
1052			yaffsfs_SetError(-EBUSY);
1053		}
1054	}
1055	else
1056	{
1057		// todo error - no device
1058		yaffsfs_SetError(-ENODEV);
1059	}
1060	yaffsfs_Unlock();
1061	return retVal;
1062
1063}
1064
1065int yaffs_unmount(const char *path)
1066{
1067	int retVal=-1;
1068	yaffs_Device *dev=NULL;
1069	char *dummy;
1070
1071	yaffsfs_Lock();
1072	dev = yaffsfs_FindDevice(path,&dummy);
1073	if(dev)
1074	{
1075		if(dev->isMounted)
1076		{
1077			int i;
1078			int inUse;
1079
1080			yaffs_FlushEntireDeviceCache(dev);
1081			yaffs_CheckpointSave(dev);
1082
1083			for(i = inUse = 0; i < YAFFSFS_N_HANDLES && !inUse; i++)
1084			{
1085				if(yaffsfs_handle[i].inUse && yaffsfs_handle[i].obj->myDev == dev)
1086				{
1087					inUse = 1; // the device is in use, can't unmount
1088				}
1089			}
1090
1091			if(!inUse)
1092			{
1093				yaffs_Deinitialise(dev);
1094
1095				retVal = 0;
1096			}
1097			else
1098			{
1099				// todo error can't unmount as files are open
1100				yaffsfs_SetError(-EBUSY);
1101			}
1102
1103		}
1104		else
1105		{
1106			//todo error - not mounted.
1107			yaffsfs_SetError(-EINVAL);
1108
1109		}
1110	}
1111	else
1112	{
1113		// todo error - no device
1114		yaffsfs_SetError(-ENODEV);
1115	}
1116	yaffsfs_Unlock();
1117	return retVal;
1118
1119}
1120
1121loff_t yaffs_freespace(const char *path)
1122{
1123	loff_t retVal=-1;
1124	yaffs_Device *dev=NULL;
1125	char *dummy;
1126
1127	yaffsfs_Lock();
1128	dev = yaffsfs_FindDevice(path,&dummy);
1129	if(dev  && dev->isMounted)
1130	{
1131		retVal = yaffs_GetNumberOfFreeChunks(dev);
1132		retVal *= dev->nDataBytesPerChunk;
1133
1134	}
1135	else
1136	{
1137		yaffsfs_SetError(-EINVAL);
1138	}
1139
1140	yaffsfs_Unlock();
1141	return retVal;
1142}
1143
1144
1145
1146void yaffs_initialise(yaffsfs_DeviceConfiguration *cfgList)
1147{
1148
1149	yaffsfs_DeviceConfiguration *cfg;
1150
1151	yaffsfs_configurationList = cfgList;
1152
1153	yaffsfs_InitHandles();
1154
1155	cfg = yaffsfs_configurationList;
1156
1157	while(cfg && cfg->prefix && cfg->dev)
1158	{
1159		cfg->dev->isMounted = 0;
1160		cfg->dev->removeObjectCallback = yaffsfs_RemoveObjectCallback;
1161		cfg++;
1162	}
1163
1164
1165}
1166
1167
1168//
1169// Directory search stuff.
1170
1171//
1172// Directory search context
1173//
1174// NB this is an opaque structure.
1175
1176
1177typedef struct
1178{
1179	__u32 magic;
1180	yaffs_dirent de;		/* directory entry being used by this dsc */
1181	char name[NAME_MAX+1];		/* name of directory being searched */
1182	yaffs_Object *dirObj;		/* ptr to directory being searched */
1183	yaffs_Object *nextReturn;	/* obj to be returned by next readddir */
1184	int offset;
1185	struct list_head others;
1186} yaffsfs_DirectorySearchContext;
1187
1188
1189
1190static struct list_head search_contexts;
1191
1192
1193static void yaffsfs_SetDirRewound(yaffsfs_DirectorySearchContext *dsc)
1194{
1195	if(dsc &&
1196	   dsc->dirObj &&
1197	   dsc->dirObj->variantType == YAFFS_OBJECT_TYPE_DIRECTORY){
1198
1199	   dsc->offset = 0;
1200
1201	   if( list_empty(&dsc->dirObj->variant.directoryVariant.children)){
1202	   	dsc->nextReturn = NULL;
1203	   } else {
1204	      	dsc->nextReturn = list_entry(dsc->dirObj->variant.directoryVariant.children.next,
1205						yaffs_Object,siblings);
1206	   }
1207	} else {
1208		/* Hey someone isn't playing nice! */
1209	}
1210}
1211
1212static void yaffsfs_DirAdvance(yaffsfs_DirectorySearchContext *dsc)
1213{
1214	if(dsc &&
1215	   dsc->dirObj &&
1216	   dsc->dirObj->variantType == YAFFS_OBJECT_TYPE_DIRECTORY){
1217
1218	   if( dsc->nextReturn == NULL ||
1219	       list_empty(&dsc->dirObj->variant.directoryVariant.children)){
1220	   	dsc->nextReturn = NULL;
1221	   } else {
1222		   struct list_head *next = dsc->nextReturn->siblings.next;
1223
1224		   if( next == &dsc->dirObj->variant.directoryVariant.children)
1225	   		dsc->nextReturn = NULL; /* end of list */
1226	   	   else
1227		   	dsc->nextReturn = list_entry(next,yaffs_Object,siblings);
1228	   }
1229	} else {
1230		/* Hey someone isn't playing nice! */
1231	}
1232}
1233
1234static void yaffsfs_RemoveObjectCallback(yaffs_Object *obj)
1235{
1236
1237	struct list_head *i;
1238	yaffsfs_DirectorySearchContext *dsc;
1239
1240	/* if search contexts not initilised then skip */
1241	if(!search_contexts.next)
1242		return;
1243
1244	/* Iteratethrough the directory search contexts.
1245	 * If any are the one being removed, then advance the dsc to
1246	 * the next one to prevent a hanging ptr.
1247	 */
1248	 list_for_each(i, &search_contexts) {
1249		if (i) {
1250			dsc = list_entry(i, yaffsfs_DirectorySearchContext,others);
1251			if(dsc->nextReturn == obj)
1252				yaffsfs_DirAdvance(dsc);
1253		}
1254	}
1255
1256}
1257
1258yaffs_DIR *yaffs_opendir(const char *dirname)
1259{
1260	yaffs_DIR *dir = NULL;
1261 	yaffs_Object *obj = NULL;
1262	yaffsfs_DirectorySearchContext *dsc = NULL;
1263
1264	yaffsfs_Lock();
1265
1266	obj = yaffsfs_FindObject(NULL,dirname,0);
1267
1268	if(obj && obj->variantType == YAFFS_OBJECT_TYPE_DIRECTORY)
1269	{
1270
1271		dsc = YMALLOC(sizeof(yaffsfs_DirectorySearchContext));
1272		dir = (yaffs_DIR *)dsc;
1273		if(dsc)
1274		{
1275			memset(dsc,0,sizeof(yaffsfs_DirectorySearchContext));
1276			dsc->magic = YAFFS_MAGIC;
1277			dsc->dirObj = obj;
1278			strncpy(dsc->name,dirname,NAME_MAX);
1279			INIT_LIST_HEAD(&dsc->others);
1280
1281			if(!search_contexts.next)
1282				INIT_LIST_HEAD(&search_contexts);
1283
1284			list_add(&dsc->others,&search_contexts);
1285			yaffsfs_SetDirRewound(dsc);		}
1286
1287	}
1288
1289	yaffsfs_Unlock();
1290
1291	return dir;
1292}
1293
1294struct yaffs_dirent *yaffs_readdir(yaffs_DIR *dirp)
1295{
1296	yaffsfs_DirectorySearchContext *dsc = (yaffsfs_DirectorySearchContext *)dirp;
1297	struct yaffs_dirent *retVal = NULL;
1298
1299	yaffsfs_Lock();
1300
1301	if(dsc && dsc->magic == YAFFS_MAGIC){
1302		yaffsfs_SetError(0);
1303		if(dsc->nextReturn){
1304			dsc->de.d_ino = yaffs_GetEquivalentObject(dsc->nextReturn)->objectId;
1305			dsc->de.d_off = dsc->offset++;
1306			yaffs_GetObjectName(dsc->nextReturn,dsc->de.d_name,NAME_MAX+1);
1307			dsc->de.d_reclen = sizeof(struct yaffs_dirent);
1308			retVal = &dsc->de;
1309			yaffsfs_DirAdvance(dsc);
1310		} else
1311			retVal = NULL;
1312	}
1313	else
1314	{
1315		yaffsfs_SetError(-EBADF);
1316	}
1317
1318	yaffsfs_Unlock();
1319
1320	return retVal;
1321
1322}
1323
1324
1325void yaffs_rewinddir(yaffs_DIR *dirp)
1326{
1327	yaffsfs_DirectorySearchContext *dsc = (yaffsfs_DirectorySearchContext *)dirp;
1328
1329	yaffsfs_Lock();
1330
1331	yaffsfs_SetDirRewound(dsc);
1332
1333	yaffsfs_Unlock();
1334}
1335
1336
1337int yaffs_closedir(yaffs_DIR *dirp)
1338{
1339	yaffsfs_DirectorySearchContext *dsc = (yaffsfs_DirectorySearchContext *)dirp;
1340
1341	yaffsfs_Lock();
1342	dsc->magic = 0;
1343	list_del(&dsc->others); /* unhook from list */
1344	YFREE(dsc);
1345	yaffsfs_Unlock();
1346	return 0;
1347}
1348
1349// end of directory stuff
1350
1351
1352int yaffs_symlink(const char *oldpath, const char *newpath)
1353{
1354	yaffs_Object *parent = NULL;
1355	yaffs_Object *obj;
1356	char *name;
1357	int retVal= -1;
1358	int mode = 0; // ignore for now
1359
1360	yaffsfs_Lock();
1361	parent = yaffsfs_FindDirectory(NULL,newpath,&name,0);
1362	obj = yaffs_MknodSymLink(parent,name,mode,0,0,oldpath);
1363	if(obj)
1364	{
1365		retVal = 0;
1366	}
1367	else
1368	{
1369		yaffsfs_SetError(-ENOSPC); // just assume no space for now
1370		retVal = -1;
1371	}
1372
1373	yaffsfs_Unlock();
1374
1375	return retVal;
1376
1377}
1378
1379int yaffs_readlink(const char *path, char *buf, int bufsiz)
1380{
1381	yaffs_Object *obj = NULL;
1382	int retVal;
1383
1384
1385	yaffsfs_Lock();
1386
1387	obj = yaffsfs_FindObject(NULL,path,0);
1388
1389	if(!obj)
1390	{
1391		yaffsfs_SetError(-ENOENT);
1392		retVal = -1;
1393	}
1394	else if(obj->variantType != YAFFS_OBJECT_TYPE_SYMLINK)
1395	{
1396		yaffsfs_SetError(-EINVAL);
1397		retVal = -1;
1398	}
1399	else
1400	{
1401		char *alias = obj->variant.symLinkVariant.alias;
1402		memset(buf,0,bufsiz);
1403		strncpy(buf,alias,bufsiz - 1);
1404		retVal = 0;
1405	}
1406	yaffsfs_Unlock();
1407	return retVal;
1408}
1409
1410int yaffs_link(const char *oldpath, const char *newpath)
1411{
1412	// Creates a link called newpath to existing oldpath
1413	yaffs_Object *obj = NULL;
1414	yaffs_Object *target = NULL;
1415	int retVal = 0;
1416
1417
1418	yaffsfs_Lock();
1419
1420	obj = yaffsfs_FindObject(NULL,oldpath,0);
1421	target = yaffsfs_FindObject(NULL,newpath,0);
1422
1423	if(!obj)
1424	{
1425		yaffsfs_SetError(-ENOENT);
1426		retVal = -1;
1427	}
1428	else if(target)
1429	{
1430		yaffsfs_SetError(-EEXIST);
1431		retVal = -1;
1432	}
1433	else
1434	{
1435		yaffs_Object *newdir = NULL;
1436		yaffs_Object *link = NULL;
1437
1438		char *newname;
1439
1440		newdir = yaffsfs_FindDirectory(NULL,newpath,&newname,0);
1441
1442		if(!newdir)
1443		{
1444			yaffsfs_SetError(-ENOTDIR);
1445			retVal = -1;
1446		}
1447		else if(newdir->myDev != obj->myDev)
1448		{
1449			yaffsfs_SetError(-EXDEV);
1450			retVal = -1;
1451		}
1452		if(newdir && strlen(newname) > 0)
1453		{
1454			link = yaffs_Link(newdir,newname,obj);
1455			if(link)
1456				retVal = 0;
1457			else
1458			{
1459				yaffsfs_SetError(-ENOSPC);
1460				retVal = -1;
1461			}
1462
1463		}
1464	}
1465	yaffsfs_Unlock();
1466
1467	return retVal;
1468}
1469
1470int yaffs_mknod(const char *pathname, mode_t mode, dev_t dev);
1471
1472int yaffs_DumpDevStruct(const char *path)
1473{
1474	char *rest;
1475
1476	yaffs_Object *obj = yaffsfs_FindRoot(path,&rest);
1477
1478	if(obj)
1479	{
1480		yaffs_Device *dev = obj->myDev;
1481
1482		printf("\n"
1483			   "nPageWrites.......... %d\n"
1484			   "nPageReads........... %d\n"
1485			   "nBlockErasures....... %d\n"
1486			   "nGCCopies............ %d\n"
1487			   "garbageCollections... %d\n"
1488			   "passiveGarbageColl'ns %d\n"
1489			   "\n",
1490				dev->nPageWrites,
1491				dev->nPageReads,
1492				dev->nBlockErasures,
1493				dev->nGCCopies,
1494				dev->garbageCollections,
1495				dev->passiveGarbageCollections
1496		);
1497
1498	}
1499	return 0;
1500}
1501
1502