FreeCalypso > hg > tcs211-fcmodem
comparison chipsetsw/drivers/drv_app/ffs/board/core.c @ 0:509db1a7b7b8
initial import: leo2moko-r1
author | Space Falcon <falcon@ivan.Harhan.ORG> |
---|---|
date | Mon, 01 Jun 2015 03:24:05 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:509db1a7b7b8 |
---|---|
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, ¬_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, ¬_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) |