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