comparison src/cs/drivers/drv_app/ffs/board/core.c @ 0:b6a5e36de839

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