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