1 /*
2 * Copyright (c) 2023 Antmicro <www.antmicro.com>
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/kernel.h>
8 #include <zephyr/init.h>
9 #include <zephyr/fs/fs.h>
10 #include <zephyr/logging/log.h>
11 #include <zephyr/sys/util.h>
12 #include <zephyr/sys/byteorder.h>
13
14 #include "ext2.h"
15 #include "ext2_impl.h"
16 #include "ext2_struct.h"
17 #include "ext2_diskops.h"
18 #include "ext2_bitmap.h"
19
20 LOG_MODULE_REGISTER(ext2, CONFIG_EXT2_LOG_LEVEL);
21
22 static struct ext2_data __fs;
23 static bool initialized;
24
25 #define BLOCK_MEMORY_BUFFER_SIZE (CONFIG_EXT2_MAX_BLOCK_COUNT * CONFIG_EXT2_MAX_BLOCK_SIZE)
26 #define BLOCK_STRUCT_BUFFER_SIZE (CONFIG_EXT2_MAX_BLOCK_COUNT * sizeof(struct ext2_block))
27
28 /* Structures for blocks slab alocator */
29 struct k_mem_slab ext2_block_memory_slab, ext2_block_struct_slab;
30 char __aligned(sizeof(void *)) __ext2_block_memory_buffer[BLOCK_MEMORY_BUFFER_SIZE];
31 char __aligned(sizeof(void *)) __ext2_block_struct_buffer[BLOCK_STRUCT_BUFFER_SIZE];
32
33 /* Initialize heap memory allocator */
34 K_HEAP_DEFINE(direntry_heap, MAX_DIRENTRY_SIZE);
35 K_MEM_SLAB_DEFINE(inode_struct_slab, sizeof(struct ext2_inode), MAX_INODES, sizeof(void *));
36
37 /* Helper functions --------------------------------------------------------- */
38
error_behavior(struct ext2_data * fs,const char * msg)39 void error_behavior(struct ext2_data *fs, const char *msg)
40 {
41 LOG_ERR("File system corrupted: %s", msg);
42
43 /* If file system is not initialized panic */
44 if (!initialized) {
45 LOG_ERR("File system data not found. Panic...");
46 k_panic();
47 }
48
49 switch (fs->sblock.s_errors) {
50 case EXT2_ERRORS_CONTINUE:
51 /* Do nothing */
52 break;
53 case EXT2_ERRORS_RO:
54 LOG_WRN("Marking file system as read only");
55 fs->flags |= EXT2_DATA_FLAGS_RO;
56 break;
57 case EXT2_ERRORS_PANIC:
58 LOG_ERR("Panic...");
59 k_panic();
60 break;
61 default:
62 LOG_ERR("Unrecognized errors behavior in superblock s_errors field. Panic...");
63 k_panic();
64 }
65 }
66
67 /* Block operations --------------------------------------------------------- */
68
get_block_struct(void)69 static struct ext2_block *get_block_struct(void)
70 {
71 int ret;
72 struct ext2_block *b;
73
74 ret = k_mem_slab_alloc(&ext2_block_struct_slab, (void **)&b, K_NO_WAIT);
75 if (ret < 0) {
76 LOG_ERR("get block: alloc block struct error %d", ret);
77 return NULL;
78 }
79
80 ret = k_mem_slab_alloc(&ext2_block_memory_slab, (void **)&b->data, K_NO_WAIT);
81 if (ret < 0) {
82 LOG_ERR("get block: alloc block memory error %d", ret);
83 k_mem_slab_free(&ext2_block_struct_slab, (void *)b);
84 return NULL;
85 }
86 return b;
87 }
88
ext2_get_block(struct ext2_data * fs,uint32_t block)89 struct ext2_block *ext2_get_block(struct ext2_data *fs, uint32_t block)
90 {
91 int ret;
92 struct ext2_block *b = get_block_struct();
93
94 if (!b) {
95 return NULL;
96 }
97 b->num = block;
98 b->flags = EXT2_BLOCK_ASSIGNED;
99 ret = fs->backend_ops->read_block(fs, b->data, block);
100 if (ret < 0) {
101 LOG_ERR("get block: read block error %d", ret);
102 ext2_drop_block(b);
103 return NULL;
104 }
105 return b;
106 }
107
ext2_get_empty_block(struct ext2_data * fs)108 struct ext2_block *ext2_get_empty_block(struct ext2_data *fs)
109 {
110 struct ext2_block *b = get_block_struct();
111
112 if (!b) {
113 return NULL;
114 }
115 b->num = 0;
116 b->flags = 0;
117 memset(b->data, 0, fs->block_size);
118 return b;
119 }
120
ext2_write_block(struct ext2_data * fs,struct ext2_block * b)121 int ext2_write_block(struct ext2_data *fs, struct ext2_block *b)
122 {
123 int ret;
124
125 if (!(b->flags & EXT2_BLOCK_ASSIGNED)) {
126 return -EINVAL;
127 }
128
129 ret = fs->backend_ops->write_block(fs, b->data, b->num);
130 if (ret < 0) {
131 return ret;
132 }
133 return 0;
134 }
135
ext2_drop_block(struct ext2_block * b)136 void ext2_drop_block(struct ext2_block *b)
137 {
138 if (b == NULL) {
139 return;
140 }
141
142 if (b != NULL && b->data != NULL) {
143 k_mem_slab_free(&ext2_block_memory_slab, (void *)b->data);
144 k_mem_slab_free(&ext2_block_struct_slab, (void *)b);
145 }
146 }
147
ext2_init_blocks_slab(struct ext2_data * fs)148 void ext2_init_blocks_slab(struct ext2_data *fs)
149 {
150 memset(__ext2_block_memory_buffer, 0, BLOCK_MEMORY_BUFFER_SIZE);
151 memset(__ext2_block_struct_buffer, 0, BLOCK_STRUCT_BUFFER_SIZE);
152
153 /* These calls will always succeed because sizes and memory buffers are properly aligned. */
154
155 k_mem_slab_init(&ext2_block_struct_slab, __ext2_block_struct_buffer,
156 sizeof(struct ext2_block), CONFIG_EXT2_MAX_BLOCK_COUNT);
157
158 k_mem_slab_init(&ext2_block_memory_slab, __ext2_block_memory_buffer, fs->block_size,
159 CONFIG_EXT2_MAX_BLOCK_COUNT);
160 }
161
ext2_assign_block_num(struct ext2_data * fs,struct ext2_block * b)162 int ext2_assign_block_num(struct ext2_data *fs, struct ext2_block *b)
163 {
164 int64_t new_block;
165
166 if (b->flags & EXT2_BLOCK_ASSIGNED) {
167 return -EINVAL;
168 }
169
170 /* Allocate block in the file system. */
171 new_block = ext2_alloc_block(fs);
172 if (new_block < 0) {
173 return new_block;
174 }
175
176 b->num = new_block;
177 b->flags |= EXT2_BLOCK_ASSIGNED;
178 return 0;
179 }
180
181
182 /* FS operations ------------------------------------------------------------ */
183
ext2_init_storage(struct ext2_data ** fsp,const void * storage_dev,int flags)184 int ext2_init_storage(struct ext2_data **fsp, const void *storage_dev, int flags)
185 {
186 if (initialized) {
187 return -EBUSY;
188 }
189
190 int ret = 0;
191 struct ext2_data *fs = &__fs;
192 int64_t dev_size, write_size;
193
194 *fsp = fs;
195 fs->open_inodes = 0;
196 fs->flags = 0;
197 fs->bgroup.num = -1;
198
199 ret = ext2_init_disk_access_backend(fs, storage_dev, flags);
200 if (ret < 0) {
201 return ret;
202 }
203
204 dev_size = fs->backend_ops->get_device_size(fs);
205 if (dev_size < 0) {
206 ret = dev_size;
207 goto err;
208 }
209
210 write_size = fs->backend_ops->get_write_size(fs);
211 if (write_size < 0) {
212 ret = write_size;
213 goto err;
214 }
215
216 if (write_size < 1024 && 1024 % write_size != 0) {
217 ret = -EINVAL;
218 LOG_ERR("expecting sector size that divides 1024 (got: %lld)", write_size);
219 goto err;
220 }
221
222 LOG_DBG("Device size: %lld", dev_size);
223 LOG_DBG("Write size: %lld", write_size);
224
225 fs->device_size = dev_size;
226 fs->write_size = write_size;
227
228 initialized = true;
229 err:
230 return ret;
231 }
232
ext2_verify_disk_superblock(struct ext2_disk_superblock * sb)233 int ext2_verify_disk_superblock(struct ext2_disk_superblock *sb)
234 {
235 /* Check if it is a valid Ext2 file system. */
236 if (sys_le16_to_cpu(sb->s_magic) != EXT2_MAGIC_NUMBER) {
237 LOG_ERR("Wrong file system magic number (%x)", sb->s_magic);
238 return -EINVAL;
239 }
240
241 /* For now we don't support file systems with frag size different from block size */
242 if (sys_le32_to_cpu(sb->s_log_block_size) != sb->s_log_frag_size) {
243 LOG_ERR("Filesystem with frag_size != block_size is not supported");
244 return -ENOTSUP;
245 }
246
247 /* Support only second revision */
248 if (sys_le32_to_cpu(sb->s_rev_level) != EXT2_DYNAMIC_REV) {
249 LOG_ERR("Filesystem with revision %d is not supported", sb->s_rev_level);
250 return -ENOTSUP;
251 }
252
253 if (sys_le16_to_cpu(sb->s_inode_size) != EXT2_GOOD_OLD_INODE_SIZE) {
254 LOG_ERR("Filesystem with inode size %d is not supported", sb->s_inode_size);
255 return -ENOTSUP;
256 }
257
258 /* Check if file system may contain errors. */
259 if (sys_le16_to_cpu(sb->s_state) == EXT2_ERROR_FS) {
260 LOG_WRN("File system may contain errors.");
261 switch (sys_le16_to_cpu(sb->s_errors)) {
262 case EXT2_ERRORS_CONTINUE:
263 break;
264
265 case EXT2_ERRORS_RO:
266 LOG_WRN("File system can be mounted read only");
267 return -EROFS;
268
269 case EXT2_ERRORS_PANIC:
270 LOG_ERR("File system can't be mounted. Panic...");
271 k_panic();
272 default:
273 LOG_WRN("Unknown option for superblock s_errors field.");
274 }
275 }
276
277 if ((sys_le32_to_cpu(sb->s_feature_incompat) & EXT2_FEATURE_INCOMPAT_FILETYPE) == 0) {
278 LOG_ERR("File system without file type stored in de is not supported");
279 return -ENOTSUP;
280 }
281
282 if ((sys_le32_to_cpu(sb->s_feature_incompat) & ~EXT2_FEATURE_INCOMPAT_SUPPORTED) > 0) {
283 LOG_ERR("File system can't be mounted. Incompat features %d not supported",
284 (sb->s_feature_incompat & ~EXT2_FEATURE_INCOMPAT_SUPPORTED));
285 return -ENOTSUP;
286 }
287
288 if ((sys_le32_to_cpu(sb->s_feature_ro_compat) & ~EXT2_FEATURE_RO_COMPAT_SUPPORTED) > 0) {
289 LOG_WRN("File system can be mounted read only. RO features %d detected.",
290 (sb->s_feature_ro_compat & ~EXT2_FEATURE_RO_COMPAT_SUPPORTED));
291 return -EROFS;
292 }
293
294 LOG_DBG("ino_cnt:%d blk_cnt:%d blk_per_grp:%d ino_per_grp:%d free_ino:%d free_blk:%d "
295 "blk_size:%d ino_size:%d mntc:%d",
296 sys_le32_to_cpu(sb->s_inodes_count),
297 sys_le32_to_cpu(sb->s_blocks_count),
298 sys_le32_to_cpu(sb->s_blocks_per_group),
299 sys_le32_to_cpu(sb->s_inodes_per_group),
300 sys_le32_to_cpu(sb->s_free_inodes_count),
301 sys_le32_to_cpu(sb->s_free_blocks_count),
302 sys_le32_to_cpu(1024 << sb->s_log_block_size),
303 sys_le16_to_cpu(sb->s_inode_size),
304 sys_le16_to_cpu(sb->s_mnt_count));
305 return 0;
306 }
307
ext2_init_fs(struct ext2_data * fs)308 int ext2_init_fs(struct ext2_data *fs)
309 {
310 int ret = 0;
311
312 /* Fetch superblock */
313 ret = ext2_fetch_superblock(fs);
314 if (ret < 0) {
315 return ret;
316 }
317
318 if (!(fs->flags & EXT2_DATA_FLAGS_RO)) {
319 /* Update sblock fields set during the successful mount. */
320 fs->sblock.s_state = EXT2_ERROR_FS;
321 fs->sblock.s_mnt_count += 1;
322 ret = ext2_commit_superblock(fs);
323 if (ret < 0) {
324 return ret;
325 }
326 }
327
328 ret = ext2_fetch_block_group(fs, 0);
329 if (ret < 0) {
330 return ret;
331 }
332 ret = ext2_fetch_bg_ibitmap(&fs->bgroup);
333 if (ret < 0) {
334 return ret;
335 }
336 ret = ext2_fetch_bg_bbitmap(&fs->bgroup);
337 if (ret < 0) {
338 return ret;
339 }
340
341 /* Validate superblock */
342 uint32_t set;
343 struct ext2_superblock *sb = &fs->sblock;
344 uint32_t fs_blocks = sb->s_blocks_count - sb->s_first_data_block;
345
346 set = ext2_bitmap_count_set(BGROUP_BLOCK_BITMAP(&fs->bgroup), fs_blocks);
347
348 if (set != sb->s_blocks_count - sb->s_free_blocks_count - sb->s_first_data_block) {
349 error_behavior(fs, "Wrong number of used blocks in superblock and bitmap");
350 return -EINVAL;
351 }
352
353 set = ext2_bitmap_count_set(BGROUP_INODE_BITMAP(&fs->bgroup), sb->s_inodes_count);
354
355 if (set != sb->s_inodes_count - sb->s_free_inodes_count) {
356 error_behavior(fs, "Wrong number of used inodes in superblock and bitmap");
357 return -EINVAL;
358 }
359 return 0;
360 }
361
ext2_close_fs(struct ext2_data * fs)362 int ext2_close_fs(struct ext2_data *fs)
363 {
364 int ret = 0;
365
366 /* Close all open inodes */
367 for (int32_t i = 0; i < fs->open_inodes; ++i) {
368 if (fs->inode_pool[i] != NULL) {
369 ext2_inode_drop(fs->inode_pool[i]);
370 }
371 }
372
373 /* To save file system as correct it must be writable and without errors */
374 if (!(fs->flags & (EXT2_DATA_FLAGS_RO | EXT2_DATA_FLAGS_ERR))) {
375 fs->sblock.s_state = EXT2_VALID_FS;
376 ret = ext2_commit_superblock(fs);
377 if (ret < 0) {
378 return ret;
379 }
380 }
381
382 /* free block group if it is fetched */
383 ext2_drop_block(fs->bgroup.inode_table);
384 ext2_drop_block(fs->bgroup.inode_bitmap);
385 ext2_drop_block(fs->bgroup.block_bitmap);
386
387 if (fs->backend_ops->sync(fs) < 0) {
388 return -EIO;
389 }
390 return 0;
391 }
392
ext2_close_struct(struct ext2_data * fs)393 int ext2_close_struct(struct ext2_data *fs)
394 {
395 memset(fs, 0, sizeof(struct ext2_data));
396 initialized = false;
397 return 0;
398 }
399
400 /* Lookup ------------------------------------------------------------------- */
401
402 /* Functions needed by lookup inode */
403 static const char *skip_slash(const char *str);
404 static char *strchrnul(const char *str, const char c);
405 static int64_t find_dir_entry(struct ext2_inode *inode, const char *name, size_t len,
406 uint32_t *r_offset);
407
ext2_lookup_inode(struct ext2_data * fs,struct ext2_lookup_args * args)408 int ext2_lookup_inode(struct ext2_data *fs, struct ext2_lookup_args *args)
409 {
410 LOG_DBG("Looking for file %s", args->path);
411
412 int rc, ret = 0;
413 struct ext2_inode *cur_dir = NULL, *next = NULL;
414 static char name_buf[EXT2_MAX_FILE_NAME + 1];
415
416 /* Start looking from root directory of file system */
417 rc = ext2_inode_get(fs, EXT2_ROOT_INODE, &cur_dir);
418 if (rc < 0) {
419 ret = rc;
420 goto out;
421 }
422
423 /* There may be slash at the beginning of path */
424 const char *path = args->path;
425
426 path = skip_slash(path);
427
428 /* If path is empty then return root directory */
429 if (path[0] == '\0') {
430 args->inode = cur_dir;
431 cur_dir = NULL;
432 goto out;
433 }
434
435 for (;;) {
436 /* Get path component */
437 char *end = strchrnul(path, '/');
438 size_t len = end - path;
439
440 if (len > EXT2_MAX_FILE_NAME) {
441 ret = -ENAMETOOLONG;
442 goto out;
443 }
444
445 strncpy(name_buf, path, len);
446 name_buf[len] = '\0';
447
448 /* Search in current directory */
449 uint32_t dir_off = 0;
450 /* using 64 bit value to don't lose any information on error */
451 int64_t ino = find_dir_entry(cur_dir, name_buf, len, &dir_off);
452
453 const char *next_path = skip_slash(end);
454 bool last_entry = next_path[0] == '\0';
455
456 if (!last_entry) {
457 /* prepare the next loop iteration */
458
459 if (ino < 0) {
460 /* next entry not found */
461 ret = -ENOENT;
462 goto out;
463 }
464
465 rc = ext2_inode_get(fs, ino, &next);
466 if (rc < 0) {
467 /* error while fetching next entry */
468 ret = rc;
469 goto out;
470 }
471
472 if (!(next->i_mode & EXT2_S_IFDIR)) {
473 /* path component should be directory */
474 ret = -ENOTDIR;
475 goto out;
476 }
477
478 /* Go to the next path component */
479 path = next_path;
480
481 /* Move to next directory */
482 ext2_inode_drop(cur_dir);
483 cur_dir = next;
484
485 next = NULL;
486 continue;
487 }
488
489 /* Last entry */
490
491 if (ino < 0 && !(args->flags & LOOKUP_ARG_CREATE)) {
492 /* entry not found but we need it */
493 ret = -ENOENT;
494 goto out;
495 }
496
497 if (ino > 0) {
498 rc = ext2_inode_get(fs, ino, &next);
499 if (rc < 0) {
500 ret = rc;
501 goto out;
502 }
503 }
504
505 /* Store parent directory and offset in parent directory */
506 if (args->flags & (LOOKUP_ARG_CREATE | LOOKUP_ARG_STAT | LOOKUP_ARG_UNLINK)) {
507 /* In create it will be valid only if we have found existing file */
508 args->offset = dir_off;
509 args->parent = cur_dir;
510 cur_dir = NULL;
511 }
512
513 /* Store name info */
514 if (args->flags & LOOKUP_ARG_CREATE) {
515 args->name_pos = path - args->path;
516 args->name_len = len;
517 }
518
519 /* Store found inode */
520 if (ino > 0) {
521 args->inode = next;
522 next = NULL;
523 }
524 goto out;
525 }
526
527 out:
528 /* Always free that inodes.
529 * If some of them is returned from function then proper pointer is set to NULL.
530 */
531 ext2_inode_drop(cur_dir);
532 ext2_inode_drop(next);
533 return ret;
534 }
535
536 /* Return position of given char or end of string. */
strchrnul(const char * s,char c)537 static char *strchrnul(const char *s, char c)
538 {
539 while ((*s != c) && (*s != '\0')) {
540 s++;
541 }
542 return (char *) s;
543 }
544
skip_slash(const char * s)545 static const char *skip_slash(const char *s)
546 {
547 while ((*s == '/') && (*s != '\0')) {
548 s++;
549 }
550 return s;
551 }
552
553 /**
554 * @brief Find inode
555 *
556 * @note Inodes are 32 bit. When we return signed 64 bit number then we don't
557 * lose any information.
558 *
559 * @param r_offset If not NULL then offset in directory of that entry is written here.
560 * @return Inode number or negative error code
561 */
find_dir_entry(struct ext2_inode * inode,const char * name,size_t len,uint32_t * r_offset)562 static int64_t find_dir_entry(struct ext2_inode *inode, const char *name, size_t len,
563 uint32_t *r_offset)
564 {
565 int rc;
566 uint32_t block, block_off, offset = 0;
567 int64_t ino = -1;
568 struct ext2_data *fs = inode->i_fs;
569 struct ext2_direntry *de;
570
571 while (offset < inode->i_size) {
572 block = offset / fs->block_size;
573 block_off = offset % fs->block_size;
574
575 rc = ext2_fetch_inode_block(inode, block);
576 if (rc < 0) {
577 return rc;
578 }
579
580 struct ext2_disk_direntry *disk_de =
581 EXT2_DISK_DIRENTRY_BY_OFFSET(inode_current_block_mem(inode), block_off);
582
583 de = ext2_fetch_direntry(disk_de);
584 if (de == NULL) {
585 return -EINVAL;
586 }
587
588 if (len == de->de_name_len && strncmp(de->de_name, name, len) == 0) {
589 ino = de->de_inode;
590 if (r_offset) {
591 /* Return offset*/
592 *r_offset = offset;
593 }
594 goto success;
595 }
596 /* move to the next directory entry */
597 offset += de->de_rec_len;
598 k_heap_free(&direntry_heap, de);
599 }
600
601 return -EINVAL;
602 success:
603 k_heap_free(&direntry_heap, de);
604 return (int64_t)ino;
605 }
606
607 /* Inode operations --------------------------------------------------------- */
608
ext2_inode_read(struct ext2_inode * inode,void * buf,uint32_t offset,size_t nbytes)609 ssize_t ext2_inode_read(struct ext2_inode *inode, void *buf, uint32_t offset, size_t nbytes)
610 {
611 int rc = 0;
612 ssize_t read = 0;
613 uint32_t block_size = inode->i_fs->block_size;
614
615 while (read < nbytes && offset < inode->i_size) {
616
617 uint32_t block = offset / block_size;
618 uint32_t block_off = offset % block_size;
619
620 rc = ext2_fetch_inode_block(inode, block);
621 if (rc < 0) {
622 break;
623 }
624
625 uint32_t left_on_blk = block_size - block_off;
626 uint32_t left_in_file = inode->i_size - offset;
627 size_t to_read = MIN(nbytes, MIN(left_on_blk, left_in_file));
628
629 memcpy((uint8_t *)buf + read, inode_current_block_mem(inode) + block_off, to_read);
630
631 read += to_read;
632 offset += to_read;
633 }
634
635 if (rc < 0) {
636 return rc;
637 }
638 return read;
639 }
640
ext2_inode_write(struct ext2_inode * inode,const void * buf,uint32_t offset,size_t nbytes)641 ssize_t ext2_inode_write(struct ext2_inode *inode, const void *buf, uint32_t offset, size_t nbytes)
642 {
643 int rc = 0;
644 ssize_t written = 0;
645 uint32_t block_size = inode->i_fs->block_size;
646
647 while (written < nbytes) {
648 uint32_t block = offset / block_size;
649 uint32_t block_off = offset % block_size;
650
651 LOG_DBG("inode:%d Write to block %d (offset: %d-%zd/%d)",
652 inode->i_id, block, offset, offset + nbytes, inode->i_size);
653
654 rc = ext2_fetch_inode_block(inode, block);
655 if (rc < 0) {
656 break;
657 }
658
659 size_t to_write = MIN(nbytes, block_size - block_off);
660
661 memcpy(inode_current_block_mem(inode) + block_off, (uint8_t *)buf + written,
662 to_write);
663 LOG_DBG("Written %zd bytes at offset %d in block i%d", to_write, block_off, block);
664
665 rc = ext2_commit_inode_block(inode);
666 if (rc < 0) {
667 break;
668 }
669
670 written += to_write;
671 }
672
673 if (rc < 0) {
674 return rc;
675 }
676
677 if (offset + written > inode->i_size) {
678 LOG_DBG("New inode size: %d -> %zd", inode->i_size, offset + written);
679 inode->i_size = offset + written;
680 rc = ext2_commit_inode(inode);
681 if (rc < 0) {
682 return rc;
683 }
684 }
685
686 return written;
687 }
688
ext2_inode_trunc(struct ext2_inode * inode,off_t length)689 int ext2_inode_trunc(struct ext2_inode *inode, off_t length)
690 {
691 if (length > UINT32_MAX) {
692 return -ENOTSUP;
693 }
694
695 int rc = 0;
696 uint32_t new_size = (uint32_t)length;
697 uint32_t old_size = inode->i_size;
698 const uint32_t block_size = inode->i_fs->block_size;
699
700 LOG_DBG("Resizing inode from %d to %d", old_size, new_size);
701
702 if (old_size == new_size) {
703 return 0;
704 }
705
706 uint32_t used_blocks = new_size / block_size + (new_size % block_size != 0);
707
708 if (new_size > old_size) {
709 if (old_size % block_size != 0) {
710 /* file ends inside some block */
711
712 LOG_DBG("Has to insert zeros to the end of block");
713
714 /* insert zeros to the end of last block */
715 uint32_t old_block = old_size / block_size;
716 uint32_t start_off = old_size % block_size;
717 uint32_t to_write = MIN(new_size - old_size, block_size - start_off);
718
719 rc = ext2_fetch_inode_block(inode, old_block);
720 if (rc < 0) {
721 return rc;
722 }
723
724 memset(inode_current_block_mem(inode) + start_off, 0, to_write);
725 rc = ext2_commit_inode_block(inode);
726 if (rc < 0) {
727 return rc;
728 }
729 }
730
731 /* There is no need to zero rest of blocks because they will be automatically
732 * treated as zero filled.
733 */
734
735 } else {
736 /* First removed block is just the number of used blocks.
737 * (We count blocks from zero hence its number is just number of used blocks.)
738 */
739 uint32_t start_blk = used_blocks;
740 int64_t removed_blocks;
741
742 LOG_DBG("Inode trunc from blk: %d", start_blk);
743
744 /* Remove blocks starting with start_blk. */
745 removed_blocks = ext2_inode_remove_blocks(inode, start_blk);
746 if (removed_blocks < 0) {
747 return removed_blocks;
748 }
749
750 LOG_DBG("Removed blocks: %lld (%lld)",
751 removed_blocks, removed_blocks * (block_size / 512));
752 inode->i_blocks -= removed_blocks * (block_size / 512);
753 }
754
755 inode->i_size = new_size;
756
757 LOG_DBG("New inode size: %d (blocks: %d)", inode->i_size, inode->i_blocks);
758
759 rc = ext2_commit_inode(inode);
760 return rc;
761 }
762
write_one_block(struct ext2_data * fs,struct ext2_block * b)763 static int write_one_block(struct ext2_data *fs, struct ext2_block *b)
764 {
765 int ret = 0;
766
767 if (!(b->flags & EXT2_BLOCK_ASSIGNED)) {
768 ret = ext2_assign_block_num(fs, b);
769 if (ret < 0) {
770 return ret;
771 }
772 }
773
774 ret = ext2_write_block(fs, b);
775 return ret;
776 }
777
ext2_inode_sync(struct ext2_inode * inode)778 int ext2_inode_sync(struct ext2_inode *inode)
779 {
780 int ret;
781 struct ext2_data *fs = inode->i_fs;
782
783 for (int i = 0; i < 4; ++i) {
784 if (inode->blocks[i] == NULL) {
785 break;
786 }
787 ret = write_one_block(fs, inode->blocks[i]);
788 if (ret < 0) {
789 return ret;
790 }
791 ret = fs->backend_ops->sync(fs);
792 if (ret < 0) {
793 return ret;
794 }
795 }
796 return 0;
797 }
798
ext2_get_direntry(struct ext2_file * dir,struct fs_dirent * ent)799 int ext2_get_direntry(struct ext2_file *dir, struct fs_dirent *ent)
800 {
801 if (dir->f_off >= dir->f_inode->i_size) {
802 /* end of directory */
803 ent->name[0] = 0;
804 return 0;
805 }
806
807 struct ext2_data *fs = dir->f_inode->i_fs;
808
809 int rc, ret = 0;
810 uint32_t block = dir->f_off / fs->block_size;
811 uint32_t block_off = dir->f_off % fs->block_size;
812 uint32_t len;
813
814 LOG_DBG("Reading dir entry from block %d at offset %d", block, block_off);
815
816 rc = ext2_fetch_inode_block(dir->f_inode, block);
817 if (rc < 0) {
818 return rc;
819 }
820
821 struct ext2_inode *inode = NULL;
822 struct ext2_disk_direntry *disk_de =
823 EXT2_DISK_DIRENTRY_BY_OFFSET(inode_current_block_mem(dir->f_inode), block_off);
824 struct ext2_direntry *de = ext2_fetch_direntry(disk_de);
825
826 if (de == NULL) {
827 LOG_ERR("Read directory entry name too long");
828 return -EINVAL;
829 }
830
831 LOG_DBG("inode=%d name_len=%d rec_len=%d", de->de_inode, de->de_name_len, de->de_rec_len);
832
833 len = de->de_name_len;
834 if (de->de_name_len > MAX_FILE_NAME) {
835 LOG_WRN("Directory name won't fit in direntry");
836 len = MAX_FILE_NAME;
837 }
838 memcpy(ent->name, de->de_name, len);
839 ent->name[len] = '\0';
840
841 LOG_DBG("name_len=%d name=%s %d", de->de_name_len, ent->name, EXT2_MAX_FILE_NAME);
842
843 /* Get type of directory entry */
844 ent->type = de->de_file_type & EXT2_FT_DIR ? FS_DIR_ENTRY_DIR : FS_DIR_ENTRY_FILE;
845
846 /* Get size only for files. Directories have size 0. */
847 size_t size = 0;
848
849 if (ent->type == FS_DIR_ENTRY_FILE) {
850 rc = ext2_inode_get(fs, de->de_inode, &inode);
851 if (rc < 0) {
852 ret = rc;
853 goto out;
854 }
855 size = inode->i_size;
856 }
857
858 ent->size = size;
859
860 /* Update offset to point to next directory entry */
861 dir->f_off += de->de_rec_len;
862
863 out:
864 k_heap_free(&direntry_heap, de);
865 ext2_inode_drop(inode);
866 return ret;
867 }
868
869 /* Create files and directories */
870
871 /* Allocate inode number and fill inode table with default values. */
ext2_create_inode(struct ext2_data * fs,struct ext2_inode * parent,struct ext2_inode * inode,int type)872 static int ext2_create_inode(struct ext2_data *fs, struct ext2_inode *parent,
873 struct ext2_inode *inode, int type)
874 {
875 int rc;
876 int32_t ino = ext2_alloc_inode(fs);
877
878 if (ino < 0) {
879 return ino;
880 }
881
882 /* fill inode with correct data */
883 inode->i_fs = fs;
884 inode->flags = 0;
885 inode->i_id = ino;
886 inode->i_size = 0;
887 inode->i_mode = type == FS_DIR_ENTRY_FILE ? EXT2_DEF_FILE_MODE : EXT2_DEF_DIR_MODE;
888 inode->i_links_count = 0;
889 memset(inode->i_block, 0, 15 * 4);
890
891 if (type == FS_DIR_ENTRY_DIR) {
892 /* Block group current block is already fetched. We don't have to do it again.
893 * (It was done above in ext2_alloc_inode function.)
894 */
895 fs->bgroup.bg_used_dirs_count += 1;
896 rc = ext2_commit_bg(fs);
897 if (rc < 0) {
898 return rc;
899 }
900 }
901
902 rc = ext2_commit_inode(inode);
903 return rc;
904 }
905
ext2_create_direntry(const char * name,uint8_t namelen,uint32_t ino,uint8_t filetype)906 struct ext2_direntry *ext2_create_direntry(const char *name, uint8_t namelen, uint32_t ino,
907 uint8_t filetype)
908 {
909 __ASSERT(namelen <= EXT2_MAX_FILE_NAME, "Name length to long");
910
911 uint32_t prog_rec_len = sizeof(struct ext2_direntry) + namelen;
912 struct ext2_direntry *de = k_heap_alloc(&direntry_heap, prog_rec_len, K_FOREVER);
913
914 /* Size of future disk structure. */
915 uint32_t reclen = sizeof(struct ext2_disk_direntry) + namelen;
916
917 /* Align reclen to 4 bytes. */
918 reclen = ROUND_UP(reclen, 4);
919
920 de->de_inode = ino;
921 de->de_rec_len = reclen;
922 de->de_name_len = (uint8_t)namelen;
923 de->de_file_type = filetype;
924 memcpy(de->de_name, name, namelen);
925
926 LOG_DBG("Initialized directory entry %p{%s(%d) %d %d %c}",
927 de, de->de_name, de->de_name_len, de->de_inode, de->de_rec_len,
928 de->de_file_type == EXT2_FT_DIR ? 'd' : 'f');
929 return de;
930 }
931
ext2_add_direntry(struct ext2_inode * dir,struct ext2_direntry * entry)932 static int ext2_add_direntry(struct ext2_inode *dir, struct ext2_direntry *entry)
933 {
934 LOG_DBG("Adding entry: {in=%d type=%d name_len=%d} to directory (in=%d)",
935 entry->de_inode, entry->de_file_type, entry->de_name_len, dir->i_id);
936
937 int rc = 0;
938 uint32_t block_size = dir->i_fs->block_size;
939 uint32_t entry_size = sizeof(struct ext2_disk_direntry) + entry->de_name_len;
940
941 if (entry_size > block_size) {
942 return -EINVAL;
943 }
944
945 /* Find last entry */
946 /* get last block and start from first entry on that block */
947 int last_blk = (dir->i_size / block_size) - 1;
948
949 rc = ext2_fetch_inode_block(dir, last_blk);
950 if (rc < 0) {
951 return rc;
952 }
953
954 uint32_t offset = 0;
955 uint16_t reclen;
956
957 struct ext2_disk_direntry *de = 0;
958
959 /* loop must be executed at least once, because block_size > 0 */
960 while (offset < block_size) {
961 de = EXT2_DISK_DIRENTRY_BY_OFFSET(inode_current_block_mem(dir), offset);
962 reclen = ext2_get_disk_direntry_reclen(de);
963 if (offset + reclen == block_size) {
964 break;
965 }
966 offset += reclen;
967 }
968
969
970 uint32_t occupied = sizeof(struct ext2_disk_direntry) + ext2_get_disk_direntry_namelen(de);
971
972 /* Align to 4 bytes */
973 occupied = ROUND_UP(occupied, 4);
974
975 LOG_DBG("Occupied: %d total: %d needed: %d", occupied, reclen, entry_size);
976
977 if (reclen - occupied >= entry_size) {
978 /* Entry fits into current block */
979 offset += occupied;
980 entry->de_rec_len = block_size - offset;
981 ext2_set_disk_direntry_reclen(de, occupied);
982 } else {
983 LOG_DBG("Allocating new block for directory");
984
985 /* Have to allocate new block */
986 rc = ext2_fetch_inode_block(dir, last_blk + 1);
987 if (rc < 0) {
988 return rc;
989 }
990
991 /* Increase size of directory */
992 dir->i_size += block_size;
993 rc = ext2_commit_inode(dir);
994 if (rc < 0) {
995 return rc;
996 }
997 rc = ext2_commit_inode_block(dir);
998 if (rc < 0) {
999 return rc;
1000 }
1001
1002 /* New entry will start at offset 0 */
1003 offset = 0;
1004 entry->de_rec_len = block_size;
1005 }
1006
1007 LOG_DBG("Writing entry {in=%d type=%d rec_len=%d name_len=%d} to block %d of inode %d",
1008 entry->de_inode, entry->de_file_type, entry->de_rec_len, entry->de_name_len,
1009 inode_current_block(dir)->num, dir->i_id);
1010
1011
1012 de = EXT2_DISK_DIRENTRY_BY_OFFSET(inode_current_block_mem(dir), offset);
1013 ext2_write_direntry(de, entry);
1014
1015 rc = ext2_commit_inode_block(dir);
1016 return rc;
1017 }
1018
ext2_create_file(struct ext2_inode * parent,struct ext2_inode * new_inode,struct ext2_lookup_args * args)1019 int ext2_create_file(struct ext2_inode *parent, struct ext2_inode *new_inode,
1020 struct ext2_lookup_args *args)
1021 {
1022 int rc, ret = 0;
1023 struct ext2_direntry *entry;
1024 struct ext2_data *fs = parent->i_fs;
1025
1026 rc = ext2_create_inode(fs, args->inode, new_inode, FS_DIR_ENTRY_FILE);
1027 if (rc < 0) {
1028 return rc;
1029 }
1030
1031 entry = ext2_create_direntry(args->path + args->name_pos, args->name_len, new_inode->i_id,
1032 EXT2_FT_REG_FILE);
1033
1034 rc = ext2_add_direntry(parent, entry);
1035 if (rc < 0) {
1036 ret = rc;
1037 goto out;
1038 }
1039
1040 /* Successfully added to directory */
1041 new_inode->i_links_count += 1;
1042
1043 rc = ext2_commit_inode(new_inode);
1044 if (rc < 0) {
1045 ret = rc;
1046 }
1047 out:
1048 k_heap_free(&direntry_heap, entry);
1049 return ret;
1050 }
1051
ext2_create_dir(struct ext2_inode * parent,struct ext2_inode * new_inode,struct ext2_lookup_args * args)1052 int ext2_create_dir(struct ext2_inode *parent, struct ext2_inode *new_inode,
1053 struct ext2_lookup_args *args)
1054 {
1055 int rc, ret = 0;
1056 struct ext2_direntry *entry;
1057 struct ext2_disk_direntry *disk_de;
1058 struct ext2_data *fs = parent->i_fs;
1059 uint32_t block_size = parent->i_fs->block_size;
1060
1061 rc = ext2_create_inode(fs, args->inode, new_inode, FS_DIR_ENTRY_DIR);
1062 if (rc < 0) {
1063 return rc;
1064 }
1065
1066 /* Directory must have at least one block */
1067 new_inode->i_size = block_size;
1068
1069 entry = ext2_create_direntry(args->path + args->name_pos, args->name_len, new_inode->i_id,
1070 EXT2_FT_DIR);
1071
1072 rc = ext2_add_direntry(parent, entry);
1073 if (rc < 0) {
1074 ret = rc;
1075 goto out;
1076 }
1077
1078 /* Successfully added to directory */
1079 new_inode->i_links_count += 1;
1080
1081 k_heap_free(&direntry_heap, entry);
1082
1083 /* Create "." directory entry */
1084 entry = ext2_create_direntry(".", 1, new_inode->i_id, EXT2_FT_DIR);
1085 entry->de_rec_len = block_size;
1086
1087 /* It has to be inserted manually */
1088 rc = ext2_fetch_inode_block(new_inode, 0);
1089 if (rc < 0) {
1090 ret = rc;
1091 goto out;
1092 }
1093
1094 disk_de = EXT2_DISK_DIRENTRY_BY_OFFSET(inode_current_block_mem(new_inode), 0);
1095 ext2_write_direntry(disk_de, entry);
1096
1097 new_inode->i_links_count += 1;
1098
1099 k_heap_free(&direntry_heap, entry);
1100
1101 /* Add ".." directory entry */
1102 entry = ext2_create_direntry("..", 2, parent->i_id, EXT2_FT_DIR);
1103
1104 rc = ext2_add_direntry(new_inode, entry);
1105 if (rc < 0) {
1106 ret = rc;
1107 goto out;
1108 }
1109
1110 /* Successfully added to directory */
1111 parent->i_links_count += 1;
1112
1113 rc = ext2_commit_inode_block(new_inode);
1114 if (rc < 0) {
1115 ret = rc;
1116 }
1117
1118 rc = ext2_commit_inode_block(parent);
1119 if (rc < 0) {
1120 ret = rc;
1121 }
1122
1123 /* Commit inodes after increasing link counts */
1124 rc = ext2_commit_inode(new_inode);
1125 if (rc < 0) {
1126 ret = rc;
1127 }
1128
1129 rc = ext2_commit_inode(parent);
1130 if (rc < 0) {
1131 ret = rc;
1132 }
1133 out:
1134 k_heap_free(&direntry_heap, entry);
1135 return ret;
1136 }
1137
ext2_del_direntry(struct ext2_inode * parent,uint32_t offset)1138 static int ext2_del_direntry(struct ext2_inode *parent, uint32_t offset)
1139 {
1140 int rc = 0;
1141 uint32_t block_size = parent->i_fs->block_size;
1142
1143 uint32_t blk = offset / block_size;
1144 uint32_t blk_off = offset % block_size;
1145
1146 rc = ext2_fetch_inode_block(parent, blk);
1147 if (rc < 0) {
1148 return rc;
1149 }
1150
1151 if (blk_off == 0) {
1152 struct ext2_disk_direntry *de =
1153 EXT2_DISK_DIRENTRY_BY_OFFSET(inode_current_block_mem(parent), 0);
1154 uint16_t reclen = ext2_get_disk_direntry_reclen(de);
1155
1156 if (reclen == block_size) {
1157 /* Remove whole block */
1158
1159 uint32_t last_blk = parent->i_size / block_size - 1;
1160 uint32_t old_blk = parent->i_block[blk];
1161
1162 /* move last block in place of removed one. Entries start only at beginning
1163 * of the block, hence we don't have to care to move any entry.
1164 */
1165 parent->i_block[blk] = parent->i_block[last_blk];
1166 parent->i_block[last_blk] = 0;
1167
1168 /* Free removed block */
1169 rc = ext2_free_block(parent->i_fs, old_blk);
1170 if (rc < 0) {
1171 return rc;
1172 }
1173
1174 rc = ext2_commit_inode(parent);
1175 if (rc < 0) {
1176 return rc;
1177 }
1178 } else {
1179 /* Move next entry to beginning of block */
1180 struct ext2_disk_direntry *next =
1181 EXT2_DISK_DIRENTRY_BY_OFFSET(inode_current_block_mem(parent), reclen);
1182 uint16_t next_reclen = ext2_get_disk_direntry_reclen(next);
1183
1184 memmove(de, next, next_reclen);
1185 ext2_set_disk_direntry_reclen(de, reclen + next_reclen);
1186
1187 rc = ext2_commit_inode_block(parent);
1188 if (rc < 0) {
1189 return rc;
1190 }
1191 }
1192
1193 } else {
1194 /* Entry inside the block */
1195 uint32_t cur = 0;
1196 uint16_t reclen;
1197
1198 struct ext2_disk_direntry *de =
1199 EXT2_DISK_DIRENTRY_BY_OFFSET(inode_current_block_mem(parent), 0);
1200
1201 reclen = ext2_get_disk_direntry_reclen(de);
1202 /* find previous entry */
1203 while (cur + reclen < blk_off) {
1204 cur += reclen;
1205 de = EXT2_DISK_DIRENTRY_BY_OFFSET(inode_current_block_mem(parent), cur);
1206 reclen = ext2_get_disk_direntry_reclen(de);
1207 }
1208
1209 struct ext2_disk_direntry *del_entry =
1210 EXT2_DISK_DIRENTRY_BY_OFFSET(inode_current_block_mem(parent), blk_off);
1211 uint16_t del_reclen = ext2_get_disk_direntry_reclen(del_entry);
1212
1213 ext2_set_disk_direntry_reclen(de, reclen + del_reclen);
1214 rc = ext2_commit_inode_block(parent);
1215 if (rc < 0) {
1216 return rc;
1217 }
1218 }
1219
1220 return 0;
1221 }
1222
remove_inode(struct ext2_inode * inode)1223 static int remove_inode(struct ext2_inode *inode)
1224 {
1225 int ret = 0;
1226
1227 LOG_DBG("inode: %d", inode->i_id);
1228
1229 /* Free blocks of inode */
1230 ret = ext2_inode_remove_blocks(inode, 0);
1231 if (ret < 0) {
1232 return ret;
1233 }
1234
1235 /* Free inode */
1236 ret = ext2_free_inode(inode->i_fs, inode->i_id, IS_DIR(inode->i_mode));
1237 return ret;
1238 }
1239
can_unlink(struct ext2_inode * inode)1240 static int can_unlink(struct ext2_inode *inode)
1241 {
1242 if (!IS_DIR(inode->i_mode)) {
1243 return 0;
1244 }
1245
1246 int rc = 0;
1247
1248 rc = ext2_fetch_inode_block(inode, 0);
1249 if (rc < 0) {
1250 return rc;
1251 }
1252
1253 /* If directory check if it is empty */
1254
1255 uint32_t offset = 0;
1256 struct ext2_disk_direntry *de;
1257
1258 /* Get first entry */
1259 de = EXT2_DISK_DIRENTRY_BY_OFFSET(inode_current_block_mem(inode), 0);
1260 offset += ext2_get_disk_direntry_reclen(de);
1261
1262 /* Get second entry */
1263 de = EXT2_DISK_DIRENTRY_BY_OFFSET(inode_current_block_mem(inode), offset);
1264 offset += ext2_get_disk_direntry_reclen(de);
1265
1266 uint32_t block_size = inode->i_fs->block_size;
1267
1268 /* If directory has size of one block and second entry ends with block end
1269 * then directory is empty.
1270 */
1271 if (offset == block_size && inode->i_size == block_size) {
1272 return 0;
1273 }
1274
1275 return -ENOTEMPTY;
1276 }
1277
ext2_inode_unlink(struct ext2_inode * parent,struct ext2_inode * inode,uint32_t offset)1278 int ext2_inode_unlink(struct ext2_inode *parent, struct ext2_inode *inode, uint32_t offset)
1279 {
1280 int rc;
1281
1282 rc = can_unlink(inode);
1283 if (rc < 0) {
1284 return rc;
1285 }
1286
1287 rc = ext2_del_direntry(parent, offset);
1288 if (rc < 0) {
1289 return rc;
1290 }
1291
1292 if ((IS_REG_FILE(inode->i_mode) && inode->i_links_count == 1) ||
1293 (IS_DIR(inode->i_mode) && inode->i_links_count == 2)) {
1294
1295 /* Only set the flag. Inode may still be open. Inode will be
1296 * removed after dropping all references to it.
1297 */
1298 inode->flags |= INODE_REMOVE;
1299 }
1300
1301 inode->i_links_count -= 1;
1302 rc = ext2_commit_inode(inode);
1303 if (rc < 0) {
1304 return rc;
1305 }
1306 return 0;
1307 }
1308
ext2_replace_file(struct ext2_lookup_args * args_from,struct ext2_lookup_args * args_to)1309 int ext2_replace_file(struct ext2_lookup_args *args_from, struct ext2_lookup_args *args_to)
1310 {
1311 LOG_DBG("Replace existing directory entry in rename");
1312 LOG_DBG("Inode: %d Inode to replace: %d", args_from->inode->i_id, args_to->inode->i_id);
1313
1314 int rc = 0;
1315 struct ext2_disk_direntry *de;
1316
1317 uint32_t block_size = args_from->parent->i_fs->block_size;
1318 uint32_t from_offset = args_from->offset;
1319 uint32_t from_blk = from_offset / block_size;
1320 uint32_t from_blk_off = from_offset % block_size;
1321
1322 rc = ext2_fetch_inode_block(args_from->parent, from_blk);
1323 if (rc < 0) {
1324 return rc;
1325 }
1326
1327 de = EXT2_DISK_DIRENTRY_BY_OFFSET(inode_current_block_mem(args_from->parent), from_blk_off);
1328
1329 /* record file type */
1330 uint8_t file_type = ext2_get_disk_direntry_type(de);
1331
1332 /* NOTE: Replace the inode number in removed entry with inode of file that will be replaced
1333 * with new one. Thanks to that we can use the function that unlinks directory entry to get
1334 * rid of old directory entry and link to inode that will no longer be referenced by the
1335 * directory entry after it is replaced with moved file.
1336 */
1337 ext2_set_disk_direntry_inode(de, args_to->inode->i_id);
1338 rc = ext2_inode_unlink(args_from->parent, args_to->inode, args_from->offset);
1339 if (rc < 0) {
1340 /* restore the old inode number */
1341 ext2_set_disk_direntry_inode(de, args_from->inode->i_id);
1342 return rc;
1343 }
1344
1345 uint32_t to_offset = args_to->offset;
1346 uint32_t to_blk = to_offset / block_size;
1347 uint32_t to_blk_off = to_offset % block_size;
1348
1349 rc = ext2_fetch_inode_block(args_to->parent, to_blk);
1350 if (rc < 0) {
1351 return rc;
1352 }
1353
1354 de = EXT2_DISK_DIRENTRY_BY_OFFSET(inode_current_block_mem(args_to->parent), to_blk_off);
1355
1356 /* change inode of new entry */
1357 ext2_set_disk_direntry_inode(de, args_from->inode->i_id);
1358 ext2_set_disk_direntry_type(de, file_type);
1359
1360 rc = ext2_commit_inode_block(args_to->parent);
1361 if (rc < 0) {
1362 return rc;
1363 }
1364 return 0;
1365 }
1366
ext2_move_file(struct ext2_lookup_args * args_from,struct ext2_lookup_args * args_to)1367 int ext2_move_file(struct ext2_lookup_args *args_from, struct ext2_lookup_args *args_to)
1368 {
1369 int rc = 0;
1370 uint32_t block_size = args_from->parent->i_fs->block_size;
1371
1372 struct ext2_inode *fparent = args_from->parent;
1373 struct ext2_inode *tparent = args_to->parent;
1374 uint32_t offset = args_from->offset;
1375 uint32_t blk = offset / block_size;
1376 uint32_t blk_off = offset % block_size;
1377
1378 /* Check if we could just modify existing entry */
1379 if (fparent->i_id == tparent->i_id) {
1380 rc = ext2_fetch_inode_block(fparent, blk);
1381 if (rc < 0) {
1382 return rc;
1383 }
1384
1385 struct ext2_disk_direntry *de;
1386
1387 de = EXT2_DISK_DIRENTRY_BY_OFFSET(inode_current_block_mem(fparent), blk_off);
1388
1389 uint16_t reclen = ext2_get_disk_direntry_reclen(de);
1390
1391 /* If new name fits in old entry, then just copy it there */
1392 if (reclen - sizeof(struct ext2_disk_direntry) >= args_to->name_len) {
1393 LOG_DBG("Old entry is modified to hold new name");
1394 ext2_set_disk_direntry_namelen(de, args_to->name_len);
1395 ext2_set_disk_direntry_name(de, args_to->path + args_to->name_pos,
1396 args_to->name_len);
1397
1398 rc = ext2_commit_inode_block(fparent);
1399 return rc;
1400 }
1401 }
1402
1403 LOG_DBG("Create new directory entry in rename");
1404
1405 int ret = 0;
1406
1407 rc = ext2_fetch_inode_block(fparent, blk);
1408 if (rc < 0) {
1409 return rc;
1410 }
1411
1412 struct ext2_disk_direntry *old_de;
1413 struct ext2_direntry *new_de;
1414
1415 old_de = EXT2_DISK_DIRENTRY_BY_OFFSET(inode_current_block_mem(fparent), blk_off);
1416
1417 uint32_t inode = ext2_get_disk_direntry_inode(old_de);
1418 uint8_t file_type = ext2_get_disk_direntry_type(old_de);
1419
1420 new_de = ext2_create_direntry(args_to->path + args_to->name_pos, args_to->name_len, inode,
1421 file_type);
1422
1423 rc = ext2_add_direntry(tparent, new_de);
1424 if (rc < 0) {
1425 ret = rc;
1426 goto out;
1427 }
1428
1429 rc = ext2_del_direntry(fparent, args_from->offset);
1430 if (rc < 0) {
1431 return rc;
1432 }
1433
1434 out:
1435 k_heap_free(&direntry_heap, new_de);
1436 return ret;
1437 }
1438
ext2_inode_get(struct ext2_data * fs,uint32_t ino,struct ext2_inode ** ret)1439 int ext2_inode_get(struct ext2_data *fs, uint32_t ino, struct ext2_inode **ret)
1440 {
1441 int rc;
1442 struct ext2_inode *inode;
1443
1444 for (int i = 0; i < fs->open_inodes; ++i) {
1445 inode = fs->inode_pool[i];
1446
1447 if (inode->i_id == ino) {
1448 *ret = inode;
1449 inode->i_ref++;
1450 return 0;
1451 }
1452 }
1453
1454 if (fs->open_inodes >= MAX_INODES) {
1455 return -ENOMEM;
1456 }
1457
1458
1459 rc = k_mem_slab_alloc(&inode_struct_slab, (void **)&inode, K_FOREVER);
1460 if (rc < 0) {
1461 return -ENOMEM;
1462 }
1463 memset(inode, 0, sizeof(struct ext2_inode));
1464
1465 if (ino != 0) {
1466 int rc2 = ext2_fetch_inode(fs, ino, inode);
1467
1468 if (rc2 < 0) {
1469 k_mem_slab_free(&inode_struct_slab, (void *)inode);
1470 return rc2;
1471 }
1472 }
1473
1474 fs->inode_pool[fs->open_inodes] = inode;
1475 fs->open_inodes++;
1476
1477 inode->i_fs = fs;
1478 inode->i_ref = 1;
1479 *ret = inode;
1480 return 0;
1481 }
1482
ext2_inode_drop(struct ext2_inode * inode)1483 int ext2_inode_drop(struct ext2_inode *inode)
1484 {
1485 if (inode == NULL) {
1486 return 0;
1487 }
1488
1489 struct ext2_data *fs = inode->i_fs;
1490
1491 if (fs->open_inodes <= 0) {
1492 LOG_WRN("All inodes should be already closed");
1493 return 0;
1494 }
1495
1496 inode->i_ref--;
1497
1498 /* Clean inode if that was last reference */
1499 if (inode->i_ref == 0) {
1500
1501 /* find entry */
1502 uint32_t offset = 0;
1503
1504 while (offset < MAX_INODES && fs->inode_pool[offset] != inode) {
1505 offset++;
1506 }
1507
1508 if (offset >= MAX_INODES) {
1509 LOG_ERR("Inode structure at %p not in inode_pool", inode);
1510 return -EINVAL;
1511 }
1512
1513 ext2_inode_drop_blocks(inode);
1514
1515 if (inode->flags & INODE_REMOVE) {
1516 /* This is the inode that should be removed because
1517 * there was called unlink function on it.
1518 */
1519 int rc = remove_inode(inode);
1520
1521 if (rc < 0) {
1522 return rc;
1523 }
1524 }
1525
1526 k_mem_slab_free(&inode_struct_slab, (void *)inode);
1527
1528 /* copy last open in place of freed inode */
1529 uint32_t last = fs->open_inodes - 1;
1530
1531 fs->inode_pool[offset] = fs->inode_pool[last];
1532 fs->open_inodes--;
1533
1534 }
1535
1536 return 0;
1537 }
1538
ext2_inode_drop_blocks(struct ext2_inode * inode)1539 void ext2_inode_drop_blocks(struct ext2_inode *inode)
1540 {
1541 for (int i = 0; i < 4; ++i) {
1542 ext2_drop_block(inode->blocks[i]);
1543 }
1544 inode->flags &= ~INODE_FETCHED_BLOCK;
1545 }
1546