comparison gsm-fw/services/ffs/core.c @ 211:847e2585a0f2

gsm-fw/services/ffs: core.c integrated
author Michael Spacefalcon <msokolov@ivan.Harhan.ORG>
date Thu, 26 Dec 2013 07:23:38 +0000
parents
children 2beb88a3d528
comparison
equal deleted inserted replaced
210:1d87b335fc50 211:847e2585a0f2
1 /******************************************************************************
2 * Flash File System (ffs)
3 * Idea, design and coding by Mads Meisner-Jensen, mmj@ti.com
4 *
5 * FFS core functions (not public)
6 *
7 * $Id: core.c 1.156.1.13.1.1.1.50 Thu, 08 Jan 2004 15:05:23 +0100 tsj $
8 *
9 ******************************************************************************/
10
11 #include "ffs.h"
12 #include "core.h"
13 #include "drv.h"
14 #include "ffstrace.h"
15 #include "tmffs.h"
16 #include <string.h>
17 #include <limits.h>
18
19 /******************************************************************************
20 * Globals
21 ******************************************************************************/
22
23 struct fs_s fs;
24 struct block_stat_s bstat[FFS_BLOCKS_MAX];
25
26 struct ffs_stats_s stats;
27
28 // The following line is automatically expanded by the revision control
29 // system to make a unique ffs revision. The revision can be retrieved by
30 // ffs_query().
31
32 //$Format: "static const uint16 ffs_revision = ($ProjectMajorVersion$<<12)|(0x$ProjectMinorVersion$);"$
33 static const uint16 ffs_revision = (5<<12)|(0x56);
34
35
36 /******************************************************************************
37 * Main Functions
38 ******************************************************************************/
39
40 // Create a new ffs object (object type is undefined)
41 iref_t object_create(const char *name, const char *buf, int size, iref_t dir)
42 {
43 iref_t i;
44 struct inode_s *ip;
45 int realsize, namelength;
46 offset_t offset;
47 char *dataaddr;
48 char is_journal;
49 char name_copy[FFS_FILENAME_MAX + 1]; // NOTEME: make dynamic?
50
51 ttw(ttr(TTrObj, "ocr(%s){" NL, name));
52 tw(tr(TR_BEGIN, TrObject, "object_create('%s', ?, %d, %d) {\n",
53 name, size, dir));
54
55 // NOTEME: special case just for format()!?
56 if (dir == 0)
57 namelength = ffs_strlen(name);
58 else
59 namelength = is_filename(name);
60
61 if (namelength < 0) {
62 tw(tr(TR_END, TrObject, "} %d\n", namelength));
63 ttw(ttr(TTrObj, "} %d" NL, namelength));
64 return namelength;
65 }
66
67 is_journal = (name[0] == '.' && ffs_strcmp(name, FFS_JOURNAL_NAME) == 0);
68 if (is_journal)
69 tw(tr(TR_FUNC, TrObject, "Journal file creation!\n"));
70 else
71 if (buf == NULL && size > 0) {
72 tw(tr(TR_END, TrObject, "} %d\n", EFFS_INVALID));
73 ttw(ttr(TTrObj, "} %d" NL, EFFS_INVALID));
74 return EFFS_INVALID;
75 }
76
77 // We don't write the data null_terminator if no data exists
78 realsize = namelength + 1 + size + (size > 0 ? 1 : 0);
79 fs.journal.size = realsize = atomalign(realsize);
80
81 // We save the diri in the ram journal because this will be updated if
82 // chunk_alloc trigger a data_reclaim
83 fs.journal.diri = dir;
84
85 // We have to make a local copy of name because name can be destroyed if
86 // it points into an object that is relocated by an ffs_data_reclaim.
87 memcpy(name_copy, name, ffs_strlen(name) + 1);
88
89 if ((i = chunk_alloc(realsize, is_journal, &offset)) < 0)
90 return i;
91
92 ip = inode_addr(i);
93
94 // Write filename including null-terminator
95 ffsdrv.write(addr2name(offset2addr(offset)), name_copy, namelength + 1);
96
97 // Write data and null terminator. We null-terminate the data block,
98 // such that blocks_fsck() can determine the amount of used data block
99 // space correctly. Note that we don't write null-terminator for objects
100 // with no data, e.g. empty files and directories.
101 if (size > 0) {
102 dataaddr = addr2name(offset2addr(offset)) + namelength + 1;
103 // Do NOT write data if we are creating the journal file --- it must
104 // be created as empty!
105 if (!is_journal)
106 ffsdrv.write(dataaddr, buf, size);
107 ffsdrv_write_byte(dataaddr + size, 0);
108 }
109
110 // Insert object in parent directory if this is not the root dir
111 if (dir != 0)
112 fs.journal.diri = dir_traverse(fs.journal.diri, 0);
113 else
114 fs.journal.diri = 0;
115
116 tw(tr(TR_END, TrObject, "} %d\n", i));
117 ttw(ttr(TTrObj, "} %d" NL,i));
118
119 return i;
120 }
121
122 int file_read(const char *name, void *addr, int size)
123 {
124 int size_read, object_size, total_read = 0;
125 iref_t i, dir;
126 char *not_used;
127 fd_t fdi;
128
129 if (size < 0)
130 return EFFS_INVALID;
131
132 if ((i = object_lookup(name, &not_used, &dir)) < 0)
133 return i;
134
135 if ((fdi = get_fdi(i)) >= 0)
136 if (is_open_option(fs.fd[fdi].options, FFS_O_WRONLY))
137 return EFFS_LOCKED;
138
139 object_size = object_datasize(i);
140
141 do {
142 size_read = segment_read(i, (char*)addr + total_read,
143 size - total_read, 0);
144 total_read += size_read;
145 } while ((i = segment_next(i)) != 0 && size > total_read);
146
147 // Did we read the comlete object?
148 if (object_size > size)
149 return EFFS_FILETOOBIG;
150
151 return total_read; // number of bytes read
152 }
153
154
155 int stream_read(fd_t fdi, void *src, int size)
156 {
157 int offset, size_read = 0, copied = 0;
158 iref_t i;
159
160 if (!is_fd_valid(fdi))
161 return EFFS_BADFD;
162
163 if (!is_open_option(fs.fd[fdi].options, FFS_O_RDONLY))
164 return EFFS_INVALID;
165
166 if (src == NULL || size < 0)
167 return EFFS_INVALID;
168
169 // NOTEME: do this in another way?
170 // No data to read because fp is ad eof.
171 if (fs.fd[fdi].fp >= fs.fd[fdi].size) {
172 tw(tr(TR_FUNC, TrObject, "eof(no data read)\n"));
173 return 0;
174 }
175
176 segfile_seek(fs.fd[fdi].seghead, fs.fd[fdi].fp, &i, &offset);
177
178 // Read data from chunks or buffer until all data is read or eof is reach.
179 do {
180 if (is_offset_in_buf(fs.fd[fdi].fp, fdi)) {
181 offset = fs.fd[fdi].fp - fs.fd[fdi].wfp;
182 size_read = size - copied; // requested data that is left
183 // Saturate size to max left in buf or max left to eof
184 if (size_read > (fs.chunk_size_max - offset))
185 size_read = fs.chunk_size_max - offset;
186 if (size_read > (fs.fd[fdi].size - fs.fd[fdi].fp))
187 size_read = fs.fd[fdi].size - fs.fd[fdi].fp;
188
189 memcpy((char*)src + copied, fs.fd[fdi].buf + offset, size_read);
190 }
191 else {
192 // Data is only in the chunk
193 size_read = segment_read(i, (char*) src + copied,
194 size - copied, offset);
195 }
196
197 offset = 0;
198 fs.fd[fdi].fp += size_read;
199 copied += size_read;
200
201 if ((i = segment_next(i)) < 0)
202 return i;
203
204 } while (copied != size && fs.fd[fdi].fp < fs.fd[fdi].size);
205
206 if (copied == size) {
207 tw(tr(TR_FUNC, TrObject, "All requested data has been read\n"));
208 }
209 if (fs.fd[fdi].fp >= fs.fd[fdi].size) {
210 tw(tr(TR_FUNC, TrObject, "eof\n"));
211 }
212
213 return copied; // number of bytes read
214 }
215
216
217 int object_read(const char *name, char *buf, int size, int linkflag)
218 {
219 iref_t i;
220 struct inode_s *ip;
221 struct xstat_s stat;
222 char *p;
223
224 tw(tr(TR_BEGIN, TrObject, "object_read('%s', 0x%x, %d, %d) {\n",
225 name, buf, size, linkflag));
226
227 if (buf == NULL || size < 0) {
228 tw(tr(TR_END, TrObject, "} %d\n", EFFS_INVALID));
229 return EFFS_INVALID;
230 }
231
232 i = object_stat(name, &stat, linkflag, 0, 0);
233 if (i < 0) {
234 tw(tr(TR_END, TrObject, "} %d\n", EFFS_NOTFOUND));
235 return i;
236 }
237
238 ip = inode_addr(i);
239
240 if (stat.size > size) {
241 tw(tr(TR_END, TrObject, "} %d\n", EFFS_FILETOOBIG));
242 return EFFS_FILETOOBIG;
243 }
244
245 // return error if called as readlink() and object is not a link
246 if (!is_object(ip, OT_LINK) && linkflag) {
247 tw(tr(TR_END, TrObject, "} %d\n", EFFS_INVALID));
248 return EFFS_INVALID;
249 }
250
251 // Even though the ffs architecture allows to have data in directory
252 // objects, we don't want to complicate matters, so we return an error
253 if (is_object(ip, OT_DIR) && !(fs.flags & FS_DIR_DATA)) {
254 tw(tr(TR_END, TrObject, "} %d\n", EFFS_NOTAFILE));
255 return EFFS_NOTAFILE;
256 }
257
258 p = offset2addr(location2offset(ip->location));
259 size = stat.size;
260
261 p = addr2data(p, ip);
262
263 // Copy data. NOTEME: Should be optimized!
264 while (size--)
265 *buf++ = *p++;
266
267 tw(tr(TR_END, TrObject, "} %d\n", stat.size));
268 return stat.size;
269 }
270
271
272 // Convert an object data addres to pure data address
273 char *addr2data(const char *addr, const struct inode_s *ip)
274 {
275 // OT_SEGMENT is pure data so it do not have any name to skip
276 if (!is_object(ip, OT_SEGMENT)) {
277 while (*addr++)
278 ;
279 }
280
281 return (char *) addr;
282 }
283
284 // Calculate exact size of file data; without filename and null terminator
285 // and without data null terminator and succeeding alignment padding.
286 // NOTEME: Does this also work for empty files and directories?
287 int object_datasize(iref_t i)
288 {
289 iref_t not_used;
290 return segfile_seek(i, INT_MAX, &not_used, 0);
291 }
292
293 iref_t object_stat(const char *name, struct xstat_s *stat,
294 int linkflag, int fdi, int extended)
295 {
296 iref_t i;
297 fd_t other_fdi;
298 struct inode_s *ip;
299
300 tw(tr(TR_BEGIN, TrObject, "object_stat('%s', ?, %x, %d, %d) {\n",
301 name, linkflag, fdi, extended));
302
303 if (stat == NULL) {
304 tw(tr(TR_END, TrObject, "} %d\n", EFFS_INVALID));
305 return EFFS_INVALID;
306 }
307
308 if (linkflag)
309 i = object_lookup_once(name, 0, 0);
310 else if (name == 0) {
311 fdi -= FFS_FD_OFFSET;
312 if (!is_fd_valid(fdi)) {
313 tw(tr(TR_END, TrObject, "} %d\n", EFFS_BADFD));
314 return EFFS_BADFD;
315 }
316 i = fs.fd[fdi].seghead;
317 }
318 else
319 i = object_lookup(name, 0, 0);
320
321 if (i > 0) {
322 ip = inode_addr(i);
323 stat->type = ip->flags & OT_MASK;;
324 stat->flags = ~ip->flags & OF_MASK;
325 stat->inode = i;
326
327 // If the file is open so get the size from the file descriptor
328 if ((other_fdi = get_fdi(i)) >= 0) {
329 if (i == fs.fd[other_fdi].seghead) {
330 stat->size = fs.fd[other_fdi].size;
331 }
332 }
333
334 else
335 stat->size = object_datasize(i);
336
337 if (extended) {
338 stat->location = ip->location;
339 stat->block = offset2block(location2offset(stat->location));
340 stat->space = ip->size;
341 while ((i = segment_next(i)) > 0) {
342 ip = inode_addr(i);
343 stat->space += ip->size;
344 }
345 stat->reserved = 0;
346 stat->sequence = ip->sequence;
347 stat->updates = ip->updates;
348 }
349 }
350
351 tw(tr(TR_END, TrObject, "} %d\n", i));
352
353 return i;
354 }
355
356
357 /******************************************************************************
358 * Remove and Rename
359 ******************************************************************************/
360
361 // Delete a ffs object
362 effs_t object_remove(iref_t i)
363 {
364 struct inode_s *ip = inode_addr(i);
365 iref_t entries;
366
367 tw(tr(TR_BEGIN, TrObject, "object_remove(%d) {\n", i));
368
369 // if object is a dir, ensure it is empty
370 if (is_object(ip, OT_DIR)) {
371 dir_traverse(-i, &entries);
372 if (entries) {
373 tw(tr(TR_END, TrObject, "} %d\n", EFFS_DIRNOTEMPTY));
374 return EFFS_DIRNOTEMPTY;
375 }
376 }
377
378 // We don't actually journal deletions, this is why we call
379 // journal_commit() instead of journal_end(). We have to set
380 // journal.location to something else, otherwise journal_commit() will
381 // not discount the number of bytes lost by this delete.
382 if (is_object(ip, OT_DIR)) {
383 journal_begin(i);
384 fs.journal.location = 0;
385 journal_commit(0);
386 }
387 else {
388 // NOTE: This is not nice if we get a break down however the
389 // remaning chunks will be removed later by a block reclaim.
390 do {
391 journal_begin(i);
392 fs.journal.location = 0;
393 journal_commit(0);
394 } while ((i = segment_next(i)) != 0);
395 }
396
397 tw(tr(TR_END, TrObject, "} %d\n", EFFS_OK));
398
399 return EFFS_OK;
400 }
401
402 // Rename an object. <newname> is the new name.
403 iref_t object_rename(iref_t oldi, const char *newname, iref_t newdir)
404 {
405 iref_t newi;
406 struct inode_s *oldip;
407 char *olddata;
408 int oldsize, namelength, realsize, offset;
409
410 tw(tr(TR_BEGIN, TrObject, "object_rename(%d, '%s', %d) {\n",
411 oldi, newname, newdir));
412
413 oldip = inode_addr(oldi);
414 oldsize = segment_datasize(oldip);
415
416 // Make sure that there is enough space to make the rename without
417 // object_create() trigger a data_reclaim() (awoid relocate oldi/data
418 // source)
419 namelength = is_filename(newname);
420 realsize = namelength + 1 + oldsize + (oldsize > 0 ? 1 : 0);
421 realsize = atomalign(realsize);
422
423 // Save newdir in fs.xx because it will be updated if it is relocated.
424 fs.i_backup = newdir;
425
426 if ((offset = data_prealloc(realsize)) <= 0)
427 return EFFS_NOSPACE;
428
429 // Use fs.journal.oldi because i would have been updated if
430 // data_reclaim() relocate oldi
431 oldip = inode_addr(fs.journal.oldi);
432 olddata = offset2addr(location2offset(oldip->location));
433 olddata = addr2data(olddata, oldip);
434
435 newi = object_create(newname, olddata, oldsize, fs.i_backup);
436
437 tw(tr(TR_END, TrObject, "} %d\n", newi));
438 return newi;
439 }
440
441
442 /******************************************************************************
443 * Object Lookup
444 ******************************************************************************/
445
446 // We can *not* use global variables, only local --- we must be re-entrant!
447
448 #if 0
449
450 // NEW CODE!
451
452 iref_t ffs_object_lookup_do(const char **path, iref_t *dir, int readlink);
453
454 iref_t ffs_object_lookup_once(const char *path, char **leaf, iref_t *dir)
455 {
456 iref_t i, mydir;
457 const char *mypath;
458
459 tw(tr(TR_BEGIN, TrLookup, "object_lookup_once('%s', ?, ?) {\n", path));
460 ttw(ttr(TTrInode, "olu(%s){" NL, path));
461
462 mypath = path;
463 mydir = 0;
464 i = object_lookup_do(&mypath, &mydir, 0);
465 if (leaf) *leaf = (char *) mypath;
466 if (dir) *dir = mydir;
467
468 tw(tr(TR_END, TrLookup, "} (%d, '%s') %d\n",
469 (dir ? *dir : 0), (leaf ? *leaf : ""), i));
470 ttw(ttr(TTrInode, "} %d" NL, i));
471
472 return i;
473 }
474
475 // Lookup an object. Symlinks are followed.
476 iref_t ffs_object_lookup(const char *path, char **leaf, iref_t *dir)
477 {
478 iref_t i, mydir;
479 const char *mypath;
480
481 tw(tr(TR_BEGIN, TrLookup, "object_lookup('%s', ?, ?) {\n", path));
482 ttw(ttr(TTrInode, "olu(%s){" NL, path));
483
484 mypath = path;
485 mydir = 0;
486 i = object_lookup_do(&mypath, &mydir, 1);
487
488 if (is_object(ip, OT_LINK)) {
489 // If it is a link, we unconditionally follow it
490 mypath = offset2addr(location2offset(ip->location));
491 mypath += ffs_strlen(mypath) + 1; // point to data
492 if (*mypath == '/') {
493 mypath++;
494 depth = 0;
495 d = fs.root;
496 }
497 i = d;
498 ip = inode_addr(d);
499 }
500
501 if (leaf) *leaf = (char *) mypath;
502 if (dir) *dir = mydir;
503
504 tw(tr(TR_END, TrLookup, "} (%d, '%s') %d\n",
505 (dir ? *dir : 0), (leaf ? *leaf : ""), i));
506 ttw(ttr(TTrInode, "} %d" NL, i));
507
508 return i;
509 }
510
511 // NEW CODE!
512
513 // Ignore all occurrences of two successive slashes. Accept trailing slash
514 // in directory name.
515 iref_t ffs_object_lookup_do(const char **path, iref_t *dir, int followlink)
516 {
517 // int lookup_followed; // number of symlinks followed
518 iref_t i, j, d;
519 struct inode_s *ip;
520 const char *p, *q, *mypath = *path;
521 uint8 depth = 1;
522
523 tw(tr(TR_FUNC, TrLookup, "object_lookup_do('%s', ?, %d) {\n",
524 *path, followlink));
525
526 d = fs.root;
527 if (*mypath == '/') {
528 mypath++; // silently ignore and skip prefix slash
529 // root directory is a special case
530 if (*mypath == 0) {
531 j = d;
532 if (path) *path = mypath;
533 if (dir) *dir = 0;
534 tw(tr(TR_NULL, TrLookup, "} ('%s', %d) %d\n", mypath, 0, j));
535 return j;
536 }
537 }
538
539 // set default return value if root dir is empty (child link empty)
540 j = EFFS_NOTFOUND;
541
542 ip = inode_addr(d);
543
544 tw(tr(TR_FUNC, TrLookup, ""));
545
546 while ((i = ip->child) != (iref_t) IREF_NULL)
547 {
548 j = 0; // default to not found
549 do {
550 tw(tr(TR_NULL, TrLookup, " %d", (int) i));
551
552 p = mypath;
553 ip = inode_addr(i);
554 if (is_object_valid(ip) && !is_object(ip, OT_SEGMENT)) {
555 q = addr2name(offset2addr(location2offset(ip->location)));
556 tw(tr(TR_NULL, TrLookup, ":%s", q));
557 while (*p == *q && *p != 0 && *q != 0) {
558 p++;
559 q++;
560 }
561 if (*q == 0 && (*p == 0 || *p == '/')) {
562 j = i;
563 break;
564 }
565 }
566 } while ((i = ip->sibling) != (iref_t) IREF_NULL);
567
568 if (j == 0) {
569 // we did not find this component of the mypath. Let's see if this
570 // was the leafname component or not...
571 while (*p != 0 && *p != '/')
572 p++;
573
574 if (*p == 0)
575 // The mypath component was indeed the leafname
576 j = EFFS_NOTFOUND;
577 else
578 // The path component was not the last, so it obviously
579 // contained an object that was not a directory.
580 j = EFFS_NOTADIR;
581 break;
582 }
583
584 if (*p == '/') {
585 // if there are more path components, the object found must be a
586 // directory or a symlink...
587 if (is_object(ip, OT_LINK)) {
588 // If it is a link, we unconditionally follow it
589 mypath = offset2addr(location2offset(ip->location));
590 mypath += ffs_strlen(mypath) + 1; // point to data
591 if (*mypath == '/') {
592 mypath++;
593 depth = 0;
594 d = fs.root;
595 }
596 i = d;
597 ip = inode_addr(d);
598 }
599 else if (is_object(ip, OT_DIR)) {
600 mypath = p + 1;
601 d = i;
602 }
603 else {
604 j = EFFS_NOTADIR;
605 break;
606 }
607 if (++depth > fs.path_depth_max) {
608 j = EFFS_PATHTOODEEP;
609 break;
610 }
611
612 // if this dir inode has no children, we will leave the while
613 // loop, so we preset the return error code. NOTEME: Not
614 // strictly correct because if we still have a lot of the
615 // pathname left, it should return the error EFFS_NOTADIR
616 j = EFFS_NOTFOUND;
617
618 tw(tr(TR_NULL, TrLookup, " /"));
619
620 }
621 else {
622 // It is a fact that *p == 0. So we found the object
623 if (is_object(ip, OT_LINK) && followlink) {
624 // If object is a link, we conditionally follow it...
625 mypath = offset2addr(location2offset(ip->location));
626 mypath += ffs_strlen(mypath) + 1; // point to data
627 if (*mypath == '/') {
628 mypath++;
629 depth = 0;
630 d = fs.root;
631 i = fs.root;
632 }
633 else
634 i = d;
635 ip = inode_addr(d);
636 tw(tr(TR_NULL, TrLookup, " -%d->", d));
637 }
638 else {
639 break; // Success, we found the object!
640 }
641 }
642 }
643
644 if (path) *path = (char *) mypath;
645 if (dir) *dir = d;
646
647 tw(tr(TR_NULL, TrLookup, "} (%d, '%s') %d\n", d, mypath, j));
648
649 return j;
650 }
651
652 #else
653
654 // Lookup an object. Symlinks are followed.
655 iref_t object_lookup(const char *path, char **leaf, iref_t *dir)
656 {
657 iref_t i;
658 struct inode_s *ip;
659
660 tw(tr(TR_BEGIN, TrLookup, "object_lookup('%s', ?, ?) {\n", path));
661 ttw(ttr(TTrInode, "olu(%s){" NL, path));
662
663 i = object_lookup_once(path, leaf, dir);
664 ip = inode_addr(i);
665
666 if (i > 0 && is_object(ip, OT_LINK)) {
667 path = offset2addr(location2offset(ip->location));
668 path += ffs_strlen(path) + 1; // point to data portion
669 i = object_lookup_once(path, leaf, dir);
670
671 // Links may only point to regular files...
672 ip = inode_addr(i);
673 if (+i > 0 && !is_object(ip, OT_FILE))
674 i = EFFS_NOTAFILE;
675 }
676 else {
677 leaf = 0;
678 dir = 0;
679 }
680 tw(tr(TR_END, TrLookup, "} (%d, '%s') %d\n",
681 (dir ? *dir : 0), (leaf ? *leaf : ""), i));
682
683 ttw(ttr(TTrInode, "} %d" NL, i));
684 return i;
685 }
686
687 // Lookup an object. If object is found: Return iref of object and
688 // directory of object in <dir>. If object is not found: Return
689 // EFFS_NOTFOUND and last directory component of path in <dir> and leafname
690 // of pathname in <leaf>
691 iref_t object_lookup_once(const char *path, char **leaf, iref_t *dir)
692 {
693 iref_t i, j, d;
694 struct inode_s *ip;
695 const char *p, *q;
696 uint8 depth = 1;
697
698 tw(tr(TR_FUNC, TrLookup, "object_lookup_once('%s', ?, ?) { ", path));
699
700 if (path == NULL)
701 return EFFS_BADNAME;
702
703 d = fs.root;
704 if (*path == '/') {
705 path++; // silently ignore and skip prefix slash
706 // root directory is a special case
707 if (*path == 0) {
708 j = d;
709 if (leaf) *leaf = (char *) path;
710 if (dir) *dir = 0;
711 tw(tr(TR_NULL, TrLookup, "} ('%s', %d) %d\n", path, 0, j));
712 return j;
713 }
714 }
715 else
716 return EFFS_BADNAME;
717
718 // set default return value if root dir is completely empty
719 // (child link empty)
720 j = EFFS_NOTFOUND;
721
722 ip = inode_addr(d);
723
724 while ((i = ip->child) != (iref_t) IREF_NULL)
725 {
726 j = 0; // default to not found
727 do {
728 tw(tr(TR_NULL, TrLookup, "i%d ", (int) i));
729
730 p = path;
731 ip = inode_addr(i);
732 if (is_object_valid(ip) && !is_object(ip, OT_SEGMENT)) {
733 q = addr2name(offset2addr(location2offset(ip->location)));
734 tw(tr(TR_NULL, TrLookup, "%s ", q));
735 while (*p == *q && *p != 0 && *q != 0) {
736 p++;
737 q++;
738 }
739 if (*q == 0 && (*p == 0 || *p == '/')) {
740 j = i;
741 break;
742 }
743 }
744 } while ((i = ip->sibling) != (iref_t) IREF_NULL);
745
746
747 if (j == 0) {
748 // we did not find this component of the path. Let's
749 // see if this was the leafname component or not...
750 while (*p != 0 && *p != '/')
751 p++;
752
753 if (*p == 0)
754 // The path component was indeed the leafname
755 j = EFFS_NOTFOUND;
756 else
757 // The path component was not the last, so it
758 // obviously contained an object that was not a
759 // directory.
760 j = EFFS_NOTADIR;
761 break;
762 }
763
764 if (*p == '/') {
765 // if there are more path components, the object found
766 // must be a directory...
767 if (!is_object(ip, OT_DIR)) {
768 j = EFFS_NOTADIR;
769 break;
770 }
771 if (++depth > fs.path_depth_max) {
772 j = EFFS_PATHTOODEEP;
773 break;
774 }
775 path = p + 1;
776 d = i;
777
778 // if this dir inode has no children, we will leave the
779 // while loop, so we preset the return error code. NOTEME:
780 // Not strictly correct because if we still have a lot of
781 // the pathname left, it should return the error
782 // EFFS_NOTADIR
783 j = EFFS_NOTFOUND;
784
785 tw(tr(TR_NULL, TrLookup, "/ "));
786
787 }
788 else {
789 // It is a fact that *p == 0. So we found the object!
790 break;
791 }
792 }
793
794 if (leaf) *leaf = (char *) path;
795 if (dir) *dir = d;
796
797 tw(tr(TR_NULL, TrLookup, "} (%d, '%s') %d\n", d, path, j));
798
799 return j;
800 }
801
802 #endif
803
804
805 /******************************************************************************
806 * Directory Operations
807 ******************************************************************************/
808
809 // Open a directory, returning the iref of the directory's inode.
810 iref_t dir_open(const char *name)
811 {
812 iref_t i;
813 struct inode_s *ip;
814
815 tw(tr(TR_BEGIN, TrDirHigh, "dir_open('%s') {\n", name));
816
817 if ((i = object_lookup(name, 0, 0)) < 0) {
818 tw(tr(TR_END, TrDirHigh, "} %d\n", i));
819 return i;
820 }
821
822 ip = inode_addr(i);
823 if (!is_object(ip, OT_DIR))
824 i = EFFS_NOTADIR;
825
826 tw(tr(TR_END, TrDirHigh, "} %d\n", i));
827
828 return i;
829 }
830
831 // Return name and iref of next entry in directory <dir>. <i> is the last
832 // entry we returned from this function. In case this is the first call
833 // after the initial call to dir_open(), <i> equals <dir>.
834 iref_t dir_next(iref_t dir, iref_t i, char *name, int8 size)
835 {
836 struct inode_s *ip = inode_addr(i);
837 char *p;
838
839 tw(tr(TR_BEGIN, TrDirHigh, "dir_next(%d, %d, ?, %d) {\n", dir, i, size));
840
841 i = (i == dir ? ip->child : ip->sibling);
842
843 while (i != (iref_t) IREF_NULL) {
844 ip = inode_addr(i);
845 if (is_object_valid(ip)) {
846 p = offset2addr(location2offset(ip->location));
847 while (size-- && (*name++ = *p++))
848 ;
849 break;
850 }
851 i = ip->sibling;
852 }
853 if (i == (iref_t) IREF_NULL)
854 i = 0;
855
856 tw(tr(TR_END, TrDirHigh, "} %d\n", i));
857
858 return i;
859 }
860
861 // Traverse a directory given by inode reference <i>. If <i> is negative, it
862 // refers to the actual directory so we start by traversing the child link.
863 // Otherwise if <i> is positive, it refers to an entry within the directory
864 // and we only traverse sibling links. Returns iref of last object in
865 // directory (or negative iref of directory if the child link is empty).
866 // <entries> is number of non-deleted objects in the dir (only valid if
867 // traversed from the start, eg. with negative <i>).
868 iref_t dir_traverse(iref_t i, iref_t *entries)
869 {
870 iref_t j = 0, valid = 0, erased = 0, invalid = 0;
871 struct inode_s *ip;
872
873 tw(tr(TR_FUNC, TrDirLow, "dir_traverse(%d, ?) { ", i));
874
875 if (i < 0) {
876 // If directory's child is empty, this is a virgin directory and we
877 // return negative iref of the directory itself.
878 j = i;
879 i = -i;
880 ip = inode_addr(i);
881 i = ip->child;
882 }
883 if (i != (iref_t) IREF_NULL) {
884 do {
885 if (j == i) {
886 tw(tr(TR_NULL, TrDirLow, "LOOP! "));
887 return EFFS_SIBLINGLOOP;
888 }
889
890 j = i;
891 ip = inode_addr(j);
892
893 tw(tr(TR_NULL, TrDirLow, "%d/%x ", j, ip->flags));
894
895 if (is_object_valid(ip))
896 valid++;
897 else if (is_object(ip, OT_ERASED))
898 erased++;
899 else
900 invalid++;
901
902 } while ((i = ip->sibling) != (iref_t) IREF_NULL);
903 }
904
905 if (entries != 0)
906 *entries = valid;
907
908 tw(tr(TR_NULL, TrDirLow, "} (valid = %d, erased = %d, invalid = %d) %d\n",
909 valid, erased, invalid, j));
910
911 return j;
912 }
913
914
915 /******************************************************************************
916 * Block, Inode and Data Allocation
917 ******************************************************************************/
918
919 // Find the youngest free block. Return block index on success. If the
920 // argument <priority> is zero, this is a normal alloc and it will leave at
921 // least fs.blocks_free_min spare blocks. Otherwise, if it is non-zero, it
922 // is a privileged alloc (initiated by a reclaim operation) and it will not
923 // necessarily leave any spare blocks.
924 bref_t block_alloc(bref_t priority, uint16 flags)
925 {
926 bref_t i, b, b_min, b_max, blocks_free;
927 struct block_header_s *bhp;
928 age_t age, age_min, age_max;
929
930 tw(tr(TR_BEGIN, TrBlock, "block_alloc(%d, 0x%x) {\n", priority, flags));
931 ttw(ttr(TTrData, "ba(%d,0x%x) {" NL, priority, flags));
932
933 age_min = BLOCK_AGE_MAX;
934 age_max = 0;
935 blocks_free = 0;
936 b_min = b_max = -1;
937
938 tw(tr(TR_FUNC, TrBlock, "blocks(age): "));
939 for (i = dev.numblocks - 1; i >= 0; i--)
940 {
941 if (is_block(i, BF_IS_FREE))
942 {
943 blocks_free++;
944 bhp = (struct block_header_s *) offset2addr(dev.binfo[i].offset);
945 age = bhp->age;
946
947 tw(tr(TR_NULL, TrBlock, "%d(%d) ", i, age));
948
949 // Remember index of block found. We use '<=' and '>=' operators
950 // (instead of '<' and '>') to ensure we have both limits
951 // properly set on exit from this loop.
952 if (age <= age_min) {
953 b_min = i;
954 age_min = age;
955 }
956 if (age >= age_max) {
957 b_max = i;
958 age_max = age;
959 }
960 }
961 }
962 tw(tr(TR_NULL, TrBlock, "\n"));
963
964 // Handle age wrap around
965 b = b_min;
966 if (b_min != -1) {
967 // Either age_max really is max age, so b_min is youngest block OR
968 // age_max really is min age, so b_max is youngest block
969 b = (age_max - age_min) < 0x8000 ? b_min : b_max;
970 }
971
972 // Only privileged allocs will get the last free block
973 if (blocks_free <= fs.blocks_free_min - priority) {
974 b = -1;
975 tw(tr(TR_FUNC, TrBlock, "Only %d block(s) left, required = %d\n",
976 blocks_free, fs.blocks_free_min - priority));
977 }
978 else {
979 // Prepare/format the block for holding data/inodes
980 if (flags == BF_DATA) {
981 bstat[b].used = BHEADER_SIZE;
982 bstat[b].lost = 0;
983 bstat[b].objects = 0;
984 block_flags_write(b, BF_DATA);
985 }
986 else if (flags == BF_COPYING) {
987 // This code is used on a fresh format and when allocating a new
988 // block for reclaiming inodes
989 block_flags_write(b, BF_COPYING);
990 bstat[b].used = 0;
991 bstat[b].lost = 0;
992 bstat[b].objects = 1; // first inode to be allocated
993 }
994 else {
995 tw(tr(TR_FUNC, TrBlock, "FATAL: Bad input (flags = 0x%X)\n", flags));
996 }
997 }
998
999 tw(tr(TR_END, TrBlock, "} (%d) %d\n", blocks_free, b));
1000 ttw(ttr(TTrData, "} 0x%x" NL, b));
1001
1002 return b;
1003 }
1004
1005 // Free and schedule a block for erase.
1006 void block_free(bref_t b)
1007 {
1008 tw(tr(TR_BEGIN, TrBlock, "block_free(%d) {\n", b));
1009
1010 // mark block as invalid and schedule erasure
1011 block_flags_write(b, BF_LOST);
1012 block_reclaim(b);
1013
1014 tw(tr(TR_END, TrBlock, "}\n"));
1015 }
1016
1017 void block_flags_write(uint8 block, uint8 flags)
1018 {
1019 struct block_header_s *bhp =
1020 (struct block_header_s *) offset2addr(dev.binfo[block].offset);
1021
1022 tw(tr(TR_BEGIN, TrBlock, "block_flags_write(%d, 0x%x)\n", block, flags));
1023
1024 bstat[block].flags = BIT_SET(bstat[block].flags, flags);
1025 ffsdrv.write_halfword((uint16 *) &bhp->flags, bstat[block].flags );
1026
1027 tw(tr(TR_END, TrBlock, ""));
1028 }
1029
1030 // Allocate an inode for a new object. We use bstat[fs.inodes].objects to
1031 // start our scan for a free inode instead of starting from the first time
1032 // each time.
1033 iref_t inode_alloc(void)
1034 {
1035 iref_t i;
1036
1037 tw(tr(TR_BEGIN, TrInode, "inode_alloc() {\n"));
1038 ttw(ttr(TTrInode, "i_a() {" NL));
1039
1040 if ((i = inode_alloc_try()) == 0) {
1041 // FIXME NO we are not always of inodes, maybe dos there exist to
1042 // many objects! It will not help to reclaim the inodes in that case!
1043 tw(tr(TR_FUNC, TrInode, "NOTE: Out of free inodes...\n"));
1044 inodes_reclaim();
1045 i = inode_alloc_try();
1046 }
1047
1048 tw(tr(TR_END, TrInode, "} %d\n", i));
1049 ttw(ttr(TTrInode, "} %d" NL, i));
1050
1051 return i;
1052 }
1053
1054 iref_t inode_alloc_try(void)
1055 {
1056 iref_t i = fs.inodes_max;
1057 struct inode_s *ip;
1058
1059 // If we have not yet reached the maximum allowed number of objects,
1060 // search for next free inode...
1061 if (bstat[fs.inodes].used - bstat[fs.inodes].lost < fs.objects_max)
1062 {
1063 ip = inode_addr(bstat[fs.inodes].objects);
1064 for (i = bstat[fs.inodes].objects;
1065 i < fs.inodes_max - FFS_INODES_MARGIN; i++, ip++) {
1066 if (ip->location == FLASH_NULL32) {
1067 bstat[fs.inodes].objects = i;
1068 bstat[fs.inodes].used++;
1069 break;
1070 }
1071 }
1072 }
1073 if (i >= fs.inodes_max - FFS_INODES_MARGIN)
1074 i = 0;
1075
1076 tw(tr(TR_FUNC, TrInode, "inode_alloc_try() %d\n", i));
1077 ttw(ttr(TTrInode, "i_a_t() %d" NL, i));
1078
1079 return i;
1080 }
1081
1082 // NOTEME: Should file data be word aligned to enable faster reads and
1083 // writes in word quantities AND to be more compatible with the inherent
1084 // 16-bit access width of flash memories?
1085 offset_t data_alloc(int size)
1086 {
1087 offset_t offset = 0;
1088 bref_t b;
1089
1090 tw(tr(TR_BEGIN, TrData, "data_alloc(%d) {\n", size));
1091 ttw(ttr(TTrData, "da(%d) {" NL, size));
1092
1093 offset = data_prealloc(size);
1094
1095 // If we did allocate the space, we update bstat[]
1096 if (offset > 0) {
1097 b = offset2block(offset);
1098 bstat[b].used += size;
1099 stats.data_allocated += size; // STATS
1100 }
1101
1102 tw(tr(TR_END, TrData, "} 0x%04x\n", offset));
1103 ttw(ttr(TTrData, "} %x" NL, offset));
1104
1105 return offset;
1106 }
1107
1108 offset_t data_prealloc(int realsize)
1109 {
1110 int result, i, bytes_free;
1111 offset_t offset;
1112
1113 // Is it possible to get this amount of free space and still have enough
1114 // reserved space?
1115 ffs_query(Q_BYTES_FREE_RAW, &bytes_free);
1116 if (realsize > (bytes_free + FFS_FILENAME_MAX + dev.atomsize))
1117 return 0; // Not enough unused space
1118
1119 for (i = 0; i < dev.numblocks; i++) {
1120 if ((offset = data_alloc_try(realsize)) > 0)
1121 return offset; // Space found
1122
1123 if ((result = data_reclaim(realsize)) < 0)
1124 return 0; // Data reclaim failed!
1125 }
1126
1127 return 0; // No space found
1128 }
1129
1130 // Find free data space of size <size>. Return zero if no space available.
1131 // Note that we ensure that we always have space immediately available for a
1132 // privileged data_alloc(), e.g. a data_alloc() that allocates data space
1133 // without performing a data_reclaim(). This is important when
1134 // re-creating/re-locating the journal file.
1135 offset_t data_alloc_try(int size)
1136 {
1137 bref_t b;
1138 int free;
1139 offset_t offset_big = 0, offset_small = 0;
1140 int size_big_ok = 0, size_small_ok = 0;
1141 int size_big, size_small;
1142 int reserved;
1143
1144 tw(tr(TR_FUNC, TrData, "data_alloc_try(%d) { ", size));
1145 ttw(ttr(TTrData, "dat(%d) {" NL, size));
1146
1147 // NOTE when we alloc do we only need to have reserved space for X
1148 // number of journal files, where X is the max number of used journals
1149 // per data reclaim. The only exception is when an object_relocate has
1150 // failed thus we set reserved_space to zero.
1151 reserved = RESERVED_LOW;
1152
1153 if (fs.reserved_space < reserved)
1154 reserved = fs.reserved_space;
1155
1156 // Set size_big to the grater of the sizes and size_small to the lesser.
1157 size_big = (size > reserved ? size : reserved);
1158 size_small = (size > reserved ? reserved : size);
1159 tw(tr(TR_NULL, TrData, "(size_big, small = %d, %d) ", size_big, size_small));
1160
1161 // First search for free space in data blocks
1162 tw(tr(TR_NULL, TrData, "block:free,objects: "));
1163
1164 for (b = 0; b < dev.numblocks; b++) {
1165 if (is_block(b, BF_IS_DATA)) {
1166 free = dev.blocksize - bstat[b].used;
1167 tw(tr(TR_NULL, TrData, "%d:%d,%d ", b, free, bstat[b].objects));
1168 if (bstat[b].objects < fs.block_files_max - fs.block_files_reserved) {
1169 if (!size_big_ok && !size_small_ok &&
1170 (free >= size_big + size_small)) {
1171 size_big_ok = size_small_ok = 1;
1172 offset_big = offset_small =
1173 dev.binfo[b].offset + bstat[b].used;
1174 tw(tr(TR_NULL, TrData, "big/small_ok "));
1175 break;
1176 }
1177 else if (!size_big_ok && free >= size_big) {
1178 size_big_ok = 1;
1179 offset_big = dev.binfo[b].offset + bstat[b].used;
1180 tw(tr(TR_NULL, TrData, "big_ok "));
1181 }
1182 else if (!size_small_ok && free >= size_small) {
1183 size_small_ok = 1;
1184 offset_small = dev.binfo[b].offset + bstat[b].used;
1185 tw(tr(TR_NULL, TrData, "small_ok "));
1186 }
1187 }
1188 }
1189 if (size_small_ok && size_big_ok)
1190 break;
1191 }
1192
1193 if (size_big_ok && size_small_ok)
1194 offset_big = (size > reserved ? offset_big : offset_small);
1195 else
1196 offset_big = 0;
1197
1198 tw(tr(TR_NULL, TrData, "} 0x%x\n", offset_big));
1199 ttw(ttr(TTrData, "} %x " NL, offset_big));
1200
1201 return offset_big;
1202 }
1203
1204 offset_t data_reserved_alloc(int size)
1205 {
1206 bref_t b;
1207 offset_t offset = 0;
1208 int free;
1209
1210 tw(tr(TR_BEGIN, TrData, "data_reserved_alloc(%d) {\n", size));
1211 ttw(ttr(TTrData, "dra(%d) {" NL, size));
1212
1213 tw(tr(TR_NULL, TrData, "block:free,objects: "));
1214 for (b = 0; b < dev.numblocks; b++) {
1215 if (is_block(b, BF_IS_DATA)) {
1216 free = dev.blocksize - bstat[b].used;
1217 tw(tr(TR_NULL, TrData, "%d:%d,%d ", b, free, bstat[b].objects));
1218 if (free >= size) {
1219 offset = dev.binfo[b].offset + bstat[b].used;
1220 break;
1221 }
1222 }
1223 }
1224
1225 // If we did allocate the space, we update bstat[]
1226 if (offset != 0) {
1227 b = offset2block(offset);
1228 bstat[b].used += size;
1229 stats.data_allocated += size; // STATS
1230 }
1231
1232 tw(tr(TR_END, TrData, "} 0x%04x\n", offset));
1233 ttw(ttr(TTrData, "} %x" NL, offset));
1234
1235 return offset;
1236 }
1237
1238
1239 iref_t chunk_alloc(int realsize, int is_journal, offset_t *offset)
1240 {
1241 iref_t i;
1242
1243 if (realsize < 0)
1244 return EFFS_INVALID;
1245
1246 // Have we reached objects_max? We make a similar test in
1247 // inode_alloc_try(), however we need to do it here or else we risk to start
1248 // a data_reclaim we not can finish.
1249 if (bstat[fs.inodes].used - bstat[fs.inodes].lost >= fs.objects_max) {
1250 tw(tr(TR_END, TrObject, "} %d\n", EFFS_FSFULL));
1251 ttw(ttr(TTrObj, "} %d" NL, EFFS_FSFULL));
1252 return EFFS_FSFULL;
1253 }
1254
1255 // Allocate space for the object name (and object data)
1256 if (is_journal)
1257 *offset = data_reserved_alloc(realsize);
1258 else
1259 *offset = data_alloc(realsize);
1260
1261 if (*offset == 0) {
1262 tw(tr(TR_END, TrObject, "} %d\n", EFFS_NOSPACE));
1263 ttw(ttr(TTrObj, "} %d" NL, EFFS_NOSPACE));
1264 return EFFS_NOSPACE;
1265 }
1266 fs.journal.location = offset2location(*offset);
1267
1268 // Allocate an inode for the object
1269 i = fs.journal.i = inode_alloc();
1270 if (i == 0) {
1271 tw(tr(TR_END, TrObject, "} %d\n", EFFS_FSFULL));
1272 ttw(ttr(TTrObj, "} %d" NL, EFFS_FSFULL));
1273 return EFFS_FSFULL;
1274 }
1275
1276 return i;
1277 }
1278
1279 /******************************************************************************
1280 * query and fcontrol
1281 ******************************************************************************/
1282
1283 #if 0
1284 extern uint16 ffs_flash_device;
1285 extern uint16 ffs_flash_manufact;
1286 #endif
1287
1288 effs_t object_control(iref_t i, int8 action, int value)
1289 {
1290 effs_t error = EFFS_OK;
1291
1292 tw(tr(TR_BEGIN, TrOther, "object_control(%d, %d, 0x%x) {\n",
1293 i, action, value));
1294 ttw(ttr(TTrApi, "obj_control(%d,%d,0x%x)" NL, i, action, value));
1295
1296 switch (action) {
1297 case OC_FLAGS:
1298 // Set/clear object flags. Attempting to modify the "/dev/ffs"
1299 // object (i = 0) or any non-defined flags is an invalid operation.
1300 if (i <= 0 || value & ~OF_ALL) {
1301 error = EFFS_INVALID;
1302 }
1303 else {
1304 // there are two cases; either we only set bits in the flags.
1305 // This is simple, as we just have to update the flags byte. The
1306 // other case is harder because we have to clear bits and for
1307 // this we have to copy the old inode to a new inode, setting
1308 // the flags appropriately. For now we always just allocate a
1309 // new inode and set the flags according to the <value>
1310 // argument.
1311 journal_begin(i);
1312 fs.journal.flags |= OF_MASK; // reset all flags
1313 fs.journal.flags = BIT_SET(fs.journal.flags, value);
1314 if ((fs.journal.i = inode_alloc()) == 0)
1315 error = EFFS_FSFULL;
1316 else {
1317 fs.journal.diri = dir_traverse(fs.journal.diri, 0);
1318 journal_end(0);
1319 }
1320 }
1321 break;
1322
1323 case OC_FS_FLAGS: fs.flags = value; break;
1324 #if 0
1325 case OC_DEV_MANUFACT: ffs_flash_manufact = value; break;
1326 case OC_DEV_DEVICE: ffs_flash_device = value; break;
1327 #endif
1328 case OC_FS_TESTFLAGS: fs.testflags = value; break;
1329 case OC_DEBUG_0:
1330 case OC_DEBUG_1:
1331 case OC_DEBUG_2:
1332 case OC_DEBUG_3: fs.debug[action - OC_DEBUG_FIRST] = value; break;
1333 case OC_TRACE_INIT:
1334 #if (TARGET == 1)
1335 ttr_init(value);
1336 #endif
1337 break;
1338 default:
1339 error = EFFS_INVALID;
1340 }
1341
1342 tw(tr(TR_END, TrOther, "} %d\n", error));
1343
1344 return error;
1345 }
1346
1347 extern int tmffs_bufsize(void); // used by ffs_query()
1348 extern unsigned char *tmffs_bufaddr(void); // used by ffs_query()
1349
1350 #if (TARGET == 1)
1351 // request_id_last is only used in TARGET not to any use on the PC side
1352 extern req_id_t request_id_last; // from task.c
1353 #else
1354 req_id_t request_id_last;
1355 #endif
1356
1357 // If tmffs not is represented we define a dummy tm version
1358 #ifndef FFS_TM_VERSION
1359 #define FFS_TM_VERSION ((uint16) 0x0BAD)
1360 #endif
1361
1362 effs_t ffs_query(int8 query, void *p)
1363 {
1364 tw(tr(TR_FUNC, TrOther, "query(%d) (?)\n", query));
1365
1366 if (p == NULL)
1367 return EFFS_INVALID;
1368
1369 switch (query)
1370 {
1371 case Q_BYTES_FREE:
1372 case Q_BYTES_USED:
1373 case Q_BYTES_LOST:
1374 case Q_BYTES_MAX:
1375 case Q_OBJECTS_TOTAL:
1376 case Q_BLOCKS_FREE:
1377 case Q_BYTES_FREE_RAW:
1378 {
1379 bref_t b;
1380 bref_t blocks_free = 0;
1381 iref_t objects = 0;
1382 offset_t max, used = 0, lost = 0;
1383 struct block_stat_s *bp;
1384
1385 // Don't count free blocks, inode block, block header and reserved space.
1386 max = (dev.numblocks - fs.blocks_free_min - 1) *
1387 (dev.blocksize - BHEADER_SIZE) - fs.reserved_space;
1388
1389 // Furthermore don't count the ovewrhead from each chunk (alignment)
1390 // NOTE: If we call query while FFS not is formatted there is a risk
1391 // of deviding with zero!
1392 if (fs.chunk_size_max > 0)
1393 max -= ((max / fs.chunk_size_max + 1) * dev.atomsize);
1394
1395 for (b = 0, bp = &bstat[0]; b < dev.numblocks; b++, bp++) {
1396 if (is_block(b, BF_IS_FREE))
1397 blocks_free++;
1398 if (is_block(b, BF_IS_DATA)) {
1399 objects += bp->objects;
1400 used += bp->used;
1401 lost += bp->lost;
1402 }
1403 }
1404
1405 switch (query) {
1406 case Q_BYTES_FREE: *(uint32*)p = max - (used - lost) - FFS_FILENAME_MAX;
1407 break;
1408 case Q_BYTES_FREE_RAW:*(uint32*)p = max - (used - lost); break;
1409 case Q_BYTES_USED: *(uint32*)p = used; break;
1410 case Q_BYTES_LOST: *(uint32*)p = lost; break;
1411 case Q_BYTES_MAX: *(uint32*)p = max; break;
1412 case Q_OBJECTS_TOTAL: *(uint16*)p = objects; break;
1413 case Q_BLOCKS_FREE: *(uint16*)p = blocks_free; break;
1414 }
1415 break;
1416 }
1417
1418 case Q_TM_BUFADDR: *(uint32*)p = (uint32) tmffs_bufaddr(); break;
1419 case Q_TM_BUFSIZE: *(uint32*)p = tmffs_bufsize(); break;
1420 case Q_DEV_BASE: *(uint32*)p = (uint32) dev.base; break;
1421
1422 // FFS versions
1423 case Q_FFS_API_VERSION: *(uint16*)p = FFS_API_VERSION; break;
1424 case Q_FFS_DRV_VERSION: *(uint16*)p = FFS_DRV_VERSION; break;
1425 case Q_FFS_REVISION: *(uint16*)p = ffs_revision; break;
1426 case Q_FFS_FORMAT_WRITE: *(uint16*)p = FFS_FORMAT_VERSION; break;
1427 case Q_FFS_FORMAT_READ: *(uint16*)p = fs.format; break;
1428 case Q_FFS_LASTERROR: *(int16*)p = fs.initerror; break;
1429 case Q_FFS_TM_VERSION: *(int16*)p = FFS_TM_VERSION; break;
1430
1431 // File system queries
1432 case Q_FILENAME_MAX: *(uint16*)p = fs.filename_max; break;
1433 case Q_PATH_DEPTH_MAX: *(uint16*)p = fs.path_depth_max; break;
1434
1435 case Q_OBJECTS_FREE: *(uint16*)p = fs.objects_max -
1436 (bstat[fs.inodes].used -
1437 bstat[fs.inodes].lost); break;
1438 case Q_INODES_USED: *(uint16*)p = bstat[fs.inodes].used; break;
1439 case Q_INODES_LOST: *(uint16*)p = bstat[fs.inodes].lost; break;
1440 case Q_OBJECTS_MAX: *(uint16*)p = fs.objects_max; break;
1441
1442 case Q_INODES_MAX: *(uint16*)p = fs.inodes_max; break;
1443 case Q_CHUNK_SIZE_MAX: *(uint16*)p = fs.chunk_size_max; break;
1444
1445 // File descriptor queris
1446 case Q_FD_BUF_SIZE: *(uint32*)p = fs.fd_buf_size; break;
1447 case Q_FD_MAX: *(uint16*)p = fs.fd_max; break;
1448
1449 // device queries
1450 case Q_DEV_MANUFACTURER: *(uint16*)p = dev.manufact; break;
1451 case Q_DEV_DEVICE: *(uint16*)p = dev.device; break;
1452 case Q_DEV_BLOCKS: *(uint16*)p = dev.numblocks; break;
1453 case Q_DEV_ATOMSIZE: *(uint16*)p = dev.atomsize; break;
1454 case Q_DEV_DRIVER: *(uint16*)p = dev.driver; break;
1455
1456 // Miscellaneous/Internal
1457 case Q_BLOCKS_FREE_MIN: *(uint16*)p = fs.blocks_free_min; break;
1458 case Q_LOST_HIGH: *(uint16*)p = fs.lost_threshold; break;
1459
1460 // Debug queries
1461 case Q_FS_FLAGS: *(uint16*)p = fs.flags; break;
1462 case Q_FS_INODES: *(uint16*)p = fs.inodes; break;
1463 case Q_FS_ROOT: *(uint16*)p = fs.root; break;
1464
1465 case Q_STATS_DRECLAIMS: *(uint32*)p = stats.drec.most_lost +
1466 stats.drec.most_unused +
1467 stats.drec.youngest; break;
1468 case Q_STATS_IRECLAIMS: *(uint32*)p = stats.irec.num; break;
1469 case Q_STATS_DATA_RECLAIMED: *(uint32*)p = stats.drec.valid[0] +
1470 stats.drec.lost[0]; break;
1471 case Q_STATS_INODES_RECLAIMED: *(uint32*)p = stats.irec.valid + stats.irec.lost;
1472 break;
1473 case Q_STATS_DATA_ALLOCATED: *(uint32*)p = stats.data_allocated; break;
1474 case Q_REQUEST_ID_LAST: *(uint32*)p = request_id_last; break;
1475
1476 default:
1477 if (query >= Q_BSTAT && (query - Q_BSTAT) < dev.numblocks)
1478 {
1479 struct block_header_s *bhp;
1480 uint32 *myp = p;
1481
1482 query -= Q_BSTAT;
1483 bhp = (struct block_header_s *) offset2addr(dev.binfo[query].offset);
1484
1485 *myp++ = bstat[query].used;
1486 *myp++ = bstat[query].lost;
1487 // If we are in READ mode or this block is not lost, we can
1488 // safely read the age. Otherwise it is maybe currently erasing
1489 // and thus we cannot read the age.
1490 // NOTEME: Should this not have been handled by a driver function?
1491 if (dev.state == DEV_READ || !is_block_flag(query, BF_LOST))
1492 *myp++ = (bhp->age << 16) | bstat[query].flags;
1493 else
1494 *myp++ = ( 0xFFFE << 16) | bstat[query].flags;
1495 *myp++ = bstat[query].objects;
1496 }
1497 else if (query >= Q_DEBUG_FIRST && query < Q_DEBUG_LAST) {
1498 *(uint32*)p = fs.debug[query - Q_DEBUG_FIRST];
1499 }
1500 else
1501 return EFFS_INVALID;
1502 }
1503
1504 return EFFS_OK;
1505 }
1506
1507
1508 /******************************************************************************
1509 * Miscellaneous Helper Functions
1510 ******************************************************************************/
1511
1512 // Check if an object is read-only. Note that the root inode is always
1513 // readonly, no matter what! Returns error or original <i>.
1514 iref_t is_readonly(iref_t i, const char *path)
1515 {
1516 struct inode_s *ip = inode_addr(i);
1517
1518 tw(tr(TR_FUNC, TrObject, "is_readonly(%d, '%s') ", i, path));
1519
1520 if (i == fs.root || i == fs.ijournal ||
1521 (IS_BIT_SET(ip->flags, OF_READONLY) && !ffs_is_modifiable(path)))
1522 i = EFFS_ACCESS;
1523
1524 tw(tr(TR_NULL, TrObject, "(0x%X) %d\n", ip->flags, i));
1525
1526 return i;
1527 }
1528
1529
1530 // Check if filename is valid. Return EFFS_BADNAME if name contains
1531 // invalid chars. Return RFFS_NAMETOOLONG if name is too
1532 // long. Otherwise return filename length.
1533 effs_t is_filename(const char *s)
1534 {
1535 char *p = (char *) s;
1536 int n = 0;
1537
1538 while ( (*s >= 'a' && *s <= 'z') ||
1539 (*s >= 'A' && *s <= 'Z') ||
1540 (*s >= '0' && *s <= '9') ||
1541 *s == '.' ||
1542 *s == ',' ||
1543 *s == '_' ||
1544 *s == '-' ||
1545 *s == '+' ||
1546 *s == '%' ||
1547 *s == '$' ||
1548 *s == '#' )
1549 {
1550 s++;
1551 }
1552
1553 if (*s != 0)
1554 n = EFFS_BADNAME; // invalid file name character found
1555 else {
1556 n = s - p;
1557 if (n > fs.filename_max)
1558 n = EFFS_NAMETOOLONG;
1559 if (n == 0)
1560 n = EFFS_BADNAME;
1561 }
1562
1563 tw(tr(TR_FUNC, TrUtil, "is_filename('%s') %d\n", p, n));
1564
1565 return n;
1566 }
1567
1568 int ffs_strlen(const char *s)
1569 {
1570 const char *p = s;
1571
1572 while (*p++)
1573 ;
1574
1575 tw(tr(TR_FUNC, TrUtil, "strlen('%s') %d\n", s, p-s-1));
1576
1577 return p-s-1;
1578 }
1579
1580 // Return zero if strings are equal, otherwise return non-zero.
1581 int ffs_strcmp(const char *s, const char *p)
1582 {
1583 int8 n = 1;
1584
1585 tw(tr(TR_FUNC, TrUtil, "strcmp('%s', '%s') ", s, p));
1586
1587 while (*s == *p && *p != 0) {
1588 s++;
1589 p++;
1590 }
1591 if (*s == *p)
1592 n = 0;
1593
1594 tw(tr(TR_NULL, TrUtil, "(%d)\n", n));
1595
1596 return n;
1597 }
1598
1599 // Note: rename function? like get_fdi..
1600 fd_t get_fdi(iref_t i)
1601 {
1602 int j;
1603
1604 tw(tr(TR_FUNC, TrUtil, "get_fdi(%d)\n", i));
1605
1606 if (i > 0) {
1607 for (j = 0; j < fs.fd_max; j++) {
1608 if (i == fs.fd[j].seghead) {
1609 return j; // Return fdi without offset
1610 }
1611 }
1612 }
1613 return -1;
1614 }
1615
1616
1617 effs_t is_fd_valid(fd_t fdi)
1618 {
1619 if (fdi >= fs.fd_max || fdi < 0 || fs.fd[fdi].options == 0)
1620 return 0; // Not valid!
1621 return 1;
1622 }
1623
1624 effs_t is_offset_in_buf(int offset, fd_t fdi)
1625 {
1626 if (fs.fd[fdi].dirty == 1)
1627 if (offset >= fs.fd[fdi].wfp &&
1628 offset < fs.fd[fdi].wfp + fs.chunk_size_max)
1629 return 1;
1630 return 0;
1631 }
1632
1633 /******************************************************************************
1634 * Chunk Operations
1635 ******************************************************************************/
1636
1637 iref_t segment_create(const char *buf, int size, iref_t dir)
1638 {
1639 iref_t i;
1640 struct inode_s *ip;
1641 int realsize;
1642 offset_t offset;
1643 char *dataaddr;
1644
1645 ttw(ttr(TTrObj, "segc(%d, %d){" NL, size, dir));
1646 tw(tr(TR_BEGIN, TrObject, "segment_create( ?, %d, %d) {\n", size, dir));
1647
1648 fs.journal.size = realsize = atomalign(size + 1);
1649
1650 // Init journal.diri before chunk_alloc() because it might trigger a
1651 // data_reclaim() which can relocate the dir inode
1652 fs.journal.diri = dir;
1653
1654 if ((i = chunk_alloc(realsize, 0, &offset)) < 0)
1655 return i;
1656
1657 ip = inode_addr(i);
1658 dataaddr = offset2addr(offset);
1659
1660 // Write data and null terminator. We null-terminate the data block,
1661 // such that blocks_fsck() can determine the amount of used data block
1662 // space correctly.
1663 ffsdrv.write(dataaddr, buf, size);
1664 dataaddr += size;
1665 ffsdrv_write_byte(dataaddr, 0);
1666
1667 // Segments is linked together by the child link(create) or by the
1668 // sibling link(update or relocate). A negativ dir indicate that it is a
1669 // update or relocate and the sign must be reversed so the journal
1670 // system will use the sibling link to link the inode together.
1671 if (dir > 0)
1672 fs.journal.diri = chunk_traverse(fs.journal.diri);
1673
1674 fs.journal.diri = -fs.journal.diri;
1675
1676 tw(tr(TR_END, TrObject, "} %d\n", i));
1677 ttw(ttr(TTrObj, "} %d" NL,i));
1678
1679 return i;
1680 }
1681
1682
1683 int segment_read(iref_t i, char *buf, int size, int offset)
1684 {
1685 struct inode_s *ip;
1686 char *p;
1687 int chunk_size;
1688
1689 tw(tr(TR_BEGIN, TrObject, "segment_read(%d, 0x%x, %d, %d) {\n",
1690 i, buf, offset, size));
1691
1692 if (buf == NULL) {
1693 tw(tr(TR_END, TrObject, "} %d\n", EFFS_INVALID));
1694 return EFFS_INVALID;
1695 }
1696
1697 ip = inode_addr(i);
1698
1699 chunk_size = segment_datasize(ip);
1700
1701 // Saturate read buffer
1702 if (size > chunk_size - offset)
1703 size = chunk_size - offset;
1704
1705 p = offset2addr(location2offset(ip->location));
1706 p = addr2data(p, ip);
1707
1708 memcpy(buf, &p[offset], size);
1709
1710 tw(tr(TR_END, TrObject, "} %d\n", size));
1711 return size;
1712 }
1713
1714 // Find next valid chunk
1715 iref_t segment_next(iref_t i)
1716 {
1717 struct inode_s *ip = inode_addr(i);
1718
1719 tw(tr(TR_BEGIN, TrDirHigh, "ffs_segment_next(%d) {\n", i));
1720
1721 // Dir is not allowed to contain data
1722 if (is_object(ip, OT_DIR)) {
1723 tw(tr(TR_END, TrDirHigh, "} 0\n"));
1724 return 0;
1725 }
1726
1727 // Is this the last/only segment
1728 if ((i = ip->child) == (iref_t) IREF_NULL) {
1729 tw(tr(TR_END, TrDirHigh, "} 0\n"));
1730 return 0;
1731 }
1732
1733 // Get child (is valid?), search though segment by sibling link(is
1734 // valid?), and again..
1735 do {
1736 i = ip->child;
1737 ip = inode_addr(i);
1738 if (is_object_valid(ip)) {
1739 tw(tr(TR_END, TrDirHigh,"} %d\n", i));
1740 return i;
1741 }
1742
1743 while (ip->sibling != (iref_t) IREF_NULL) {
1744 i = ip->sibling;
1745 ip = inode_addr(i);
1746 if (is_object_valid(ip)) {
1747 tw(tr(TR_END, TrDirHigh,"} %d\n", i));
1748 return i;
1749 }
1750 }
1751 } while (ip->child != (iref_t) IREF_NULL);
1752
1753 // No segment found
1754 tw(tr(TR_END, TrDirHigh,"} %d\n", i));
1755 return 0;
1756 }
1757
1758 // The output "inode" will be the inode that contains the requested data or
1759 // the last inode in the segmentfile. The segmenthead will be skiped if it
1760 // don't contains any data. inode_offset is the offset in the found inode
1761 // pointed to by target_offset. If target_offset point past the last segment
1762 // will inode_offset be the size of the last inode. The return value will be
1763 // the same as target_offset but maximum the total size of the object.
1764 int segfile_seek(iref_t seghead, int target_offset,
1765 iref_t *inode, int *inode_offset)
1766 {
1767 int priv_count = 0, count_size = 0;
1768 iref_t i = seghead;
1769 struct inode_s *ip;
1770
1771 tw(tr(TR_BEGIN, TrObject, "segfile_seek(%d, %d, ?, ?) {\n",
1772 seghead, target_offset));
1773
1774 if (!is_object_valid(inode_addr(seghead))) {
1775 tw(tr(TR_END, TrAll, "FATAL: Invalid seghead!\n"));
1776 return 0;
1777 }
1778 *inode = seghead;
1779
1780 while (1)
1781 {
1782 ip = inode_addr(i);
1783 count_size += segment_datasize(ip);
1784
1785 // Seghead will be skiped if it don't contain any data
1786 if (count_size > target_offset && count_size != 0) {
1787
1788 if (inode_offset != 0)
1789 *inode_offset = target_offset - priv_count;
1790
1791 tw(tr(TR_END, TrObject, "} %d\n", target_offset));
1792 return target_offset;
1793 }
1794
1795 if ((i = segment_next(i)) == 0) {
1796 tw(tr(TR_END, TrObject, "} (eof!?) %d\n", count_size));
1797 if (inode_offset != 0)
1798 *inode_offset = count_size - priv_count;
1799 // *inode = 0;
1800 return count_size; // No more segments
1801 }
1802 priv_count = count_size;
1803
1804 *inode = i;
1805 }
1806 }
1807
1808 // Calculate exact size of file data; without filename and null terminator
1809 // and without data null terminator and succeeding alignment padding.
1810 // NOTEME: Does this also work for empty files and directories?
1811 int segment_datasize(const struct inode_s *ip)
1812 {
1813 char *p, *q;
1814 int size;
1815
1816 p = offset2addr(location2offset(ip->location));
1817 q = p + ip->size - 1;
1818
1819 // Segments is not allowed to contain any name
1820 if (!is_object(ip, OT_SEGMENT)) {
1821 // skip filename at start of data
1822 while (*p)
1823 p++;
1824 }
1825 else
1826 // If it contained a name would p pointe to the null terminator of
1827 // the name but because chunks don't have a name decrement we p to get
1828 // the size correct
1829 p--;
1830
1831 // skip padding at end of data
1832 while (*q)
1833 q--;
1834
1835 // If there are data, there is also a null-terminator. Otherwise
1836 // there is no null-terminator
1837 size = q - p;
1838 if (size > 0)
1839 size--;
1840
1841 tw(tr(TR_FUNC, TrObject, "segment_datasize(0x%x) %d\n", ip, size));
1842 return size;
1843 }
1844
1845
1846 int object_truncate(const char *pathname, fd_t fdi, offset_t length)
1847 {
1848 int segment_offset, flength, realsize, offset;
1849 iref_t i, dir, next_i;
1850 char *name = 0, *olddata;
1851 struct inode_s *oldip;
1852 effs_t error;
1853
1854 tw(tr(TR_FUNC, TrObject, "ffs_object_truncate('%s', %d, %d) \n",
1855 pathname, fdi, length));
1856 if (length < 0) return EFFS_INVALID;
1857
1858 if (pathname == 0) {
1859 // File descriptor must be open and it have to be in write mode
1860 if (!is_fd_valid(fdi))
1861 return EFFS_BADFD;;
1862
1863 if (!is_open_option(fs.fd[fdi].options, FFS_O_WRONLY))
1864 return EFFS_INVALID;
1865
1866 // It is not possible to truncate an open file to a size less than
1867 // the current file pointer
1868 if (length < fs.fd[fdi].fp)
1869 return EFFS_INVALID;
1870
1871 i = fs.fd[fdi].seghead;
1872 }
1873 else {
1874 // File must exists and not be open
1875 if ((i = object_lookup(pathname, &name, &dir)) < 0)
1876 return i;
1877
1878 if (get_fdi(i) >= 0)
1879 return EFFS_LOCKED;
1880
1881 oldip = inode_addr(i);
1882 // Even though the ffs architecture allows to have data in directory
1883 // objects, we don't want to complicate matters, so we return an error
1884 if (is_object(oldip, OT_DIR) && !(fs.flags & FS_DIR_DATA))
1885 return EFFS_NOTAFILE;
1886
1887 if ((i = is_readonly(i, pathname)) < 0)
1888 return i;
1889 }
1890 // Find the segment which length points in to
1891 flength = segfile_seek(i, length, &i, &segment_offset);
1892
1893 if (pathname == 0) {
1894 if (is_offset_in_buf(length, fdi) == 1) {
1895 fs.fd[fdi].size = (length > fs.fd[fdi].size ?
1896 fs.fd[fdi].size : length); // Truncate the buffer
1897
1898 if (i == fs.fd[fdi].wch) {
1899 next_i = segment_next(i);
1900 if (next_i > 0)
1901 if ((error = object_remove(next_i)) < 0)
1902 return error;
1903 }
1904 return EFFS_OK;
1905 }
1906 }
1907
1908 if (flength < length)
1909 return EFFS_OK;
1910
1911 journal_begin(i);
1912
1913 // Realsize do not always need to include a name but we simplify it.
1914 realsize = atomalign(segment_offset + 1 + fs.filename_max + 1);
1915
1916 // Make sure that there is enough space to make the rename without
1917 // object_create() trigger a data_reclaim() (awoid relocate oldi/data
1918 // source)
1919 if ((offset = data_prealloc(realsize)) <= 0)
1920 return EFFS_NOSPACE;
1921
1922 // Find the next segment if any.
1923 next_i = segment_next(fs.journal.oldi);
1924
1925 // Find old data source
1926 oldip = inode_addr(fs.journal.oldi);
1927 olddata = offset2addr(location2offset(oldip->location));
1928 name = addr2name(olddata); // reinit name (maybe relocated)
1929 olddata = addr2data(olddata, oldip);
1930
1931 if (is_object(oldip, OT_SEGMENT)) {
1932 if (segment_offset == 0)
1933 next_i = fs.journal.oldi; // Remove the found object
1934 else {
1935 if ((i = segment_create(olddata, segment_offset,
1936 -fs.journal.oldi)) < 0)
1937 return i;
1938
1939 fs.link_child = 0; //Do not link child
1940 journal_end(0);
1941 }
1942 }
1943 else {
1944 if ((i = object_create(name, olddata, length, fs.journal.oldi)) < 0)
1945 return i;
1946 fs.link_child = 0; //Do not link child
1947 journal_end(0);
1948
1949 if (is_fd_valid(fdi))
1950 fs.fd[fdi].seghead = i;
1951 }
1952
1953 if (is_fd_valid(fdi))
1954 fs.fd[fdi].size = length;
1955
1956 // If any remaning segment exists then remove them
1957 if (next_i > 0)
1958 if ((error = object_remove(next_i)) < 0)
1959 return error;
1960
1961 return EFFS_OK;
1962 }
1963
1964
1965 // Find the last segment valid or not valid
1966 iref_t chunk_traverse(iref_t i)
1967 {
1968 struct inode_s *ip = inode_addr(i);
1969
1970 tw(tr(TR_BEGIN, TrDirHigh, "ffs_chunk_traverse(%d) {\n", i));
1971 // Is this the last/only segment?
1972 if (ip->child == (iref_t) IREF_NULL) {
1973 tw(tr(TR_END, TrDirHigh, "} %d\n", i));
1974 return i;
1975 }
1976
1977 // Get child, find the last segment by sibling link, and again..
1978 do {
1979 i = ip->child;
1980 ip = inode_addr(i);
1981
1982 while (ip->sibling != (iref_t) IREF_NULL) {
1983 i = ip->sibling;
1984 ip = inode_addr(i);
1985 }
1986 } while (ip->child != (iref_t) IREF_NULL);
1987
1988 tw(tr(TR_END, TrDirHigh, "} %d\n", i));
1989
1990 return i;
1991 }
1992
1993 // fdi include offset now but change this so core use pure fdi.
1994 effs_t datasync(fd_t fdi)
1995 {
1996 int chunk_size;
1997 iref_t i;
1998 struct inode_s *ip;
1999 char *name;
2000
2001 tw(tr(TR_FUNC, TrObject, "datasync(%d) \n", fdi));
2002 ttw(ttr(TTrApi, "datasync(%d) {" NL, fdi));
2003
2004 // NOTEME: is this necessary?
2005 if (!is_fd_valid(fdi))
2006 return EFFS_BADFD;
2007
2008 if (fs.fd[fdi].dirty == 0)
2009 return EFFS_OK;
2010
2011 // If size - wfp is more than max is the complete buffer valid or else
2012 // is it only a part of it that consist valid data
2013 chunk_size = fs.fd[fdi].size - fs.fd[fdi].wfp;
2014 if (chunk_size > fs.chunk_size_max)
2015 chunk_size = fs.chunk_size_max;
2016
2017 ip = inode_addr(fs.fd[fdi].wch);
2018
2019 // Create new chunk or update a old one
2020 if (fs.fd[fdi].wch > 0) {
2021 // Update existing chunk
2022 // Negativ dir input because it is a update (do not traverse)
2023 if (is_object(ip, OT_SEGMENT)) {
2024 journal_begin(fs.fd[fdi].wch);
2025
2026 if ((i = segment_create(fs.fd[fdi].buf, chunk_size,
2027 -fs.fd[fdi].wch)) < 0)
2028 return i;
2029 }
2030
2031 else {
2032 // Seghead update (like a normal file)
2033 ip = inode_addr(fs.fd[fdi].seghead);
2034 name = addr2name(offset2addr(location2offset(ip->location)));
2035 journal_begin(fs.fd[fdi].seghead);
2036
2037 if ((i = object_create(name, fs.fd[fdi].buf, chunk_size,
2038 fs.fd[fdi].seghead)) < 0)
2039 return i;
2040
2041 fs.fd[fdi].seghead = i;
2042 }
2043 journal_end(0);
2044 }
2045
2046 else {
2047 // Create new chunk at the end of the existing ones.
2048 // BTW: A seghead will always have been made before this one.
2049 journal_begin(0);
2050
2051 if ((i = segment_create(fs.fd[fdi].buf, chunk_size,
2052 fs.fd[fdi].seghead)) < 0)
2053 return i;
2054
2055 journal_end(OT_SEGMENT);
2056 }
2057 fs.fd[fdi].dirty = fs.fd[fdi].wch = 0;
2058
2059 ttw(ttr(TTrApi, "} 0" NL));
2060 return EFFS_OK;
2061 }
2062
2063 /******************************************************************************
2064 * Development and Tracing
2065 ******************************************************************************/
2066
2067 #if (TARGET == 0)
2068
2069 void tr_bstat(void)
2070 {
2071 int i, n;
2072 struct block_header_s *bhp;
2073 struct block_stat_s *bsp;
2074
2075 tw(tr(TR_BEGIN, TrBstat, "bstat = {\n"));
2076
2077 bsp = &bstat[0];
2078 tw(tr(TR_FUNC, TrBstat,
2079 " bf used lost free n age state\n"));
2080 for (i = 0, n = 0; i < dev.numblocks; i++, bsp++) {
2081 bhp = (struct block_header_s *) offset2addr(dev.binfo[i].offset);
2082 tw(tr(TR_FUNC, TrBstat, "%2d %02x %6d %6d %6d %3d %5d %s%s%s%s%s%s\n",
2083 i, bsp->flags & 0xFF,
2084 bsp->used, bsp->lost,
2085 dev.blocksize - bsp->used,
2086 bsp->objects,
2087 bhp->age,
2088 (is_block(i, BF_IS_FREE) ? "FREE " : ""),
2089 (is_block(i, BF_IS_DATA) ? "DATA " : ""),
2090 (is_block(i, BF_IS_CLEANING) ? "CLEANING " : ""),
2091 (is_block(i, BF_IS_COPYING) ? "COPYING " : ""),
2092 (is_block(i, BF_IS_INODES) ? "INODES " : ""),
2093 (is_block_flag(i, BF_LOST) ? "lost " : "")
2094 ));
2095 if (is_block(i, BF_IS_DATA))
2096 n += bsp->objects;
2097 }
2098 i = bstat[fs.inodes].used - bstat[fs.inodes].lost;
2099 tw(tr(TR_FUNC, TrBstat,
2100 " %3d (used-lost = %d)\n",
2101 n, i));
2102
2103 if (n != i) {
2104 tw(tr(TR_FUNC, TrAll, "WARNING: sum(bstat[x].objects) != bstat[fs.inodes].used - bstat[fs.inodes].lost\n"));
2105 }
2106
2107 tw(tr(TR_END, TrBstat, "}\n"));
2108 }
2109
2110 #else // (TARGET == 1)
2111
2112 void tr_bstat(void)
2113 {
2114 int i;
2115 struct block_stat_s *bsp = &bstat[0];
2116
2117 for (i = 0; i < dev.numblocks; i++, bsp++) {
2118 ttw(ttr(TTrBstat, "%2d (%2x) u/l/f/n %6d %6d %6d %2d" NL,
2119 i, bsp->flags,
2120 bsp->used, bsp->lost,
2121 dev.blocksize - bsp->used,
2122 bsp->objects
2123 ));
2124 }
2125 ttw(str(TTrBstat,"" NL));
2126 }
2127
2128
2129 void tr_fd(fd_t fdi)
2130 {
2131 tw(tr(TR_BEGIN, TrHelper, "tr_fd(%d) {\n", fdi));
2132 tw(tr(TR_FUNC, TrHelper, "options: 0x%x \n", fd[fdi].options));
2133 tw(tr(TR_FUNC, TrHelper, "inode : %d \n", fd[fdi].inode_first));
2134 tw(tr(TR_FUNC, TrHelper, "fp : %d \n", fd[fdi].fp));
2135 tw(tr(TR_FUNC, TrHelper, "size : %d \n", fd[fdi].size));
2136 tw(tr(TR_FUNC, TrHelper, "dir : %d \n", fd[fdi].dir));
2137 tw(tr(TR_FUNC, TrHelper, "name : %s \n", fd[fdi].name));
2138 tw(tr(TR_END, TrHelper, "}\n", fdi));
2139 }
2140
2141 #endif // (TARGET == 0)