xref: /nrf52832-nimble/rt-thread/components/dfs/filesystems/uffs/src/uffs/uffs_fs.c (revision 104654410c56c573564690304ae786df310c91fc)
1 /*
2   This file is part of UFFS, the Ultra-low-cost Flash File System.
3 
4   Copyright (C) 2005-2009 Ricky Zheng <[email protected]>
5 
6   UFFS is free software; you can redistribute it and/or modify it under
7   the GNU Library General Public License as published by the Free Software
8   Foundation; either version 2 of the License, or (at your option) any
9   later version.
10 
11   UFFS is distributed in the hope that it will be useful, but WITHOUT
12   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   or GNU Library General Public License, as applicable, for more details.
15 
16   You should have received a copy of the GNU General Public License
17   and GNU Library General Public License along with UFFS; if not, write
18   to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19   Boston, MA  02110-1301, USA.
20 
21   As a special exception, if other files instantiate templates or use
22   macros or inline functions from this file, or you compile this file
23   and link it with other works to produce a work based on this file,
24   this file does not by itself cause the resulting work to be covered
25   by the GNU General Public License. However the source code for this
26   file must still be made available in accordance with section (3) of
27   the GNU General Public License v2.
28 
29   This exception does not invalidate any other reasons why a work based
30   on this file might be covered by the GNU General Public License.
31 */
32 
33 /**
34  * \file uffs_fs.c
35  * \brief basic file operations
36  * \author Ricky Zheng, created 12th May, 2005
37  */
38 #include "uffs_config.h"
39 #include "uffs/uffs_fs.h"
40 #include "uffs/uffs_pool.h"
41 #include "uffs/uffs_ecc.h"
42 #include "uffs/uffs_badblock.h"
43 #include "uffs/uffs_os.h"
44 #include "uffs/uffs_mtb.h"
45 #include "uffs/uffs_utils.h"
46 #include <string.h>
47 #include <stdio.h>
48 
49 #define PFX "fs  : "
50 
51 #define GET_OBJ_NODE_SERIAL(obj) \
52 	( \
53 		(obj)->type == UFFS_TYPE_DIR ? \
54 			(obj)->node->u.dir.serial :	(obj)->node->u.file.serial \
55 	 )
56 
57 #define GET_OBJ_NODE_FATHER(obj) \
58 	( \
59 		(obj)->type == UFFS_TYPE_DIR ? \
60 			(obj)->node->u.dir.parent :	(obj)->node->u.file.parent \
61 	 )
62 
63 #define GET_SERIAL_FROM_OBJECT(obj) \
64 			((obj)->node ? GET_OBJ_NODE_SERIAL(obj) : obj->serial)
65 
66 #define GET_FATHER_FROM_OBJECT(obj) \
67 			((obj)->node ? GET_OBJ_NODE_FATHER(obj) : obj->parent)
68 
69 
70 #define GET_BLOCK_FROM_NODE(obj) \
71 	( \
72 		(obj)->type == UFFS_TYPE_DIR ? \
73 			(obj)->node->u.dir.block : (obj)->node->u.file.block \
74 	 )
75 
76 typedef enum {
77 	eDRY_RUN = 0,
78 	eREAL_RUN,
79 } RunOptionE;
80 
81 
82 static void do_ReleaseObjectResource(uffs_Object *obj);
83 static URET do_TruncateObject(uffs_Object *obj, u32 remain, RunOptionE run_opt);
84 
85 
86 static int _object_data[(sizeof(struct uffs_ObjectSt) * MAX_OBJECT_HANDLE) / sizeof(int)];
87 
88 static uffs_Pool _object_pool;
89 
90 
uffs_GetObjectPool(void)91 uffs_Pool * uffs_GetObjectPool(void)
92 {
93 	return &_object_pool;
94 }
95 
96 /**
97  * initialise object buffers, called by UFFS internal
98  */
uffs_InitObjectBuf(void)99 URET uffs_InitObjectBuf(void)
100 {
101 	return uffs_PoolInit(&_object_pool, _object_data, sizeof(_object_data),
102 			sizeof(uffs_Object), MAX_OBJECT_HANDLE);
103 }
104 
105 /**
106  * Release object buffers, called by UFFS internal
107  */
uffs_ReleaseObjectBuf(void)108 URET uffs_ReleaseObjectBuf(void)
109 {
110 	return uffs_PoolRelease(&_object_pool);
111 }
112 
113 /**
114  * Get free object handlers
115  */
uffs_GetFreeObjectHandlers(void)116 int uffs_GetFreeObjectHandlers(void)
117 {
118 	int count = 0;
119 
120 	uffs_GlobalFsLockLock();
121 	count = uffs_PoolGetFreeCount(&_object_pool);
122 	uffs_GlobalFsLockUnlock();
123 
124 	return count;
125 }
126 
127 /**
128  * Put all object which match dev
129  */
uffs_PutAllObjectBuf(uffs_Device * dev)130 int uffs_PutAllObjectBuf(uffs_Device *dev)
131 {
132 	int count = 0;
133 	uffs_Object * obj = NULL;
134 
135 	do {
136 		obj = (uffs_Object *) uffs_PoolFindNextAllocated(&_object_pool, (void *)obj);
137 		if (obj && obj->dev && obj->dev->dev_num == dev->dev_num) {
138 			uffs_PutObject(obj);
139 			count++;
140 		}
141 	} while (obj);
142 
143 	return count;
144 }
145 
146 /**
147  * alloc a new object structure
148  * \return the new object
149  */
uffs_GetObject(void)150 uffs_Object * uffs_GetObject(void)
151 {
152 	uffs_Object * obj;
153 
154 	obj = (uffs_Object *) uffs_PoolGet(&_object_pool);
155 	if (obj) {
156 		memset(obj, 0, sizeof(uffs_Object));
157 		obj->attr_loaded = U_FALSE;
158 		obj->open_succ = U_FALSE;
159 	}
160 
161 	return obj;
162 }
163 
164 /**
165  * re-initialize an object.
166  *
167  * \return U_SUCC or U_FAIL if the object is openned.
168  */
uffs_ReInitObject(uffs_Object * obj)169 URET uffs_ReInitObject(uffs_Object *obj)
170 {
171 	if (obj == NULL)
172 		return U_FAIL;
173 
174 	if (obj->open_succ == U_TRUE)
175 		return U_FAIL;	// can't re-init an openned object.
176 
177 	memset(obj, 0, sizeof(uffs_Object));
178 	obj->attr_loaded = U_FALSE;
179 	obj->open_succ = U_FALSE;
180 
181 	return U_SUCC;
182 }
183 
184 /**
185  * put the object struct back to system
186  */
uffs_PutObject(uffs_Object * obj)187 void uffs_PutObject(uffs_Object *obj)
188 {
189 	if (obj)
190 		uffs_PoolPut(&_object_pool, obj);
191 }
192 
193 /**
194  * \return the internal index num of object
195  */
uffs_GetObjectIndex(uffs_Object * obj)196 int uffs_GetObjectIndex(uffs_Object *obj)
197 {
198 	return uffs_PoolGetIndex(&_object_pool, obj);
199 }
200 
201 /**
202  * \return the object by the internal index
203  */
uffs_GetObjectByIndex(int idx)204 uffs_Object * uffs_GetObjectByIndex(int idx)
205 {
206 	return (uffs_Object *) uffs_PoolGetBufByIndex(&_object_pool, idx);
207 }
208 
209 #ifdef CONFIG_PER_DEVICE_LOCK
uffs_ObjectDevLock(uffs_Object * obj)210 static void uffs_ObjectDevLock(uffs_Object *obj)
211 {
212 	if (obj) {
213 		if (obj->dev) {
214 			uffs_DeviceLock(obj->dev);
215 			obj->dev_lock_count++;
216 		}
217 	}
218 }
219 
uffs_ObjectDevUnLock(uffs_Object * obj)220 static void uffs_ObjectDevUnLock(uffs_Object *obj)
221 {
222 	if (obj) {
223 		if (obj->dev) {
224 			obj->dev_lock_count--;
225 			uffs_DeviceUnLock(obj->dev);
226 		}
227 	}
228 }
229 #else
230 #define uffs_ObjectDevLock(obj) do { } while (0)
231 #define uffs_ObjectDevUnLock(obj) do { } while (0)
232 
233 #endif
234 
235 
236 
237 /**
238  * create a new object and open it if success
239  */
uffs_CreateObject(uffs_Object * obj,const char * fullname,int oflag)240 URET uffs_CreateObject(uffs_Object *obj, const char *fullname, int oflag)
241 {
242 	URET ret = U_FAIL;
243 
244 	oflag |= UO_CREATE;
245 
246 	if (uffs_ParseObject(obj, fullname) == U_SUCC)
247 		uffs_CreateObjectEx(obj, obj->dev, obj->parent,
248 								obj->name, obj->name_len, oflag);
249 
250 	if (obj->err == UENOERR) {
251 		ret = U_SUCC;
252 	}
253 	else {
254 		if (obj->dev) {
255 			uffs_PutDevice(obj->dev);
256 			obj->dev = NULL;
257 		}
258 		ret = U_FAIL;
259 	}
260 
261 	return ret;
262 }
263 
264 
265 
266 /**
267  * return the dir length from a path.
268  * for example, path = "abc/def/xyz", return 8 ("abc/def/")
269  */
GetDirLengthFromPath(const char * path,int path_len)270 static int GetDirLengthFromPath(const char *path, int path_len)
271 {
272 	const char *p = path;
273 
274 	if (path_len > 0) {
275 		if (path[path_len - 1] == '/')
276 			path_len--;		// skip the last '/'
277 
278 		p = path + path_len - 1;
279 		while (p > path && *p != '/')
280 			p--;
281 	}
282 
283 	return p - path;
284 }
285 
286 /**
287  * Create an object under the given dir.
288  *
289  * \param[in|out] obj to be created, obj is returned from uffs_GetObject()
290  * \param[in] dev uffs device
291  * \param[in] dir object parent dir serial NO.
292  * \param[in] name point to the object name
293  * \param[in] name_len object name length
294  * \param[in] oflag open flag. UO_DIR should be passed for an dir object.
295  *
296  * \return U_SUCC or U_FAIL (error code in obj->err).
297  */
uffs_CreateObjectEx(uffs_Object * obj,uffs_Device * dev,int dir,const char * name,int name_len,int oflag)298 URET uffs_CreateObjectEx(uffs_Object *obj, uffs_Device *dev,
299 						   int dir, const char *name, int name_len, int oflag)
300 {
301 	uffs_Buf *buf = NULL;
302 	uffs_FileInfo fi;
303 	TreeNode *node;
304 
305 	obj->dev = dev;
306 	obj->parent = dir;
307 	obj->type = (oflag & UO_DIR ? UFFS_TYPE_DIR : UFFS_TYPE_FILE);
308 	obj->name = name;
309 	obj->name_len = name_len;
310 
311 	if (obj->type == UFFS_TYPE_DIR) {
312 		if (name[obj->name_len - 1] == '/')		// get rid of ending '/' for dir
313 			obj->name_len--;
314 	}
315 	else {
316 		if (name[obj->name_len - 1] == '/') {	// file name can't end with '/'
317 			obj->err = UENOENT;
318 			goto ext;
319 		}
320 	}
321 
322 	if (obj->name_len == 0) {	// empty name ?
323 		obj->err = UENOENT;
324 		goto ext;
325 	}
326 
327 	obj->sum = uffs_MakeSum16(obj->name, obj->name_len);
328 
329 	uffs_ObjectDevLock(obj);
330 
331 	if (obj->type == UFFS_TYPE_DIR) {
332 		//find out whether have file with the same name
333 		node = uffs_TreeFindFileNodeByName(obj->dev, obj->name,
334 											obj->name_len, obj->sum,
335 											obj->parent);
336 		if (node != NULL) {
337 			obj->err = UEEXIST;	// we can't create a dir has the
338 								// same name with exist file.
339 			goto ext_1;
340 		}
341 		obj->node = uffs_TreeFindDirNodeByName(obj->dev, obj->name,
342 												obj->name_len, obj->sum,
343 												obj->parent);
344 		if (obj->node != NULL) {
345 			obj->err = UEEXIST; // we can't create a dir already exist.
346 			goto ext_1;
347 		}
348 	}
349 	else {
350 		//find out whether have dir with the same name
351 		node = uffs_TreeFindDirNodeByName(obj->dev, obj->name,
352 											obj->name_len, obj->sum,
353 											obj->parent);
354 		if (node != NULL) {
355 			obj->err = UEEXIST;
356 			goto ext_1;
357 		}
358 		obj->node = uffs_TreeFindFileNodeByName(obj->dev, obj->name,
359 												obj->name_len, obj->sum,
360 												obj->parent);
361 		if (obj->node) {
362 			/* file already exist, truncate it to zero length */
363 			obj->serial = GET_OBJ_NODE_SERIAL(obj);
364 			obj->open_succ = U_TRUE; // set open_succ to U_TRUE before
365 									 // call do_TruncateObject()
366 			if (do_TruncateObject(obj, 0, eDRY_RUN) == U_SUCC)
367 				do_TruncateObject(obj, 0, eREAL_RUN);
368 			goto ext_1;
369 		}
370 	}
371 
372 	/* dir|file does not exist, create a new one */
373 	obj->serial = uffs_FindFreeFsnSerial(obj->dev);
374 	if (obj->serial == INVALID_UFFS_SERIAL) {
375 		uffs_Perror(UFFS_MSG_SERIOUS, "No free serial num!");
376 		obj->err = UENOMEM;
377 		goto ext_1;
378 	}
379 
380 	if (obj->dev->tree.erased_count < obj->dev->cfg.reserved_free_blocks) {
381 		uffs_Perror(UFFS_MSG_NOISY,
382 					"insufficient block in create obj");
383 		obj->err = UENOMEM;
384 		goto ext_1;
385 	}
386 
387 	buf = uffs_BufNew(obj->dev, obj->type, obj->parent, obj->serial, 0);
388 	if (buf == NULL) {
389 		uffs_Perror(UFFS_MSG_SERIOUS,
390 						"Can't create new buffer when create obj!");
391 		goto ext_1;
392 	}
393 
394 	memset(&fi, 0, sizeof(uffs_FileInfo));
395 	fi.name_len = obj->name_len < sizeof(fi.name) ? obj->name_len : sizeof(fi.name) - 1;
396 	memcpy(fi.name, obj->name, fi.name_len);
397 	fi.name[fi.name_len] = '\0';
398 
399 	fi.access = 0;
400 	fi.attr |= FILE_ATTR_WRITE;
401 
402 	if (obj->type == UFFS_TYPE_DIR)
403 		fi.attr |= FILE_ATTR_DIR;
404 
405 	fi.create_time = fi.last_modify = uffs_GetCurDateTime();
406 
407 	uffs_BufWrite(obj->dev, buf, &fi, 0, sizeof(uffs_FileInfo));
408 	uffs_BufPut(obj->dev, buf);
409 
410 	// flush buffer immediately,
411 	// so that the new node will be inserted into the tree
412 	uffs_BufFlushGroup(obj->dev, obj->parent, obj->serial);
413 
414 	// update obj->node: after buf flushed,
415 	// the NEW node can be found in the tree
416 	if (obj->type == UFFS_TYPE_DIR)
417 		obj->node = uffs_TreeFindDirNode(obj->dev, obj->serial);
418 	else
419 		obj->node = uffs_TreeFindFileNode(obj->dev, obj->serial);
420 
421 	if (obj->node == NULL) {
422 		uffs_Perror(UFFS_MSG_NOISY, "Can't find the node in the tree ?");
423 		obj->err = UEIOERR;
424 		goto ext_1;
425 	}
426 
427 	if (obj->type == UFFS_TYPE_FILE)
428 		obj->node->u.file.len = 0;	//init the length to 0
429 
430 	if (HAVE_BADBLOCK(obj->dev))
431 		uffs_BadBlockRecover(obj->dev);
432 
433 	obj->open_succ = U_TRUE;
434 
435 ext_1:
436 	uffs_ObjectDevUnLock(obj);
437 ext:
438 	return (obj->err == UENOERR ? U_SUCC : U_FAIL);
439 }
440 
441 /**
442  * Open object under the given dir.
443  *
444  * \param[in|out] obj to be open, obj is returned from uffs_GetObject()
445  * \param[in] dev uffs device
446  * \param[in] dir object parent dir serial NO.
447  * \param[in] name point to the object name
448  * \param[in] name_len object name length
449  * \param[in] oflag open flag. UO_DIR should be passed for an dir object.
450  *
451  * \return U_SUCC or U_FAIL (error code in obj->err).
452  */
uffs_OpenObjectEx(uffs_Object * obj,uffs_Device * dev,int dir,const char * name,int name_len,int oflag)453 URET uffs_OpenObjectEx(uffs_Object *obj, uffs_Device *dev,
454 					   int dir, const char *name, int name_len, int oflag)
455 {
456 
457 	obj->err = UENOERR;
458 	obj->open_succ = U_FALSE;
459 
460 	if (dev == NULL) {
461 		obj->err = UEINVAL;
462 		goto ext;
463 	}
464 
465 	if ((oflag & (UO_WRONLY | UO_RDWR)) == (UO_WRONLY | UO_RDWR)) {
466 		/* UO_WRONLY and UO_RDWR can't appear together */
467 		uffs_Perror(UFFS_MSG_NOISY,
468 					"UO_WRONLY and UO_RDWR can't appear together");
469 		obj->err = UEINVAL;
470 		goto ext;
471 	}
472 
473 	obj->oflag = oflag;
474 	obj->parent = dir;
475 	obj->type = (oflag & UO_DIR ? UFFS_TYPE_DIR : UFFS_TYPE_FILE);
476 	obj->pos = 0;
477 	obj->dev = dev;
478 	obj->name = name;
479 	obj->name_len = name_len;
480 
481 	// adjust the name length
482 	if (obj->type == UFFS_TYPE_DIR) {
483 		if (obj->name_len > 0 && name[obj->name_len - 1] == '/')
484 			obj->name_len--;	// truncate the ending '/' for dir
485 	}
486 
487 	obj->sum = (obj->name_len > 0 ? uffs_MakeSum16(name, obj->name_len) : 0);
488 	obj->head_pages = obj->dev->attr->pages_per_block - 1;
489 
490 	if (obj->type == UFFS_TYPE_DIR) {
491 		if (obj->name_len == 0) {
492 			if (dir != PARENT_OF_ROOT) {
493 				uffs_Perror(UFFS_MSG_SERIOUS, "Bad parent for root dir!");
494 				obj->err = UEINVAL;
495 			}
496 			else {
497 				obj->serial = ROOT_DIR_SERIAL;
498 			}
499 			goto ext;
500 		}
501 	}
502 	else {
503 		if (obj->name_len == 0 || name[obj->name_len - 1] == '/') {
504 			uffs_Perror(UFFS_MSG_SERIOUS, "Bad file name.");
505 			obj->err = UEINVAL;
506 		}
507 	}
508 
509 
510 	uffs_ObjectDevLock(obj);
511 
512 	if (obj->type == UFFS_TYPE_DIR) {
513 		obj->node = uffs_TreeFindDirNodeByName(obj->dev, obj->name,
514 												obj->name_len, obj->sum,
515 												obj->parent);
516 	}
517 	else {
518 		obj->node = uffs_TreeFindFileNodeByName(obj->dev, obj->name,
519 												obj->name_len, obj->sum,
520 												obj->parent);
521 	}
522 
523 	if (obj->node == NULL) {			// dir or file not exist
524 		if (obj->oflag & UO_CREATE) {	// expect to create a new one
525 			uffs_ObjectDevUnLock(obj);
526 			if (obj->name == NULL || obj->name_len == 0)
527 				obj->err = UEEXIST;
528 			else
529 				uffs_CreateObjectEx(obj, dev, dir, obj->name, obj->name_len, oflag);
530 			goto ext;
531 		}
532 		else {
533 			obj->err = UENOENT;
534 			goto ext_1;
535 		}
536 	}
537 
538 	if ((obj->oflag & (UO_CREATE | UO_EXCL)) == (UO_CREATE | UO_EXCL)){
539 		obj->err = UEEXIST;
540 		goto ext_1;
541 	}
542 
543 	obj->serial = GET_OBJ_NODE_SERIAL(obj);
544 	obj->open_succ = U_TRUE;
545 
546 	if (obj->oflag & UO_TRUNC)
547 		if (do_TruncateObject(obj, 0, eDRY_RUN) == U_SUCC) {
548 			//NOTE: obj->err will be set in do_TruncateObject() if failed.
549 			do_TruncateObject(obj, 0, eREAL_RUN);
550 		}
551 
552 ext_1:
553 	uffs_ObjectDevUnLock(obj);
554 ext:
555 	obj->open_succ = (obj->err == UENOERR ? U_TRUE : U_FALSE);
556 
557 	return (obj->err == UENOERR ? U_SUCC : U_FAIL);
558 }
559 
560 
561 /**
562  * Parse the full path name, initialize obj.
563  *
564  * \param[out] obj object to be initialize.
565  * \param[in] name full path name.
566  *
567  * \return U_SUCC if the name is parsed correctly,
568  *			 U_FAIL if failed, and obj->err is set.
569  *
570  *	\note the following fields in obj will be initialized:
571  *			obj->dev
572  *			obj->parent
573  *			obj->name
574  *			obj->name_len
575  */
uffs_ParseObject(uffs_Object * obj,const char * name)576 URET uffs_ParseObject(uffs_Object *obj, const char *name)
577 {
578 	int len, m_len, d_len;
579 	uffs_Device *dev;
580 	const char *start, *p, *dname;
581 	u16 dir;
582 	TreeNode *node;
583 	u16 sum;
584 
585 	if (uffs_ReInitObject(obj) == U_FAIL)
586 		return U_FAIL;
587 
588 	len = strlen(name);
589 	m_len = uffs_GetMatchedMountPointSize(name);
590 	dev = uffs_GetDeviceFromMountPointEx(name, m_len);
591 
592 	if (dev) {
593 		start = name + m_len;
594 		d_len = GetDirLengthFromPath(start, len - m_len);
595 		p = start;
596 		obj->dev = dev;
597 		if (m_len == len) {
598 			obj->parent = PARENT_OF_ROOT;
599 			obj->name = NULL;
600 			obj->name_len = 0;
601 		}
602 		else {
603 			dir = ROOT_DIR_SERIAL;
604 			dname = start;
605 			while (p - start < d_len) {
606 				while (*p != '/') p++;
607 				sum = uffs_MakeSum16(dname, p - dname);
608 				node = uffs_TreeFindDirNodeByName(dev, dname, p - dname, sum, dir);
609 				if (node == NULL) {
610 					obj->err = UENOENT;
611 					break;
612 				}
613 				else {
614 					dir = node->u.dir.serial;
615 					p++; // skip the '/'
616 					dname = p;
617 				}
618 			}
619 			obj->parent = dir;
620 			obj->name = start + (d_len > 0 ? d_len + 1 : 0);
621 			obj->name_len = len - (d_len > 0 ? d_len + 1 : 0) - m_len;
622 		}
623 
624 		if (obj->err != UENOERR) {
625 			uffs_PutDevice(obj->dev);
626 			obj->dev = NULL;
627 		}
628 	}
629 	else {
630 		obj->err = UENOENT;
631 	}
632 
633 	return (obj->err == UENOERR ? U_SUCC : U_FAIL);
634 }
635 
636 
do_ReleaseObjectResource(uffs_Object * obj)637 static void do_ReleaseObjectResource(uffs_Object *obj)
638 {
639 	if (obj) {
640 		if (obj->dev) {
641 			if (HAVE_BADBLOCK(obj->dev))
642 				uffs_BadBlockRecover(obj->dev);
643 			if (obj->dev_lock_count > 0) {
644 				uffs_ObjectDevUnLock(obj);
645 			}
646 			uffs_PutDevice(obj->dev);
647 			obj->dev = NULL;
648 			obj->open_succ = U_FALSE;
649 		}
650 	}
651 }
652 
653 /**
654  * Open a UFFS object
655  *
656  * \param[in|out] obj the object to be open
657  * \param[in] name the full name of the object
658  * \param[in] oflag open flag
659  *
660  * \return U_SUCC if object is opened successfully,
661  *			 U_FAIL if failed, error code will be set to obj->err.
662  */
uffs_OpenObject(uffs_Object * obj,const char * name,int oflag)663 URET uffs_OpenObject(uffs_Object *obj, const char *name, int oflag)
664 {
665 	URET ret;
666 
667 	if (obj == NULL)
668 		return U_FAIL;
669 
670  	if ((ret = uffs_ParseObject(obj, name)) == U_SUCC) {
671 		ret = uffs_OpenObjectEx(obj, obj->dev, obj->parent,
672 									obj->name, obj->name_len, oflag);
673  	}
674  	if (ret != U_SUCC)
675  		do_ReleaseObjectResource(obj);
676 
677 	return ret;
678 }
679 
do_FlushObject(uffs_Object * obj)680 static URET do_FlushObject(uffs_Object *obj)
681 {
682 	uffs_Device *dev;
683 	URET ret = U_SUCC;
684 	TreeNode *node = NULL;
685 
686 	dev = obj->dev;
687 	if (obj->node) {
688 		node = obj->node;
689 		if (obj->type == UFFS_TYPE_DIR)
690 			ret = uffs_BufFlushGroup(dev, obj->node->u.dir.parent,
691 										obj->node->u.dir.serial);
692 		else {
693 			ret = (
694 				uffs_BufFlushGroupMatchParent(dev, obj->node->u.file.serial) == U_SUCC &&
695 				uffs_BufFlushGroup(dev, obj->node->u.file.parent, obj->node->u.file.serial) == U_SUCC
696 				) ? U_SUCC : U_FAIL;
697 		}
698 		uffs_Assert(node == obj->node, "obj->node change!\n");
699 	}
700 
701 	return ret;
702 }
703 
704 /**
705  * Flush object data.
706  *
707  * \param[in] obj object to be flushed
708  * \return U_SUCC or U_FAIL (error code in obj->err).
709  */
uffs_FlushObject(uffs_Object * obj)710 URET uffs_FlushObject(uffs_Object *obj)
711 {
712 	if(obj->dev == NULL || obj->open_succ != U_TRUE) {
713 		obj->err = UEBADF;
714 		goto ext;
715 	}
716 
717 	uffs_ObjectDevLock(obj);
718 
719 	if (do_FlushObject(obj) != U_SUCC)
720 		obj->err = UEIOERR;
721 
722 	uffs_ObjectDevUnLock(obj);
723 
724 ext:
725 	return (obj->err == UENOERR ? U_SUCC : U_FAIL);
726 }
727 
728 /**
729  * Close an openned object.
730  *
731  * \param[in] obj object to be closed
732  * \return U_SUCC or U_FAIL (error code in obj->err).
733  */
uffs_CloseObject(uffs_Object * obj)734 URET uffs_CloseObject(uffs_Object *obj)
735 {
736 #ifdef CONFIG_CHANGE_MODIFY_TIME
737 	uffs_Device *dev;
738 	uffs_Buf *buf;
739 	uffs_FileInfo fi;
740 #endif
741 
742 	if(obj->dev == NULL || obj->open_succ != U_TRUE) {
743 		obj->err = UEBADF;
744 		goto ext;
745 	}
746 
747 
748 	uffs_ObjectDevLock(obj);
749 
750 	if (obj->oflag & (UO_WRONLY|UO_RDWR|UO_APPEND|UO_CREATE|UO_TRUNC)) {
751 
752 #ifdef CONFIG_CHANGE_MODIFY_TIME
753         dev = obj->dev;
754 		if (obj->node) {
755 			//need to change the last modify time stamp
756 			if (obj->type == UFFS_TYPE_DIR)
757 				buf = uffs_BufGetEx(dev, UFFS_TYPE_DIR, obj->node, 0, obj->oflag);
758 			else
759 				buf = uffs_BufGetEx(dev, UFFS_TYPE_FILE, obj->node, 0, obj->oflag);
760 
761 			if(buf == NULL) {
762 				uffs_Perror(UFFS_MSG_SERIOUS, "can't get file header");
763 				do_FlushObject(obj);
764 				uffs_ObjectDevUnLock(obj);
765 				goto ext;
766 			}
767 			uffs_BufRead(dev, buf, &fi, 0, sizeof(uffs_FileInfo));
768 			fi.last_modify = uffs_GetCurDateTime();
769 			uffs_BufWrite(dev, buf, &fi, 0, sizeof(uffs_FileInfo));
770 			uffs_BufPut(dev, buf);
771 		}
772 #endif
773 		do_FlushObject(obj);
774 	}
775 
776 	uffs_ObjectDevUnLock(obj);
777 
778 ext:
779 	do_ReleaseObjectResource(obj);
780 
781 	return (obj->err == UENOERR ? U_SUCC : U_FAIL);
782 }
783 
GetFdnByOfs(uffs_Object * obj,u32 ofs)784 static u16 GetFdnByOfs(uffs_Object *obj, u32 ofs)
785 {
786 	uffs_Device *dev = obj->dev;
787 
788 	if (ofs < (u32)(obj->head_pages * dev->com.pg_data_size)) {
789 		return 0;
790 	}
791 	else {
792 		ofs -= obj->head_pages * dev->com.pg_data_size;
793 		return (ofs / (dev->com.pg_data_size * dev->attr->pages_per_block)) + 1;
794 	}
795 }
796 
797 
GetStartOfDataBlock(uffs_Object * obj,u16 fdn)798 static u32 GetStartOfDataBlock(uffs_Object *obj, u16 fdn)
799 {
800 	if (fdn == 0) {
801 		return 0;
802 	}
803 	else {
804 		return (obj->head_pages * obj->dev->com.pg_data_size) +
805 					(fdn - 1) * (obj->dev->com.pg_data_size	*
806 						obj->dev->attr->pages_per_block);
807 	}
808 }
809 
810 
do_WriteNewBlock(uffs_Object * obj,const void * data,u32 len,u16 parent,u16 serial)811 static int do_WriteNewBlock(uffs_Object *obj,
812 						  const void *data, u32 len,
813 						  u16 parent,
814 						  u16 serial)
815 {
816 	uffs_Device *dev = obj->dev;
817 	u16 page_id;
818 	int wroteSize = 0;
819 	int size;
820 	uffs_Buf *buf;
821 	URET ret;
822 
823 	for (page_id = 0; page_id < dev->attr->pages_per_block; page_id++) {
824 		size = (len - wroteSize) > dev->com.pg_data_size ?
825 					dev->com.pg_data_size : len - wroteSize;
826 		if (size <= 0)
827 			break;
828 
829 		buf = uffs_BufNew(dev, UFFS_TYPE_DATA, parent, serial, page_id);
830 		if (buf == NULL) {
831 			uffs_Perror(UFFS_MSG_SERIOUS, "can't create a new page ?");
832 			break;
833 		}
834 		// Note: if data == NULL, we will fill '\0'
835 		ret = uffs_BufWrite(dev, buf, data == NULL ? NULL : (u8 *)data + wroteSize, 0, size);
836 		uffs_BufPut(dev, buf);
837 
838 		if (ret != U_SUCC) {
839 			uffs_Perror(UFFS_MSG_SERIOUS, "write data fail!");
840 			break;
841 		}
842 		wroteSize += size;
843 		obj->node->u.file.len += size;
844 	}
845 
846 	return wroteSize;
847 }
848 
do_WriteInternalBlock(uffs_Object * obj,TreeNode * node,u16 fdn,const void * data,u32 len,u32 blockOfs)849 static int do_WriteInternalBlock(uffs_Object *obj,
850 							   TreeNode *node,
851 							   u16 fdn,
852 							   const void *data,
853 							   u32 len,
854 							   u32 blockOfs)
855 {
856 	uffs_Device *dev = obj->dev;
857 	u16 maxPageID;
858 	u16 page_id;
859 	u32 size;
860 	u32 pageOfs;
861 	u32 wroteSize = 0;
862 	URET ret;
863 	uffs_Buf *buf;
864 	u32 block_start;
865 	u8 type;
866 	u16 parent, serial;
867 
868 	block_start = GetStartOfDataBlock(obj, fdn);
869 
870 	if (fdn == 0) {
871 		type = UFFS_TYPE_FILE;
872 		parent = node->u.file.parent;
873 		serial = node->u.file.serial;
874 	}
875 	else {
876 		type = UFFS_TYPE_DATA;
877 		parent = node->u.data.parent;
878 		serial = fdn;
879 	}
880 
881 	if (fdn == 0)
882 		maxPageID = obj->head_pages;
883 	else
884 		maxPageID = dev->attr->pages_per_block - 1;
885 
886 
887 	while (wroteSize < len) {
888 		page_id = blockOfs / dev->com.pg_data_size;
889 		if (fdn == 0)
890 			page_id++; //in file header, page_id start from 1, not 0.
891 		if (page_id > maxPageID)
892 			break;
893 
894 		pageOfs = blockOfs % dev->com.pg_data_size;
895 		size = (len - wroteSize + pageOfs) > dev->com.pg_data_size ?
896 					(dev->com.pg_data_size - pageOfs) : (len - wroteSize);
897 
898 		if ((obj->node->u.file.len % dev->com.pg_data_size) == 0 &&
899 			(blockOfs + block_start) == obj->node->u.file.len) {
900 
901 			buf = uffs_BufNew(dev, type, parent, serial, page_id);
902 
903 			if(buf == NULL) {
904 				uffs_Perror(UFFS_MSG_SERIOUS, "can create a new buf!");
905 				break;
906 			}
907 		}
908 		else {
909 			buf = uffs_BufGetEx(dev, type, node, page_id, obj->oflag);
910 			if (buf == NULL) {
911 				uffs_Perror(UFFS_MSG_SERIOUS, "can't get buffer ?");
912 				break;
913 			}
914 		}
915 
916 		// Note: if data == NULL, then we will fill '\0'
917 		ret = uffs_BufWrite(dev, buf, data == NULL ? NULL : (u8 *)data + wroteSize, pageOfs, size);
918 
919 		uffs_BufPut(dev, buf);
920 
921 		if (ret == U_FAIL) {
922 			uffs_Perror(UFFS_MSG_SERIOUS, "write inter data fail!");
923 			break;
924 		}
925 
926 		wroteSize += size;
927 		blockOfs += size;
928 
929 		if (block_start + blockOfs > obj->node->u.file.len)
930 			obj->node->u.file.len = block_start + blockOfs;
931 
932 	}
933 
934 	return wroteSize;
935 }
936 
937 
938 /**
939  * write data to obj, return remain data (0 if all data been written).
940  */
do_WriteObject(uffs_Object * obj,const void * data,int len)941 static int do_WriteObject(uffs_Object *obj, const void *data, int len)
942 {
943 	uffs_Device *dev = obj->dev;
944 	TreeNode *fnode = obj->node;
945 	int remain = len;
946 	u16 fdn;
947 	u32 write_start;
948 	TreeNode *dnode;
949 	u32 size;
950 
951 	while (remain > 0) {
952 		write_start = obj->pos + len - remain;
953 		if (write_start > fnode->u.file.len) {
954 			uffs_Perror(UFFS_MSG_SERIOUS, "write point out of file ?");
955 			break;
956 		}
957 
958 		fdn = GetFdnByOfs(obj, write_start);
959 
960 		if (write_start == fnode->u.file.len && fdn > 0 &&
961 			write_start == GetStartOfDataBlock(obj, fdn)) {
962 			if (dev->tree.erased_count < dev->cfg.reserved_free_blocks) {
963 				uffs_Perror(UFFS_MSG_NOISY, "insufficient block in write obj, new block");
964 				break;
965 			}
966 			size = do_WriteNewBlock(obj, data ? (u8 *)data + len - remain : NULL,
967 										remain, fnode->u.file.serial, fdn);
968 
969 			//
970 			// Flush the new block buffers immediately, so that the new data node will be
971 			// created and put in the tree.
972 			//
973 			// But before do that, we need to make sure the previous
974 			// data block (if exist) been flushed first.
975 			//
976 			if (fdn > 1) {
977 				uffs_BufFlushGroup(dev, fnode->u.file.serial, fdn - 1);
978 			}
979 			else {
980 				uffs_BufFlushGroup(dev, fnode->u.file.parent, fnode->u.file.serial);
981 			}
982 			// Now flush the new block.
983 			uffs_BufFlushGroup(dev, fnode->u.file.serial, fdn);
984 
985 			if (size == 0)
986 				break;
987 
988 			remain -= size;
989 		}
990 		else {
991 
992 			if(fdn == 0)
993 				dnode = obj->node;
994 			else
995 				dnode = uffs_TreeFindDataNode(dev, fnode->u.file.serial, fdn);
996 
997 			if(dnode == NULL) {
998 				uffs_Perror(UFFS_MSG_SERIOUS, "can't find data node in tree ?");
999 				obj->err = UEUNKNOWN_ERR;
1000 				break;
1001 			}
1002 			size = do_WriteInternalBlock(obj, dnode, fdn,
1003 									data ? (u8 *)data + len - remain : NULL, remain,
1004 									write_start - GetStartOfDataBlock(obj, fdn));
1005 #ifdef CONFIG_FLUSH_BUF_AFTER_WRITE
1006 			if (fdn == 0)
1007 				uffs_BufFlushGroup(dev, fnode->u.file.parent, fnode->u.file.serial);
1008 			else
1009 				uffs_BufFlushGroup(dev, fnode->u.file.serial, fdn);
1010 #endif
1011 			if (size == 0)
1012 				break;
1013 
1014 			remain -= size;
1015 		}
1016 	}
1017 
1018 	uffs_Assert(fnode == obj->node, "obj->node change!\n");
1019 
1020 	return remain;
1021 }
1022 
1023 
1024 /**
1025  * write data to obj, from obj->pos
1026  *
1027  * \param[in] obj file obj
1028  * \param[in] data data pointer
1029  * \param[in] len length of data to be write
1030  *
1031  * \return bytes wrote to obj
1032  */
uffs_WriteObject(uffs_Object * obj,const void * data,int len)1033 int uffs_WriteObject(uffs_Object *obj, const void *data, int len)
1034 {
1035 	uffs_Device *dev = obj->dev;
1036 	TreeNode *fnode = NULL;
1037 	int remain;
1038 	u32 pos;
1039 	int wrote = 0;
1040 
1041 	if (obj == NULL)
1042 		return 0;
1043 
1044 	if (obj->dev == NULL || obj->open_succ != U_TRUE) {
1045 		obj->err = UEBADF;
1046 		return 0;
1047 	}
1048 
1049 	if (obj->type == UFFS_TYPE_DIR) {
1050 		uffs_Perror(UFFS_MSG_NOISY, "Can't write to an dir object!");
1051 		obj->err = UEACCES;
1052 		return 0;
1053 	}
1054 
1055 	if (obj->oflag == UO_RDONLY) {
1056 		obj->err = UEACCES;  // can't write to 'read only' mode opened file
1057 		return 0;
1058 	}
1059 
1060 	fnode = obj->node;
1061 
1062 	uffs_ObjectDevLock(obj);
1063 
1064 	if (obj->oflag & UO_APPEND)
1065 		obj->pos = fnode->u.file.len;
1066 	else {
1067 		if (obj->pos > fnode->u.file.len) {
1068 			// current pos pass over the end of file, need to fill the gap with '\0'
1069 			pos = obj->pos;	// save desired pos
1070 			obj->pos = fnode->u.file.len; // filling gap from the end of the file.
1071 			remain = do_WriteObject(obj, NULL, pos - fnode->u.file.len);  // Write filling bytes. Note: the filling data does not count as 'wrote' in this write operation.
1072 			obj->pos = pos - remain;
1073 			if (remain > 0)	// fail to fill the gap ? stop.
1074 				goto ext;
1075 		}
1076 	}
1077 
1078 	remain = do_WriteObject(obj, data, len);
1079 	wrote = len - remain;
1080 	obj->pos += wrote;
1081 
1082 ext:
1083 	if (HAVE_BADBLOCK(dev))
1084 		uffs_BadBlockRecover(dev);
1085 
1086 	uffs_ObjectDevUnLock(obj);
1087 
1088 	uffs_Assert(fnode == obj->node, "obj->node change!\n");
1089 
1090 	return wrote;
1091 }
1092 
1093 /**
1094  * read data from obj
1095  *
1096  * \param[in] obj uffs object
1097  * \param[out] data output data buffer
1098  * \param[in] len required length of data to be read from object->pos
1099  *
1100  * \return return bytes of data have been read
1101  */
uffs_ReadObject(uffs_Object * obj,void * data,int len)1102 int uffs_ReadObject(uffs_Object *obj, void *data, int len)
1103 {
1104 	uffs_Device *dev = obj->dev;
1105 	TreeNode *fnode = NULL;
1106 	u32 remain = len;
1107 	u16 fdn;
1108 	u32 read_start;
1109 	TreeNode *dnode;
1110 	u32 size;
1111 	uffs_Buf *buf;
1112 	u32 blockOfs;
1113 	u16 page_id;
1114 	u8 type;
1115 	u32 pageOfs;
1116 
1117 	if (obj == NULL)
1118 		return 0;
1119 
1120 	fnode = obj->node;
1121 
1122 	if (obj->dev == NULL || obj->open_succ == U_FALSE) {
1123 		obj->err = UEBADF;
1124 		return 0;
1125 	}
1126 
1127 	if (obj->type == UFFS_TYPE_DIR) {
1128 		uffs_Perror(UFFS_MSG_NOISY, "Can't read data from a dir object!");
1129 		obj->err = UEBADF;
1130 		return 0;
1131 	}
1132 
1133 	if (obj->pos > fnode->u.file.len) {
1134 		return 0; //can't read file out of range
1135 	}
1136 
1137 	if (obj->oflag & UO_WRONLY) {
1138 		obj->err = UEACCES;
1139 		return 0;
1140 	}
1141 
1142 	uffs_ObjectDevLock(obj);
1143 
1144 	while (remain > 0) {
1145 		read_start = obj->pos + len - remain;
1146 		if (read_start >= fnode->u.file.len) {
1147 			//uffs_Perror(UFFS_MSG_NOISY, "read point out of file ?");
1148 			break;
1149 		}
1150 
1151 		fdn = GetFdnByOfs(obj, read_start);
1152 		if (fdn == 0) {
1153 			dnode = obj->node;
1154 			type = UFFS_TYPE_FILE;
1155 		}
1156 		else {
1157 			type = UFFS_TYPE_DATA;
1158 			dnode = uffs_TreeFindDataNode(dev, fnode->u.file.serial, fdn);
1159 			if (dnode == NULL) {
1160 				uffs_Perror(UFFS_MSG_SERIOUS, "can't get data node in entry!");
1161 				obj->err = UEUNKNOWN_ERR;
1162 				break;
1163 			}
1164 		}
1165 
1166 		blockOfs = GetStartOfDataBlock(obj, fdn);
1167 		page_id = (read_start - blockOfs) / dev->com.pg_data_size;
1168 
1169 		if (fdn == 0) {
1170 			/**
1171 			 * fdn == 0: this means that the reading is start from the first block,
1172 			 * since the page 0 is for file attr, so we move to the next page ID.
1173 			 */
1174 			page_id++;
1175 		}
1176 
1177 		buf = uffs_BufGetEx(dev, type, dnode, (u16)page_id, obj->oflag);
1178 		if (buf == NULL) {
1179 			uffs_Perror(UFFS_MSG_SERIOUS, "can't get buffer when read obj.");
1180 			obj->err = UEIOERR;
1181 			break;
1182 		}
1183 
1184 		pageOfs = read_start % dev->com.pg_data_size;
1185 		if (pageOfs >= buf->data_len) {
1186 			//uffs_Perror(UFFS_MSG_NOISY, "read data out of page range ?");
1187 			uffs_BufPut(dev, buf);
1188 			break;
1189 		}
1190 		size = (remain + pageOfs > buf->data_len ? buf->data_len - pageOfs : remain);
1191 
1192 		uffs_BufRead(dev, buf, (u8 *)data + len - remain, pageOfs, size);
1193 		uffs_BufPut(dev, buf);
1194 
1195 		remain -= size;
1196 	}
1197 
1198 	obj->pos += (len - remain);
1199 
1200 	if (HAVE_BADBLOCK(dev))
1201 		uffs_BadBlockRecover(dev);
1202 
1203 	uffs_ObjectDevUnLock(obj);
1204 
1205 	uffs_Assert(fnode == obj->node, "obj->node change!\n");
1206 
1207 	return len - remain;
1208 }
1209 
1210 /**
1211  * move the file pointer
1212  *
1213  * \param[in] obj uffs object
1214  * \param[in] offset offset from origin
1215  * \param[in] origin the origin position, one of:
1216  *				#USEEK_CUR, #USEEK_SET or #USEEK_END
1217  *
1218  * \return the new file pointer position if success,
1219  *			or -1 if the new position would be negative.
1220  */
uffs_SeekObject(uffs_Object * obj,long offset,int origin)1221 long uffs_SeekObject(uffs_Object *obj, long offset, int origin)
1222 {
1223 	if (obj->type == UFFS_TYPE_DIR) {
1224 		uffs_Perror(UFFS_MSG_NOISY, "Can't seek a dir object!");
1225 		obj->err = UEACCES;
1226 	}
1227 	else {
1228 		uffs_ObjectDevLock(obj);
1229 		switch (origin) {
1230 			case USEEK_CUR:
1231 				if ((long)obj->pos + offset < 0) {
1232 					obj->err = UEINVAL;
1233 				}
1234 				else {
1235 					obj->pos += offset;
1236 				}
1237 				break;
1238 			case USEEK_SET:
1239 				if (offset < 0) {
1240 					obj->err = UEINVAL;
1241 				}
1242 				else {
1243 					obj->pos = offset;
1244 				}
1245 				break;
1246 			case USEEK_END:
1247 				if ((long)obj->node->u.file.len + offset < 0) {
1248 					obj->err = UEINVAL;
1249 				}
1250 				else {
1251 					obj->pos = obj->node->u.file.len + offset;
1252 				}
1253 				break;
1254 		}
1255 		uffs_ObjectDevUnLock(obj);
1256 	}
1257 
1258 	return (obj->err == UENOERR ? (long)obj->pos : -1);
1259 }
1260 
1261 /**
1262  * get current file pointer
1263  *
1264  * \param[in] obj uffs object
1265  *
1266  * \return return the file pointer position if the obj is valid,
1267  *	 return -1 if obj is invalid.
1268  */
uffs_GetCurOffset(uffs_Object * obj)1269 int uffs_GetCurOffset(uffs_Object *obj)
1270 {
1271 	if (obj) {
1272 		if (obj->dev && obj->open_succ == U_TRUE)
1273 			return obj->pos;
1274 	}
1275 	return -1;
1276 }
1277 
1278 /**
1279  * check whether the file pointer is at the end of file
1280  *
1281  * \param[in] obj uffs object
1282  *
1283  * \return return 1 if file pointer is at the end of file,
1284  *	 return -1 if error occur, else return 0.
1285  */
uffs_EndOfFile(uffs_Object * obj)1286 int uffs_EndOfFile(uffs_Object *obj)
1287 {
1288 	if (obj) {
1289 		if (obj->dev && obj->type == UFFS_TYPE_FILE && obj->open_succ == U_TRUE) {
1290 			if (obj->pos >= obj->node->u.file.len) {
1291 				return 1;
1292 			}
1293 			else {
1294 				return 0;
1295 			}
1296 		}
1297 	}
1298 
1299 	return -1;
1300 }
1301 
1302 //
1303 // To trancate the file, this is the last block to be trancated.
1304 // We need to discard one or more pages within this block, hence requires 'block recover'.
1305 //
do_TruncateInternalWithBlockRecover(uffs_Object * obj,u16 fdn,u32 remain,RunOptionE run_opt)1306 static URET do_TruncateInternalWithBlockRecover(uffs_Object *obj,
1307 												u16 fdn, u32 remain, RunOptionE run_opt)
1308 {
1309 	uffs_Device *dev = obj->dev;
1310 	TreeNode *fnode = obj->node;
1311 	u16 page_id, max_page_id;
1312 	TreeNode *node;
1313 	uffs_Buf *buf = NULL;
1314 	u8 type;
1315 	u32 block_start;
1316 	u16 parent, serial;
1317 	int slot;
1318 	uffs_BlockInfo *bc = NULL;
1319 	int block = -1;
1320 
1321 	if (fdn == 0) {
1322 		node = fnode;
1323 		type = UFFS_TYPE_FILE;
1324 		max_page_id = obj->head_pages;
1325 		block_start = 0;
1326 		parent = node->u.file.parent;
1327 		serial = node->u.file.serial;
1328 		block = node->u.file.block;
1329 	}
1330 	else {
1331 		node = uffs_TreeFindDataNode(dev, fnode->u.file.serial, fdn);
1332 		if (node == NULL) {
1333 			obj->err = UEIOERR;
1334 			uffs_Perror(UFFS_MSG_SERIOUS,
1335 						"can't find data node when truncate obj");
1336 			goto ext;
1337 		}
1338 		block = node->u.data.block;
1339 		type = UFFS_TYPE_DATA;
1340 		max_page_id = dev->attr->pages_per_block - 1;
1341 		block_start = obj->head_pages * dev->com.pg_data_size +
1342 						(fdn - 1) * dev->com.pg_data_size *
1343 						dev->attr->pages_per_block;
1344 		parent = node->u.data.parent;
1345 		serial = node->u.data.serial;
1346 	}
1347 
1348 	if (run_opt == eDRY_RUN) {
1349 		// checking the buffer. this is the main reason why we need the 'dry run' mode.
1350 		for (page_id = 0; page_id <= max_page_id; page_id++) {
1351 			buf = uffs_BufFind(dev, parent, serial, page_id);
1352 			if (buf) {
1353 				//!< ok, the buffer was loaded before ...
1354 				if (uffs_BufIsFree(buf) == U_FALSE) {
1355 					obj->err = UEEXIST;
1356 					break;	//!< and someone is still holding the buffer,
1357 							//   can't truncate it !!!
1358 				}
1359 			}
1360 		}
1361 		buf = NULL;
1362 		goto ext;
1363 	}
1364 
1365 	// find the last page *after* truncate
1366 	for (page_id = (fdn == 0 ? 1 : 0); page_id <= max_page_id; page_id++) {
1367 		if (block_start + (page_id + 1) * dev->com.pg_data_size >= remain)
1368 			break;
1369 	}
1370 
1371 	if (!uffs_Assert(page_id <= max_page_id, "fdn = %d, block_start = %d, remain = %d\n", fdn, block_start, remain)) {
1372 		obj->err = UEUNKNOWN_ERR;
1373 		goto ext;
1374 	}
1375 
1376 	// flush buffer before performing block recovery
1377 	uffs_BufFlushGroup(dev, parent, serial);
1378 
1379 	// load the last page
1380 	buf = uffs_BufGetEx(dev, type, node, page_id, obj->oflag);
1381 	if (buf == NULL) {
1382 		obj->err = UENOMEM;
1383 		uffs_Perror(UFFS_MSG_SERIOUS, "Can't get buf");
1384 		goto ext;
1385 	}
1386 
1387 	uffs_BufWrite(dev, buf, NULL, 0, 0); // just make this buf dirty
1388 
1389 	// lock the group
1390 	slot = uffs_BufFindGroupSlot(dev, parent, serial);
1391 	uffs_BufLockGroup(dev, slot);
1392 
1393 	if (remain == 0) // remain == 0: means discard all data in this block.
1394 		buf->data_len = 0;
1395 	else {
1396 		remain = (remain % dev->com.pg_data_size);
1397 		// remain == 0: means that we need to keep all data in this page.
1398 		buf->data_len = (remain == 0 ? dev->com.pg_data_size : remain);
1399 	}
1400 
1401 	/* mark this buf as UFFS_BUF_EXT_MARK_TRUNC_TAIL, when flushing
1402 		dirty buffers, UFFS will not do page recover for pages after
1403 		this buf page id (because this file is ended at this page) */
1404 	buf->ext_mark |= UFFS_BUF_EXT_MARK_TRUNC_TAIL;
1405 
1406 	uffs_BufPut(dev, buf);
1407 
1408 	// invalidate the rest page buf
1409 	page_id++;
1410 	for (; page_id <= max_page_id; page_id++) {
1411 		buf = uffs_BufFind(dev, parent, serial, page_id);
1412 		if (buf)
1413 			uffs_BufMarkEmpty(dev, buf);
1414 	}
1415 
1416 	// flush dirty buffer immediately, forcing block recovery.
1417 	uffs_BufFlushGroupEx(dev, parent, serial, U_TRUE);
1418 
1419 	// unlock the group
1420 	uffs_BufUnLockGroup(dev, slot);
1421 
1422 	// Invalidate block info cache for the 'old' block
1423 	bc = uffs_BlockInfoGet(dev, block);
1424 	if (bc) {
1425 		uffs_BlockInfoExpire(dev, bc, UFFS_ALL_PAGES);
1426 		uffs_BlockInfoPut(dev, bc);
1427 	}
1428 
1429 ext:
1430 	return (obj->err == UENOERR ? U_SUCC : U_FAIL);
1431 }
1432 
1433 /**
1434  * truncate an object
1435  *
1436  * \param[in] obj object to be truncated
1437  * \param[in] remain data bytes to be remained in this object
1438  *
1439  * \return U_SUCC or U_FAIL (error code in obj->err)
1440  */
uffs_TruncateObject(uffs_Object * obj,u32 remain)1441 URET uffs_TruncateObject(uffs_Object *obj, u32 remain)
1442 {
1443 	uffs_ObjectDevLock(obj);
1444 	if (do_TruncateObject(obj, remain, eDRY_RUN) == U_SUCC)
1445 		do_TruncateObject(obj, remain, eREAL_RUN);
1446 	uffs_ObjectDevUnLock(obj);
1447 
1448 	uffs_FlushObject(obj);
1449 
1450 	return (obj->err == UENOERR ? U_SUCC : U_FAIL);
1451 }
1452 
1453 /** truncate obj without lock device */
do_TruncateObject(uffs_Object * obj,u32 remain,RunOptionE run_opt)1454 static URET do_TruncateObject(uffs_Object *obj, u32 remain, RunOptionE run_opt)
1455 {
1456 	uffs_Device *dev = obj->dev;
1457 	TreeNode *fnode = obj->node;
1458 	u16 fdn;
1459 	u32 flen;
1460 	u32 block_start;
1461 	TreeNode *node;
1462 	uffs_BlockInfo *bc;
1463 	uffs_Buf *buf;
1464 	u16 page;
1465 	int pos;
1466 
1467 	pos = obj->pos;   // save current file position
1468 
1469 	if (obj->dev == NULL || obj->open_succ == U_FALSE || fnode == NULL) {
1470 		obj->err = UEBADF;
1471 		goto ext;
1472 	}
1473 
1474 	/* can't truncate a dir */
1475 	/* TODO: delete files under dir ? */
1476 	if (obj->type == UFFS_TYPE_DIR) {
1477 		obj->err = UEEXIST;
1478 		goto ext;
1479 	}
1480 
1481 	if (remain >= fnode->u.file.len) {
1482 		goto ext;	//!< nothing to do ...
1483 	}
1484 
1485 	flen = fnode->u.file.len;
1486 
1487 	if (flen < remain) {
1488 		// file is shorter than 'reamin', fill the gap with '\0'
1489 		if (run_opt == eREAL_RUN) {
1490 			obj->pos = flen;  // move file pointer to the end
1491 			if (do_WriteObject(obj, NULL, remain - flen) > 0) {	// fill '\0' ...
1492 				uffs_Perror(UFFS_MSG_SERIOUS, "Write object not finished. expect %d but only %d wrote.",
1493 												remain - flen, fnode->u.file.len - flen);
1494 				obj->err = UEIOERR;   // likely be an I/O error.
1495 			}
1496 			flen = obj->node->u.file.len;
1497 		}
1498 	}
1499 	else {
1500 		while (flen > remain) {
1501 			fdn = GetFdnByOfs(obj, flen - 1);
1502 
1503 			//uffs_BufFlushGroup(dev, obj->serial, fdn);	//!< flush the buffer
1504 
1505 			block_start = GetStartOfDataBlock(obj, fdn);
1506 			if (remain <= block_start && fdn > 0) {
1507 				node = uffs_TreeFindDataNode(dev, obj->serial, fdn);
1508 				if (node == NULL) {
1509 					uffs_Perror(UFFS_MSG_SERIOUS,
1510 								"can't find data node when trancate obj.");
1511 					obj->err = UEIOERR;
1512 					goto ext;
1513 				}
1514 				bc = uffs_BlockInfoGet(dev, node->u.data.block);
1515 				if (bc == NULL) {
1516 					uffs_Perror(UFFS_MSG_SERIOUS,
1517 								"can't get block info when trancate obj.");
1518 					obj->err = UEIOERR;
1519 					goto ext;
1520 				}
1521 
1522 				for (page = 0; page < dev->attr->pages_per_block; page++) {
1523 					buf = uffs_BufFind(dev, fnode->u.file.serial, fdn, page);
1524 					if (buf) {
1525 						//!< ok, the buffer was loaded before ...
1526 						if (uffs_BufIsFree(buf) == U_FALSE) {
1527 							uffs_BlockInfoPut(dev, bc);
1528 							goto ext;	//!< and someone is still holding the buffer,
1529 										//   can't truncate it !!!
1530 						}
1531 						else if (run_opt == eREAL_RUN)
1532 							uffs_BufMarkEmpty(dev, buf);	//!< discard the buffer
1533 					}
1534 				}
1535 
1536 				if (run_opt == eREAL_RUN) {
1537 					uffs_BreakFromEntry(dev, UFFS_TYPE_DATA, node);
1538 					uffs_FlashEraseBlock(dev, bc->block);
1539 					node->u.list.block = bc->block;
1540 					if (HAVE_BADBLOCK(dev))
1541 						uffs_BadBlockProcess(dev, node);
1542 					else
1543 						uffs_TreeInsertToErasedListTail(dev, node);
1544 
1545 					fnode->u.file.len = block_start;
1546 				}
1547 
1548 				flen = block_start;
1549 				uffs_BlockInfoPut(dev, bc);
1550 			}
1551 			else {
1552 				if (do_TruncateInternalWithBlockRecover(obj, fdn,
1553 														remain, run_opt) == U_SUCC) {
1554 					if (run_opt == eREAL_RUN)
1555 						fnode->u.file.len = remain;
1556 					flen = remain;
1557 				}
1558 			}
1559 		}
1560 	}
1561 
1562 	if (HAVE_BADBLOCK(dev))
1563 		uffs_BadBlockRecover(dev);
1564 ext:
1565 	obj->pos = pos;  // keep file pointer offset not changed.
1566 
1567 	uffs_Assert(fnode == obj->node, "obj->node change!\n");
1568 
1569 	return (obj->err == UENOERR ? U_SUCC : U_FAIL);
1570 
1571 }
1572 
1573 /**
1574  * \brief check if there are anyone holding buf of this obj.
1575  *        If no one holding the buffers, expire the buffer.
1576  * \return
1577  *		0	: no one holding any buf of this obj
1578  *		>0	: the ref_count of buf which refer to this obj.
1579  */
_CheckObjBufRef(uffs_Object * obj)1580 int _CheckObjBufRef(uffs_Object *obj)
1581 {
1582 	uffs_Device *dev = obj->dev;
1583 	uffs_Buf *buf;
1584 	TreeNode *node = obj->node;
1585 	u16 parent, serial, last_serial;
1586 
1587 	// check the DIR or FILE block
1588 	for (buf = uffs_BufFind(dev, obj->parent, obj->serial, UFFS_ALL_PAGES);
1589 		 buf != NULL;
1590 		 buf = uffs_BufFindFrom(dev, buf->next, obj->parent, obj->serial, UFFS_ALL_PAGES))
1591 	{
1592 		if (buf->ref_count > 0) {
1593 			// oops ...
1594 			uffs_Perror(UFFS_MSG_SERIOUS, "someone still hold buf parent = %d, serial = %d, ref_count",
1595 				obj->parent, obj->serial, buf->ref_count);
1596 
1597 			return buf->ref_count;
1598 		}
1599 		else {
1600 			buf->mark = UFFS_BUF_EMPTY;
1601 		}
1602 	}
1603 
1604 	if (buf == NULL || buf->ref_count == 0) {
1605 		// check the DATA block
1606 		if (obj->type == UFFS_TYPE_FILE && node->u.file.len > 0) {
1607 
1608 			parent = obj->serial;
1609 			last_serial = GetFdnByOfs(obj, node->u.file.len - 1);
1610 			for (serial = 1; serial <= last_serial; serial++) {
1611 
1612 				for (buf = uffs_BufFind(dev, parent, serial, UFFS_ALL_PAGES);
1613 					 buf != NULL;
1614 					 buf = uffs_BufFindFrom(dev, buf->next, parent, serial, UFFS_ALL_PAGES))
1615 				{
1616 					if (buf->ref_count != 0) {
1617 						// oops ...
1618 						uffs_Perror(UFFS_MSG_SERIOUS, "someone still hold buf parent = %d, serial = %d, ref_count",
1619 							parent, serial, buf->ref_count);
1620 
1621 						return buf->ref_count;
1622 					}
1623 					else {
1624 						buf->mark = UFFS_BUF_EMPTY;
1625 					}
1626 				}
1627 			}
1628 		}
1629 	}
1630 
1631 	return 0;
1632 }
1633 
1634 
1635 
1636 /**
1637  * \brief delete uffs object
1638  *
1639  * \param[in] name full name of object
1640  * \param[out] err return error code
1641  *
1642  * \return U_SUCC if object is deleted successfully.
1643  *	return U_FAIL if error happen, error code is set to *err.
1644  */
uffs_DeleteObject(const char * name,int * err)1645 URET uffs_DeleteObject(const char * name, int *err)
1646 {
1647 	uffs_Object *obj, *work;
1648 	TreeNode *node, *d_node;
1649 	uffs_Device *dev = NULL;
1650 	u16 block;
1651 	u16 serial, parent, last_serial;
1652 	UBOOL bad = U_FALSE;
1653 	URET ret = U_FAIL;
1654 
1655 	obj = uffs_GetObject();
1656 	if (obj == NULL) {
1657 		if (err)
1658 			*err = UEMFILE;
1659 		goto ext_unlock;
1660 	}
1661 
1662 	if (uffs_OpenObject(obj, name, UO_RDWR|UO_DIR) == U_FAIL) {
1663 		if (uffs_OpenObject(obj, name, UO_RDWR) == U_FAIL) {
1664 			if (err)
1665 				*err = UENOENT;
1666 			goto ext_unlock;
1667 		}
1668 	}
1669 
1670 	dev = obj->dev;
1671 
1672 	// working throught object pool see if the object is opened ...
1673 	uffs_ObjectDevLock(obj);
1674 	work = NULL;
1675 	while ((work = (uffs_Object *)uffs_PoolFindNextAllocated(&_object_pool, work)) != NULL) {
1676 		if (work != obj &&
1677 			work->dev &&
1678 			work->dev == obj->dev &&
1679 			work->node &&
1680 			work->node == obj->node) {
1681 			// this object is opened, can't delete it.
1682 			if (err)
1683 				*err = UEACCES;
1684 			goto ext_lock;
1685 		}
1686 	}
1687 
1688 	if (obj->type == UFFS_TYPE_DIR) {
1689 		// if the dir is not empty, can't delete it.
1690 		node = uffs_TreeFindDirNodeWithParent(dev, obj->serial);
1691 		if (node != NULL) {
1692 			if (err)
1693 				*err = UEACCES;
1694 			goto ext_lock;  //have sub dirs ?
1695 		}
1696 
1697 		node = uffs_TreeFindFileNodeWithParent(dev, obj->serial);
1698 		if (node != NULL) {
1699 			if (err)
1700 				*err = UEACCES;
1701 			goto ext_lock;  //have sub files ?
1702 		}
1703 	}
1704 
1705 	// before erase the block, we need to take care of the buffer ...
1706 	uffs_BufFlushAll(dev);
1707 
1708 	if (_CheckObjBufRef(obj) > 0) {
1709 		if (err)
1710 			*err = UEACCES;
1711 		goto ext_lock;
1712 	}
1713 
1714 	node = obj->node;
1715 
1716 	// ok, now we are safe to erase DIR/FILE block :-)
1717 	block = GET_BLOCK_FROM_NODE(obj);
1718 	parent = obj->serial;
1719 	last_serial = (obj->type == UFFS_TYPE_FILE && node->u.file.len > 0 ? GetFdnByOfs(obj, node->u.file.len - 1) : 0);
1720 
1721 	uffs_BreakFromEntry(dev, obj->type, node);
1722 	uffs_FlashEraseBlock(dev, block);
1723 	node->u.list.block = block;
1724 	node->u.list.u.serial = obj->serial;
1725 
1726 	// From now on, the object is gone physically,
1727 	// but we need to 'suspend' this node so that no one will re-use
1728 	// the serial number during deleting the reset part of object.
1729 
1730 	if (HAVE_BADBLOCK(dev)) {
1731 		uffs_BadBlockProcessSuspend(dev, node);
1732 		bad = U_TRUE;  // will be put into 'bad' list later
1733 	}
1734 	else {
1735 		uffs_TreeSuspendAdd(dev, node);
1736 		bad = U_FALSE;	// will be put into erased list later
1737 	}
1738 
1739 	// now erase DATA blocks
1740 	if (obj->type == UFFS_TYPE_FILE && last_serial > 0) {
1741 		for (serial = 1; serial <= last_serial; serial++) {
1742 
1743 			uffs_ObjectDevUnLock(obj);
1744 			; // yield CPU to improve responsive when deleting large file.
1745 			uffs_ObjectDevLock(obj);
1746 
1747 			d_node = uffs_TreeFindDataNode(dev, parent, serial);
1748 			if (uffs_Assert(d_node != NULL, "Can't find DATA node parent = %d, serial = %d\n", parent, serial)) {
1749 				uffs_BreakFromEntry(dev, UFFS_TYPE_DATA, d_node);
1750 				block = d_node->u.data.block;
1751 				uffs_FlashEraseBlock(dev, block);
1752 				d_node->u.list.block = block;
1753 				if (HAVE_BADBLOCK(dev))
1754 					uffs_BadBlockProcess(dev, d_node);
1755 				else
1756 					uffs_TreeInsertToErasedListTail(dev, d_node);
1757 			}
1758 		}
1759 	}
1760 
1761 	// now process the suspend node
1762 	uffs_TreeRemoveSuspendNode(dev, node);
1763 	if (bad)
1764 		uffs_TreeInsertToBadBlockList(dev, node);
1765 	else
1766 		uffs_TreeInsertToErasedListTail(dev, node);
1767 
1768 	ret = U_SUCC;
1769 
1770 ext_lock:
1771 	uffs_ObjectDevUnLock(obj);
1772 ext_unlock:
1773 	do_ReleaseObjectResource(obj);
1774 
1775 	uffs_PutObject(obj);
1776 
1777 	return ret;
1778 }
1779 
1780 /**
1781  * Remove object under a new parent, change object name.
1782  *
1783  * \param[in|out] obj
1784  * \param[in] new_parent new parent's serial number
1785  * \param[in] new_name new name of the object.
1786  *			 if new_name == NULL, keep the old name.
1787  * \param[in] name_len new name length.
1788  *
1789  * \return U_SUCC or U_FAIL (obj->err for the reason)
1790  */
uffs_MoveObjectEx(uffs_Object * obj,int new_parent,const char * new_name,int name_len)1791 URET uffs_MoveObjectEx(uffs_Object *obj,
1792 					   int new_parent, const char *new_name, int name_len)
1793 {
1794 	uffs_Buf *buf;
1795 	uffs_FileInfo fi;
1796 	uffs_Device *dev = obj->dev;
1797 	TreeNode *node = obj->node;
1798 
1799 	if (dev == NULL || node == NULL || obj->open_succ != U_TRUE) {
1800 		obj->err = UEBADF;
1801 		goto ext;
1802 	}
1803 
1804 	uffs_ObjectDevLock(obj);
1805 
1806 	obj->parent = new_parent;
1807 
1808 	if (name_len > 0) {
1809 
1810 		buf = uffs_BufGetEx(dev, obj->type, node, 0, obj->oflag);
1811 		if (buf == NULL) {
1812 			uffs_Perror(UFFS_MSG_SERIOUS, "can't get buf when rename!");
1813 			obj->err = UEIOERR;
1814 			goto ext_1;
1815 		}
1816 
1817 		memcpy(&fi, buf->data, sizeof(uffs_FileInfo));
1818 
1819 		if (new_name[name_len-1] == '/')
1820 			name_len--;
1821 
1822 		memcpy(fi.name, new_name, name_len);
1823 		fi.name[name_len] = 0;
1824 		fi.name_len = name_len;
1825 		fi.last_modify = uffs_GetCurDateTime();
1826 
1827 		buf->parent = new_parent;	// !! need to manually change the 'parent' !!
1828 		uffs_BufWrite(dev, buf, &fi, 0, sizeof(uffs_FileInfo));
1829 		uffs_BufPut(dev, buf);
1830 
1831 		// !! force a block recover so that all old tag will be expired !!
1832 		// This is important so we only need to check
1833 		// the first spare when mount UFFS :)
1834 		uffs_BufFlushGroupEx(dev, obj->parent, obj->serial, U_TRUE);
1835 
1836 		obj->name = new_name;
1837 		obj->name_len = name_len;
1838 		obj->sum = uffs_MakeSum16(fi.name, fi.name_len);
1839 	}
1840 
1841 	//update the check sum and new parent of tree node
1842 	if (obj->type == UFFS_TYPE_DIR) {
1843 		obj->node->u.dir.checksum = obj->sum;
1844 		obj->node->u.dir.parent = new_parent;
1845 	}
1846 	else {
1847 		obj->node->u.file.checksum = obj->sum;
1848 		obj->node->u.file.parent = new_parent;
1849 	}
1850 
1851 ext_1:
1852 	uffs_ObjectDevUnLock(obj);
1853 ext:
1854 
1855 	return (obj->err == UENOERR ? U_SUCC : U_FAIL);
1856 }
1857 
1858 /**
1859  * \brief rename(move) file or dir.
1860  * \return U_SUCC if success, otherwise return U_FAIL and set error code to *err.
1861  * \note rename/move file between different mount point is not allowed.
1862  */
uffs_RenameObject(const char * old_name,const char * new_name,int * err)1863 URET uffs_RenameObject(const char *old_name, const char *new_name, int *err)
1864 {
1865 	uffs_Object *obj = NULL, *new_obj = NULL;
1866 	URET ret = U_FAIL;
1867 	int oflag;
1868 
1869 	obj = uffs_GetObject();
1870 	new_obj = uffs_GetObject();
1871 
1872 	if (obj == NULL || new_obj == NULL) {
1873 		if (err)
1874 			*err = UEINVAL;
1875 		goto ext;
1876 	}
1877 
1878 	oflag = UO_RDONLY;
1879 	if (uffs_OpenObject(new_obj, new_name, oflag) == U_SUCC) {
1880 		uffs_CloseObject(new_obj);
1881 		uffs_Perror(UFFS_MSG_NOISY, "new object already exist!");
1882 		if (err)
1883 			*err = UEEXIST;
1884 		goto ext;
1885 	}
1886 	oflag |= UO_DIR;
1887 	if (uffs_OpenObject(new_obj, new_name, oflag) == U_SUCC) {
1888 		uffs_CloseObject(new_obj);
1889 		uffs_Perror(UFFS_MSG_NOISY, "new object already exist!");
1890 		if (err)
1891 			*err = UEEXIST;
1892 		goto ext;
1893 	}
1894 
1895 	if (uffs_ParseObject(new_obj, new_name) != U_SUCC) {
1896 		uffs_Perror(UFFS_MSG_NOISY, "parse new name fail !");
1897 		if (err)
1898 			*err = UENOENT;
1899 		goto ext;
1900 	}
1901 
1902 	if (new_obj->name_len == 0) {
1903 		uffs_Perror(UFFS_MSG_NOISY, "invalid new name");
1904 		if (err)
1905 			*err = UEINVAL;
1906 		goto ext;
1907 	}
1908 
1909 	oflag = UO_RDONLY;
1910 	if (uffs_OpenObject(obj, old_name, oflag) != U_SUCC) {
1911 		oflag |= UO_DIR;
1912 		if (uffs_OpenObject(obj, old_name, oflag) != U_SUCC) {
1913 			uffs_Perror(UFFS_MSG_NOISY, "Can't open old object !");
1914 			if (err)
1915 				*err = UEACCES;
1916 			goto ext;
1917 		}
1918 	}
1919 
1920 	if (obj->dev != new_obj->dev) {
1921 		uffs_Perror(UFFS_MSG_NOISY,
1922 					"Can't move object between different mount point");
1923 		if (err)
1924 			*err = UEACCES;
1925 	}
1926 	else {
1927 		ret = uffs_MoveObjectEx(obj, new_obj->parent,
1928 									new_obj->name, new_obj->name_len);
1929 		if (ret == U_FAIL && err)
1930 			*err = obj->err;
1931 	}
1932 
1933 	uffs_CloseObject(obj);
1934 
1935 ext:
1936 	if (obj) uffs_PutObject(obj);
1937 	if (new_obj) {
1938 		do_ReleaseObjectResource(new_obj);
1939 		uffs_PutObject(new_obj);
1940 	}
1941 
1942 	return ret;
1943 }
1944 
1945