Linux codestyle format (tabs indenation)
[lwext4.git] / lwext4 / ext4.c
1 /*\r
2  * Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com)\r
3  * All rights reserved.\r
4  *\r
5  * Redistribution and use in source and binary forms, with or without\r
6  * modification, are permitted provided that the following conditions\r
7  * are met:\r
8  *\r
9  * - Redistributions of source code must retain the above copyright\r
10  *   notice, this list of conditions and the following disclaimer.\r
11  * - Redistributions in binary form must reproduce the above copyright\r
12  *   notice, this list of conditions and the following disclaimer in the\r
13  *   documentation and/or other materials provided with the distribution.\r
14  * - The name of the author may not be used to endorse or promote products\r
15  *   derived from this software without specific prior written permission.\r
16  *\r
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\r
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\r
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\r
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\r
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\r
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\r
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\r
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\r
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF\r
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
27  */\r
28 \r
29 /** @addtogroup lwext4\r
30  * @{\r
31  */\r
32 /**\r
33  * @file  ext4.h\r
34  * @brief Ext4 high level operations (file, directory, mountpoints...)\r
35  */\r
36 \r
37 #include "ext4_config.h"\r
38 #include "ext4_blockdev.h"\r
39 #include "ext4_types.h"\r
40 #include "ext4_debug.h"\r
41 #include "ext4_errno.h"\r
42 #include "ext4_fs.h"\r
43 #include "ext4_dir.h"\r
44 #include "ext4_inode.h"\r
45 #include "ext4_super.h"\r
46 #include "ext4_dir_idx.h"\r
47 #include "ext4.h"\r
48 \r
49 #include <stdlib.h>\r
50 #include <string.h>\r
51 \r
52 /**@brief   Mount point OS dependent lock*/\r
53 #define EXT4_MP_LOCK(_m)                                                       \\r
54         do {                                                                   \\r
55                 if ((_m)->os_locks)                                            \\r
56                         (_m)->os_locks->lock();                                \\r
57         } while (0)\r
58 \r
59 /**@brief   Mount point OS dependent unlock*/\r
60 #define EXT4_MP_UNLOCK(_m)                                                     \\r
61         do {                                                                   \\r
62                 if ((_m)->os_locks)                                            \\r
63                         (_m)->os_locks->unlock();                              \\r
64         } while (0)\r
65 \r
66 /**@brief   Mount point descriptor.*/\r
67 struct ext4_mountpoint {\r
68 \r
69         /**@brief   Mount done flag.*/\r
70         bool mounted;\r
71 \r
72         /**@brief   Mount point name (@ref ext4_mount)*/\r
73         char name[32];\r
74 \r
75         /**@brief   OS dependent lock/unlock functions.*/\r
76         const struct ext4_lock *os_locks;\r
77 \r
78         /**@brief   Ext4 filesystem internals.*/\r
79         struct ext4_fs fs;\r
80 \r
81         /**@brief   Dynamic allocation cache flag.*/\r
82         bool cache_dynamic;\r
83 };\r
84 \r
85 /**@brief   Block devices descriptor.*/\r
86 struct _ext4_devices {\r
87 \r
88         /**@brief   Block device name (@ref ext4_device_register)*/\r
89         char name[32];\r
90 \r
91         /**@brief   Block device handle.*/\r
92         struct ext4_blockdev *bd;\r
93 \r
94         /**@brief   Block cache handle.*/\r
95         struct ext4_bcache *bc;\r
96 };\r
97 \r
98 /**@brief   Block devices.*/\r
99 struct _ext4_devices _bdevices[CONFIG_EXT4_BLOCKDEVS_COUNT];\r
100 \r
101 /**@brief   Mountpoints.*/\r
102 struct ext4_mountpoint _mp[CONFIG_EXT4_MOUNTPOINTS_COUNT];\r
103 \r
104 int ext4_device_register(struct ext4_blockdev *bd, struct ext4_bcache *bc,\r
105                          const char *dev_name)\r
106 {\r
107         uint32_t i;\r
108         ext4_assert(bd && dev_name);\r
109 \r
110         for (i = 0; i < CONFIG_EXT4_BLOCKDEVS_COUNT; ++i) {\r
111                 if (!_bdevices[i].bd) {\r
112                         strcpy(_bdevices[i].name, dev_name);\r
113                         _bdevices[i].bd = bd;\r
114                         _bdevices[i].bc = bc;\r
115                         return EOK;\r
116                 }\r
117 \r
118                 if (!strcmp(_bdevices[i].name, dev_name))\r
119                         return EOK;\r
120         }\r
121         return ENOSPC;\r
122 }\r
123 \r
124 /****************************************************************************/\r
125 \r
126 static bool ext4_is_dots(const uint8_t *name, size_t name_size)\r
127 {\r
128         if ((name_size == 1) && (name[0] == '.'))\r
129                 return true;\r
130 \r
131         if ((name_size == 2) && (name[0] == '.') && (name[1] == '.'))\r
132                 return true;\r
133 \r
134         return false;\r
135 }\r
136 \r
137 static int ext4_has_children(bool *has_children, struct ext4_inode_ref *enode)\r
138 {\r
139         struct ext4_fs *fs = enode->fs;\r
140 \r
141         /* Check if node is directory */\r
142         if (!ext4_inode_is_type(&fs->sb, enode->inode,\r
143                                 EXT4_INODE_MODE_DIRECTORY)) {\r
144                 *has_children = false;\r
145                 return EOK;\r
146         }\r
147 \r
148         struct ext4_directory_iterator it;\r
149         int rc = ext4_dir_iterator_init(&it, enode, 0);\r
150         if (rc != EOK)\r
151                 return rc;\r
152 \r
153         /* Find a non-empty directory entry */\r
154         bool found = false;\r
155         while (it.current != NULL) {\r
156                 if (it.current->inode != 0) {\r
157                         uint16_t name_size = ext4_dir_entry_ll_get_name_length(\r
158                             &fs->sb, it.current);\r
159                         if (!ext4_is_dots(it.current->name, name_size)) {\r
160                                 found = true;\r
161                                 break;\r
162                         }\r
163                 }\r
164 \r
165                 rc = ext4_dir_iterator_next(&it);\r
166                 if (rc != EOK) {\r
167                         ext4_dir_iterator_fini(&it);\r
168                         return rc;\r
169                 }\r
170         }\r
171 \r
172         rc = ext4_dir_iterator_fini(&it);\r
173         if (rc != EOK)\r
174                 return rc;\r
175 \r
176         *has_children = found;\r
177 \r
178         return EOK;\r
179 }\r
180 \r
181 static int ext4_link(struct ext4_mountpoint *mp, struct ext4_inode_ref *parent,\r
182                      struct ext4_inode_ref *child, const char *name,\r
183                      uint32_t name_len)\r
184 {\r
185         /* Check maximum name length */\r
186         if (name_len > EXT4_DIRECTORY_FILENAME_LEN)\r
187                 return EINVAL;\r
188 \r
189         /* Add entry to parent directory */\r
190         int rc = ext4_dir_add_entry(parent, name, name_len, child);\r
191         if (rc != EOK)\r
192                 return rc;\r
193 \r
194         /* Fill new dir -> add '.' and '..' entries.\r
195          * Also newly allocated inode should have 0 link count.\r
196          */\r
197         if (ext4_inode_is_type(&mp->fs.sb, child->inode,\r
198                                EXT4_INODE_MODE_DIRECTORY) &&\r
199             ext4_inode_get_links_count(child->inode) == 0) {\r
200                 rc = ext4_dir_add_entry(child, ".", strlen("."), child);\r
201                 if (rc != EOK) {\r
202                         ext4_dir_remove_entry(parent, name, strlen(name));\r
203                         return rc;\r
204                 }\r
205 \r
206                 rc = ext4_dir_add_entry(child, "..", strlen(".."), parent);\r
207                 if (rc != EOK) {\r
208                         ext4_dir_remove_entry(parent, name, strlen(name));\r
209                         ext4_dir_remove_entry(child, ".", strlen("."));\r
210                         return rc;\r
211                 }\r
212 \r
213                 /*New empty directory. Two links (. and ..) */\r
214                 ext4_inode_set_links_count(child->inode, 2);\r
215 \r
216 #if CONFIG_DIR_INDEX_ENABLE\r
217                 /* Initialize directory index if supported */\r
218                 if (ext4_sb_has_feature_compatible(\r
219                         &mp->fs.sb, EXT4_FEATURE_COMPAT_DIR_INDEX)) {\r
220                         rc = ext4_dir_dx_init(child);\r
221                         if (rc != EOK)\r
222                                 return rc;\r
223 \r
224                         ext4_inode_set_flag(child->inode,\r
225                                             EXT4_INODE_FLAG_INDEX);\r
226                         child->dirty = true;\r
227                 }\r
228 #endif\r
229 \r
230                 ext4_fs_inode_links_count_inc(parent);\r
231                 child->dirty = true;\r
232                 parent->dirty = true;\r
233         } else {\r
234                 if (ext4_inode_is_type(&mp->fs.sb, child->inode,\r
235                                        EXT4_INODE_MODE_DIRECTORY)) {\r
236                         /* FIXME: SO TRICKY. */\r
237                         int has_flag_index = ext4_inode_has_flag(\r
238                             child->inode, EXT4_INODE_FLAG_INDEX);\r
239                         struct ext4_directory_search_result result;\r
240                         if (has_flag_index)\r
241                                 ext4_inode_clear_flag(child->inode,\r
242                                                       EXT4_INODE_FLAG_INDEX);\r
243 \r
244                         rc = ext4_dir_find_entry(&result, child, "..",\r
245                                                  strlen(".."));\r
246                         if (has_flag_index)\r
247                                 ext4_inode_set_flag(child->inode,\r
248                                                     EXT4_INODE_FLAG_INDEX);\r
249 \r
250                         if (rc != EOK)\r
251                                 return EIO;\r
252 \r
253                         ext4_dir_entry_ll_set_inode(result.dentry,\r
254                                                     parent->index);\r
255                         result.block.dirty = true;\r
256                         rc = ext4_dir_destroy_result(child, &result);\r
257                         if (rc != EOK)\r
258                                 return rc;\r
259 \r
260                         ext4_fs_inode_links_count_inc(parent);\r
261                         parent->dirty = true;\r
262                 } else {\r
263                         ext4_fs_inode_links_count_inc(child);\r
264                         child->dirty = true;\r
265                 }\r
266         }\r
267 \r
268         return rc;\r
269 }\r
270 \r
271 static int ext4_unlink(struct ext4_mountpoint *mp,\r
272                        struct ext4_inode_ref *parent,\r
273                        struct ext4_inode_ref *child_inode_ref, const char *name,\r
274                        uint32_t name_len)\r
275 {\r
276         bool has_children;\r
277         int rc = ext4_has_children(&has_children, child_inode_ref);\r
278         if (rc != EOK)\r
279                 return rc;\r
280 \r
281         /* Cannot unlink non-empty node */\r
282         if (has_children)\r
283                 return ENOTSUP;\r
284 \r
285         /* Remove entry from parent directory */\r
286         rc = ext4_dir_remove_entry(parent, name, name_len);\r
287         if (rc != EOK)\r
288                 return rc;\r
289 \r
290         bool is_dir = ext4_inode_is_type(&mp->fs.sb, child_inode_ref->inode,\r
291                                          EXT4_INODE_MODE_DIRECTORY);\r
292 \r
293         /* If directory - handle links from parent */\r
294         if (is_dir) {\r
295                 // ext4_assert(ext4_inode_get_links_count(child_inode_ref->inode)\r
296                 // == 1);\r
297                 ext4_fs_inode_links_count_dec(parent);\r
298                 parent->dirty = true;\r
299         }\r
300 \r
301         /*\r
302          * TODO: Update timestamps of the parent\r
303          * (when we have wall-clock time).\r
304          *\r
305          * ext4_inode_set_change_inode_time(parent->inode, (uint32_t) now);\r
306          * ext4_inode_set_modification_time(parent->inode, (uint32_t) now);\r
307          * parent->dirty = true;\r
308          */\r
309 \r
310         /*\r
311          * TODO: Update timestamp for inode.\r
312          *\r
313          * ext4_inode_set_change_inode_time(child_inode_ref->inode,\r
314          *     (uint32_t) now);\r
315          */\r
316         if (ext4_inode_get_links_count(child_inode_ref->inode)) {\r
317                 ext4_fs_inode_links_count_dec(child_inode_ref);\r
318                 child_inode_ref->dirty = true;\r
319         }\r
320 \r
321         return EOK;\r
322 }\r
323 \r
324 /****************************************************************************/\r
325 \r
326 int ext4_mount(const char *dev_name, const char *mount_point)\r
327 {\r
328         ext4_assert(mount_point && dev_name);\r
329         int r;\r
330         int i;\r
331 \r
332         uint32_t bsize;\r
333         struct ext4_blockdev *bd = 0;\r
334         struct ext4_bcache *bc = 0;\r
335         struct ext4_mountpoint *mp = 0;\r
336 \r
337         if (mount_point[strlen(mount_point) - 1] != '/')\r
338                 return ENOTSUP;\r
339 \r
340         for (i = 0; i < CONFIG_EXT4_BLOCKDEVS_COUNT; ++i) {\r
341                 if (_bdevices[i].name) {\r
342                         if (!strcmp(dev_name, _bdevices[i].name)) {\r
343                                 bd = _bdevices[i].bd;\r
344                                 bc = _bdevices[i].bc;\r
345                                 break;\r
346                         }\r
347                 }\r
348         }\r
349 \r
350         if (!bd)\r
351                 return ENODEV;\r
352 \r
353         for (i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) {\r
354                 if (!_mp[i].mounted) {\r
355                         strcpy(_mp[i].name, mount_point);\r
356                         _mp[i].mounted = 1;\r
357                         mp = &_mp[i];\r
358                         break;\r
359                 }\r
360 \r
361                 if (!strcmp(_mp[i].name, mount_point))\r
362                         return EOK;\r
363         }\r
364 \r
365         if (!mp)\r
366                 return ENOMEM;\r
367 \r
368         r = ext4_block_init(bd);\r
369         if (r != EOK)\r
370                 return r;\r
371 \r
372         r = ext4_fs_init(&mp->fs, bd);\r
373         if (r != EOK) {\r
374                 ext4_block_fini(bd);\r
375                 return r;\r
376         }\r
377 \r
378         bsize = ext4_sb_get_block_size(&mp->fs.sb);\r
379         ext4_block_set_lb_size(bd, bsize);\r
380 \r
381         mp->cache_dynamic = 0;\r
382 \r
383         if (!bc) {\r
384                 /*Automatic block cache alloc.*/\r
385                 mp->cache_dynamic = 1;\r
386                 bc = malloc(sizeof(struct ext4_bcache));\r
387 \r
388                 r = ext4_bcache_init_dynamic(bc, CONFIG_BLOCK_DEV_CACHE_SIZE,\r
389                                              bsize);\r
390                 if (r != EOK) {\r
391                         free(bc);\r
392                         ext4_block_fini(bd);\r
393                         return r;\r
394                 }\r
395         }\r
396 \r
397         if (bsize != bc->itemsize)\r
398                 return ENOTSUP;\r
399 \r
400         /*Bind block cache to block device*/\r
401         r = ext4_block_bind_bcache(bd, bc);\r
402         if (r != EOK) {\r
403                 ext4_block_fini(bd);\r
404                 if (mp->cache_dynamic) {\r
405                         ext4_bcache_fini_dynamic(bc);\r
406                         free(bc);\r
407                 }\r
408                 return r;\r
409         }\r
410 \r
411         return r;\r
412 }\r
413 \r
414 int ext4_umount(const char *mount_point)\r
415 {\r
416         int i;\r
417         int r;\r
418         struct ext4_mountpoint *mp = 0;\r
419 \r
420         for (i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) {\r
421                 if (!strcmp(_mp[i].name, mount_point)) {\r
422                         mp = &_mp[i];\r
423                         break;\r
424                 }\r
425         }\r
426 \r
427         if (!mp)\r
428                 return ENODEV;\r
429 \r
430         r = ext4_fs_fini(&mp->fs);\r
431         if (r != EOK)\r
432                 return r;\r
433 \r
434         mp->mounted = 0;\r
435 \r
436         if (mp->cache_dynamic) {\r
437                 ext4_bcache_fini_dynamic(mp->fs.bdev->bc);\r
438                 free(mp->fs.bdev->bc);\r
439         }\r
440 \r
441         return ext4_block_fini(mp->fs.bdev);\r
442 }\r
443 \r
444 int ext4_mount_point_stats(const char *mount_point,\r
445                            struct ext4_mount_stats *stats)\r
446 {\r
447         uint32_t i;\r
448         struct ext4_mountpoint *mp = 0;\r
449 \r
450         for (i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) {\r
451                 if (!strcmp(_mp[i].name, mount_point)) {\r
452                         mp = &_mp[i];\r
453                         break;\r
454                 }\r
455         }\r
456         if (!mp)\r
457                 return ENOENT;\r
458 \r
459         EXT4_MP_LOCK(mp);\r
460         stats->inodes_count = ext4_get32(&mp->fs.sb, inodes_count);\r
461         stats->free_inodes_count = ext4_get32(&mp->fs.sb, free_inodes_count);\r
462         stats->blocks_count = ext4_sb_get_blocks_cnt(&mp->fs.sb);\r
463         stats->free_blocks_count = ext4_sb_get_free_blocks_cnt(&mp->fs.sb);\r
464         stats->block_size = ext4_sb_get_block_size(&mp->fs.sb);\r
465 \r
466         stats->block_group_count = ext4_block_group_cnt(&mp->fs.sb);\r
467         stats->blocks_per_group = ext4_get32(&mp->fs.sb, blocks_per_group);\r
468         stats->inodes_per_group = ext4_get32(&mp->fs.sb, inodes_per_group);\r
469 \r
470         memcpy(stats->volume_name, mp->fs.sb.volume_name, 16);\r
471         EXT4_MP_UNLOCK(mp);\r
472 \r
473         return EOK;\r
474 }\r
475 \r
476 int ext4_mount_setup_locks(const char *mount_point,\r
477                            const struct ext4_lock *locks)\r
478 {\r
479         uint32_t i;\r
480         struct ext4_mountpoint *mp = 0;\r
481 \r
482         for (i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) {\r
483                 if (!strcmp(_mp[i].name, mount_point)) {\r
484                         mp = &_mp[i];\r
485                         break;\r
486                 }\r
487         }\r
488         if (!mp)\r
489                 return ENOENT;\r
490 \r
491         mp->os_locks = locks;\r
492         return EOK;\r
493 }\r
494 \r
495 /********************************FILE OPERATIONS*****************************/\r
496 \r
497 static struct ext4_mountpoint *ext4_get_mount(const char *path)\r
498 {\r
499         int i;\r
500         for (i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) {\r
501 \r
502                 if (!_mp[i].mounted)\r
503                         continue;\r
504 \r
505                 if (!strncmp(_mp[i].name, path, strlen(_mp[i].name)))\r
506                         return &_mp[i];\r
507         }\r
508         return 0;\r
509 }\r
510 \r
511 static int ext4_path_check(const char *path, bool *is_goal)\r
512 {\r
513         int i;\r
514 \r
515         for (i = 0; i < EXT4_DIRECTORY_FILENAME_LEN; ++i) {\r
516 \r
517                 if (path[i] == '/') {\r
518                         *is_goal = false;\r
519                         return i;\r
520                 }\r
521 \r
522                 if (path[i] == 0) {\r
523                         *is_goal = true;\r
524                         return i;\r
525                 }\r
526         }\r
527 \r
528         return 0;\r
529 }\r
530 \r
531 static bool ext4_parse_flags(const char *flags, uint32_t *file_flags)\r
532 {\r
533         if (!flags)\r
534                 return false;\r
535 \r
536         if (!strcmp(flags, "r") || !strcmp(flags, "rb")) {\r
537                 *file_flags = O_RDONLY;\r
538                 return true;\r
539         }\r
540 \r
541         if (!strcmp(flags, "w") || !strcmp(flags, "wb")) {\r
542                 *file_flags = O_WRONLY | O_CREAT | O_TRUNC;\r
543                 return true;\r
544         }\r
545 \r
546         if (!strcmp(flags, "a") || !strcmp(flags, "ab")) {\r
547                 *file_flags = O_WRONLY | O_CREAT | O_APPEND;\r
548                 return true;\r
549         }\r
550 \r
551         if (!strcmp(flags, "r+") || !strcmp(flags, "rb+") ||\r
552             !strcmp(flags, "r+b")) {\r
553                 *file_flags = O_RDWR;\r
554                 return true;\r
555         }\r
556 \r
557         if (!strcmp(flags, "w+") || !strcmp(flags, "wb+") ||\r
558             !strcmp(flags, "w+b")) {\r
559                 *file_flags = O_RDWR | O_CREAT | O_TRUNC;\r
560                 return true;\r
561         }\r
562 \r
563         if (!strcmp(flags, "a+") || !strcmp(flags, "ab+") ||\r
564             !strcmp(flags, "a+b")) {\r
565                 *file_flags = O_RDWR | O_CREAT | O_APPEND;\r
566                 return true;\r
567         }\r
568 \r
569         return false;\r
570 }\r
571 \r
572 /*\r
573  * NOTICE: if filetype is equal to EXT4_DIRECTORY_FILETYPE_UNKNOWN,\r
574  * any filetype of the target dir entry will be accepted.\r
575  */\r
576 static int ext4_generic_open2(ext4_file *f, const char *path, int flags,\r
577                               int filetype, uint32_t *parent_inode,\r
578                               uint32_t *name_off)\r
579 {\r
580         bool is_goal = false;\r
581         uint8_t inode_type = EXT4_DIRECTORY_FILETYPE_DIR;\r
582         uint32_t next_inode;\r
583 \r
584         int r;\r
585         struct ext4_mountpoint *mp = ext4_get_mount(path);\r
586         struct ext4_directory_search_result result;\r
587         struct ext4_inode_ref ref;\r
588 \r
589         f->mp = 0;\r
590 \r
591         if (!mp)\r
592                 return ENOENT;\r
593 \r
594         f->flags = flags;\r
595 \r
596         /*Skip mount point*/\r
597         path += strlen(mp->name);\r
598 \r
599         if (name_off)\r
600                 *name_off = strlen(mp->name);\r
601 \r
602         /*Load root*/\r
603         r = ext4_fs_get_inode_ref(&mp->fs, EXT4_INODE_ROOT_INDEX, &ref);\r
604 \r
605         if (r != EOK)\r
606                 return r;\r
607 \r
608         if (parent_inode)\r
609                 *parent_inode = ref.index;\r
610 \r
611         int len = ext4_path_check(path, &is_goal);\r
612 \r
613         while (1) {\r
614 \r
615                 len = ext4_path_check(path, &is_goal);\r
616 \r
617                 if (!len) {\r
618                         /*If root open was request.*/\r
619                         if (is_goal &&\r
620                             ((filetype == EXT4_DIRECTORY_FILETYPE_DIR) ||\r
621                              (filetype == EXT4_DIRECTORY_FILETYPE_UNKNOWN)))\r
622                                 break;\r
623 \r
624                         r = ENOENT;\r
625                         break;\r
626                 }\r
627 \r
628                 r = ext4_dir_find_entry(&result, &ref, path, len);\r
629                 if (r != EOK) {\r
630 \r
631                         if (r != ENOENT)\r
632                                 break;\r
633 \r
634                         if (!(f->flags & O_CREAT))\r
635                                 break;\r
636 \r
637                         /*O_CREAT allows create new entry*/\r
638                         struct ext4_inode_ref child_ref;\r
639                         r = ext4_fs_alloc_inode(\r
640                             &mp->fs, &child_ref,\r
641                             is_goal ? (filetype == EXT4_DIRECTORY_FILETYPE_DIR)\r
642                                     : true);\r
643                         if (r != EOK)\r
644                                 break;\r
645 \r
646                         /*Destroy last result*/\r
647                         ext4_dir_destroy_result(&ref, &result);\r
648 \r
649                         /*Link with root dir.*/\r
650                         r = ext4_link(mp, &ref, &child_ref, path, len);\r
651                         if (r != EOK) {\r
652                                 /*Fail. Free new inode.*/\r
653                                 ext4_fs_free_inode(&child_ref);\r
654                                 /*We do not want to write new inode.\r
655                                   But block has to be released.*/\r
656                                 child_ref.dirty = false;\r
657                                 ext4_fs_put_inode_ref(&child_ref);\r
658                                 break;\r
659                         }\r
660 \r
661                         ext4_fs_put_inode_ref(&child_ref);\r
662 \r
663                         continue;\r
664                 }\r
665 \r
666                 if (parent_inode)\r
667                         *parent_inode = ref.index;\r
668 \r
669                 next_inode = result.dentry->inode;\r
670                 inode_type =\r
671                     ext4_dir_entry_ll_get_inode_type(&mp->fs.sb, result.dentry);\r
672 \r
673                 r = ext4_dir_destroy_result(&ref, &result);\r
674                 if (r != EOK)\r
675                         break;\r
676 \r
677                 /*If expected file error*/\r
678                 if (inode_type != EXT4_DIRECTORY_FILETYPE_DIR && !is_goal) {\r
679                         r = ENOENT;\r
680                         break;\r
681                 }\r
682                 if (filetype != EXT4_DIRECTORY_FILETYPE_UNKNOWN) {\r
683                         if ((inode_type != filetype) && is_goal) {\r
684                                 r = ENOENT;\r
685                                 break;\r
686                         }\r
687                 }\r
688 \r
689                 r = ext4_fs_put_inode_ref(&ref);\r
690                 if (r != EOK)\r
691                         break;\r
692 \r
693                 r = ext4_fs_get_inode_ref(&mp->fs, next_inode, &ref);\r
694                 if (r != EOK)\r
695                         break;\r
696 \r
697                 if (is_goal)\r
698                         break;\r
699 \r
700                 path += len + 1;\r
701 \r
702                 if (name_off)\r
703                         *name_off += len + 1;\r
704         };\r
705 \r
706         if (r != EOK) {\r
707                 ext4_fs_put_inode_ref(&ref);\r
708                 return r;\r
709         }\r
710 \r
711         if (is_goal) {\r
712 \r
713                 if ((f->flags & O_TRUNC) &&\r
714                     (inode_type == EXT4_DIRECTORY_FILETYPE_REG_FILE)) {\r
715 \r
716                         r = ext4_fs_truncate_inode(&ref, 0);\r
717                         if (r != EOK) {\r
718                                 ext4_fs_put_inode_ref(&ref);\r
719                                 return r;\r
720                         }\r
721                 }\r
722 \r
723                 f->mp = mp;\r
724                 f->fsize = ext4_inode_get_size(&f->mp->fs.sb, ref.inode);\r
725                 f->inode = ref.index;\r
726                 f->fpos = 0;\r
727 \r
728                 if (f->flags & O_APPEND)\r
729                         f->fpos = f->fsize;\r
730         }\r
731 \r
732         r = ext4_fs_put_inode_ref(&ref);\r
733         return r;\r
734 }\r
735 \r
736 /****************************************************************************/\r
737 \r
738 static int ext4_generic_open(ext4_file *f, const char *path, const char *flags,\r
739                              bool file_expect, uint32_t *parent_inode,\r
740                              uint32_t *name_off)\r
741 {\r
742         uint32_t iflags;\r
743         int filetype;\r
744         if (ext4_parse_flags(flags, &iflags) == false)\r
745                 return EINVAL;\r
746 \r
747         if (file_expect == true)\r
748                 filetype = EXT4_DIRECTORY_FILETYPE_REG_FILE;\r
749         else\r
750                 filetype = EXT4_DIRECTORY_FILETYPE_DIR;\r
751 \r
752         return ext4_generic_open2(f, path, iflags, filetype, parent_inode,\r
753                                   name_off);\r
754 }\r
755 \r
756 static int __ext4_create_hardlink(const char *path,\r
757                                   struct ext4_inode_ref *child_ref)\r
758 {\r
759         bool is_goal = false;\r
760         uint8_t inode_type = EXT4_DIRECTORY_FILETYPE_DIR;\r
761         uint32_t next_inode;\r
762 \r
763         int r;\r
764         struct ext4_mountpoint *mp = ext4_get_mount(path);\r
765         struct ext4_directory_search_result result;\r
766         struct ext4_inode_ref ref;\r
767 \r
768         if (!mp)\r
769                 return ENOENT;\r
770 \r
771         /*Skip mount point*/\r
772         path += strlen(mp->name);\r
773 \r
774         /*Load root*/\r
775         r = ext4_fs_get_inode_ref(&mp->fs, EXT4_INODE_ROOT_INDEX, &ref);\r
776 \r
777         if (r != EOK)\r
778                 return r;\r
779 \r
780         int len = ext4_path_check(path, &is_goal);\r
781 \r
782         while (1) {\r
783 \r
784                 len = ext4_path_check(path, &is_goal);\r
785 \r
786                 if (!len) {\r
787                         /*If root open was request.*/\r
788                         if (is_goal)\r
789                                 r = EINVAL;\r
790                         else\r
791                                 r = ENOENT;\r
792                         break;\r
793                 }\r
794 \r
795                 r = ext4_dir_find_entry(&result, &ref, path, len);\r
796                 if (r != EOK) {\r
797 \r
798                         if (r != ENOENT || !is_goal)\r
799                                 break;\r
800 \r
801                         /*Destroy last result*/\r
802                         ext4_dir_destroy_result(&ref, &result);\r
803 \r
804                         /*Link with root dir.*/\r
805                         r = ext4_link(mp, &ref, child_ref, path, len);\r
806                         break;\r
807                 }\r
808 \r
809                 next_inode = result.dentry->inode;\r
810                 inode_type =\r
811                     ext4_dir_entry_ll_get_inode_type(&mp->fs.sb, result.dentry);\r
812 \r
813                 r = ext4_dir_destroy_result(&ref, &result);\r
814                 if (r != EOK)\r
815                         break;\r
816 \r
817                 if (inode_type == EXT4_DIRECTORY_FILETYPE_REG_FILE) {\r
818                         if (is_goal)\r
819                                 r = EEXIST;\r
820                         else\r
821                                 r = ENOENT;\r
822 \r
823                         break;\r
824                 }\r
825 \r
826                 r = ext4_fs_put_inode_ref(&ref);\r
827                 if (r != EOK)\r
828                         break;\r
829 \r
830                 r = ext4_fs_get_inode_ref(&mp->fs, next_inode, &ref);\r
831                 if (r != EOK)\r
832                         break;\r
833 \r
834                 if (is_goal)\r
835                         break;\r
836 \r
837                 path += len + 1;\r
838         };\r
839 \r
840         if (r != EOK) {\r
841                 ext4_fs_put_inode_ref(&ref);\r
842                 return r;\r
843         }\r
844 \r
845         r = ext4_fs_put_inode_ref(&ref);\r
846         return r;\r
847 }\r
848 \r
849 static int __ext4_get_inode_ref_remove_hardlink(const char *path,\r
850                                                 struct ext4_inode_ref *child)\r
851 {\r
852         ext4_file f;\r
853         uint32_t parent_inode;\r
854         uint32_t name_off;\r
855         bool is_goal;\r
856         int r;\r
857         int len;\r
858         struct ext4_inode_ref parent;\r
859         struct ext4_mountpoint *mp = ext4_get_mount(path);\r
860 \r
861         if (!mp)\r
862                 return ENOENT;\r
863 \r
864         r = ext4_generic_open2(&f, path, O_RDONLY,\r
865                                EXT4_DIRECTORY_FILETYPE_UNKNOWN, &parent_inode,\r
866                                &name_off);\r
867         if (r != EOK)\r
868                 return r;\r
869 \r
870         /*Load parent*/\r
871         r = ext4_fs_get_inode_ref(&mp->fs, parent_inode, &parent);\r
872         if (r != EOK) {\r
873                 return r;\r
874         }\r
875 \r
876         /*We have file to unlink. Load it.*/\r
877         r = ext4_fs_get_inode_ref(&mp->fs, f.inode, child);\r
878         if (r != EOK) {\r
879                 ext4_fs_put_inode_ref(&parent);\r
880                 return r;\r
881         }\r
882 \r
883         if (r != EOK)\r
884                 goto Finish;\r
885 \r
886         /*Set path*/\r
887         path += name_off;\r
888 \r
889         len = ext4_path_check(path, &is_goal);\r
890 \r
891         /*Unlink from parent*/\r
892         r = ext4_unlink(mp, &parent, child, path, len);\r
893         if (r != EOK)\r
894                 goto Finish;\r
895 \r
896 Finish:\r
897         if (r != EOK)\r
898                 ext4_fs_put_inode_ref(child);\r
899 \r
900         ext4_fs_put_inode_ref(&parent);\r
901         return r;\r
902 }\r
903 \r
904 int ext4_frename(const char *path, const char *new_path)\r
905 {\r
906         int r;\r
907         struct ext4_mountpoint *mp = ext4_get_mount(path);\r
908         struct ext4_inode_ref inode_ref;\r
909 \r
910         if (!mp)\r
911                 return ENOENT;\r
912 \r
913         EXT4_MP_LOCK(mp);\r
914 \r
915         r = __ext4_get_inode_ref_remove_hardlink(path, &inode_ref);\r
916         if (r != EOK)\r
917                 goto Finish;\r
918 \r
919         r = __ext4_create_hardlink(new_path, &inode_ref);\r
920         if (r != EOK)\r
921                 r = __ext4_create_hardlink(path, &inode_ref);\r
922 \r
923         ext4_fs_put_inode_ref(&inode_ref);\r
924 \r
925 Finish:\r
926         EXT4_MP_UNLOCK(mp);\r
927         return r;\r
928 }\r
929 \r
930 /****************************************************************************/\r
931 \r
932 int ext4_get_sblock(const char *mount_point, struct ext4_sblock **sb)\r
933 {\r
934         struct ext4_mountpoint *mp = ext4_get_mount(mount_point);\r
935 \r
936         if (!mp)\r
937                 return ENOENT;\r
938 \r
939         *sb = &mp->fs.sb;\r
940         return EOK;\r
941 }\r
942 \r
943 int ext4_cache_write_back(const char *path, bool on)\r
944 {\r
945         struct ext4_mountpoint *mp = ext4_get_mount(path);\r
946 \r
947         if (!mp)\r
948                 return ENOENT;\r
949 \r
950         EXT4_MP_LOCK(mp);\r
951         ext4_block_cache_write_back(mp->fs.bdev, on);\r
952         EXT4_MP_UNLOCK(mp);\r
953         return EOK;\r
954 }\r
955 \r
956 int ext4_fremove(const char *path)\r
957 {\r
958         ext4_file f;\r
959         uint32_t parent_inode;\r
960         uint32_t name_off;\r
961         bool is_goal;\r
962         int r;\r
963         int len;\r
964         struct ext4_inode_ref child;\r
965         struct ext4_inode_ref parent;\r
966         struct ext4_mountpoint *mp = ext4_get_mount(path);\r
967 \r
968         if (!mp)\r
969                 return ENOENT;\r
970 \r
971         EXT4_MP_LOCK(mp);\r
972         r = ext4_generic_open(&f, path, "r", true, &parent_inode, &name_off);\r
973         if (r != EOK) {\r
974                 EXT4_MP_UNLOCK(mp);\r
975                 return r;\r
976         }\r
977 \r
978         /*Load parent*/\r
979         r = ext4_fs_get_inode_ref(&mp->fs, parent_inode, &parent);\r
980         if (r != EOK) {\r
981                 EXT4_MP_UNLOCK(mp);\r
982                 return r;\r
983         }\r
984 \r
985         /*We have file to delete. Load it.*/\r
986         r = ext4_fs_get_inode_ref(&mp->fs, f.inode, &child);\r
987         if (r != EOK) {\r
988                 ext4_fs_put_inode_ref(&parent);\r
989                 EXT4_MP_UNLOCK(mp);\r
990                 return r;\r
991         }\r
992 \r
993         /*Set path*/\r
994         path += name_off;\r
995 \r
996         len = ext4_path_check(path, &is_goal);\r
997 \r
998         /*Unlink from parent*/\r
999         r = ext4_unlink(mp, &parent, &child, path, len);\r
1000         if (r != EOK)\r
1001                 goto Finish;\r
1002 \r
1003         /*Link count is zero, the inode should be freed. */\r
1004         if (!ext4_inode_get_links_count(child.inode)) {\r
1005                 printf("ttttt\n");\r
1006                 ext4_inode_set_deletion_time(child.inode, 0xFFFFFFFF);\r
1007                 /*Turncate*/\r
1008                 ext4_block_cache_write_back(mp->fs.bdev, 1);\r
1009                 /*Truncate may be IO heavy. Do it writeback cache mode.*/\r
1010                 r = ext4_fs_truncate_inode(&child, 0);\r
1011                 ext4_block_cache_write_back(mp->fs.bdev, 0);\r
1012 \r
1013                 if (r != EOK)\r
1014                         goto Finish;\r
1015 \r
1016                 r = ext4_fs_free_inode(&child);\r
1017                 if (r != EOK)\r
1018                         goto Finish;\r
1019         }\r
1020 \r
1021 Finish:\r
1022         ext4_fs_put_inode_ref(&child);\r
1023         ext4_fs_put_inode_ref(&parent);\r
1024         EXT4_MP_UNLOCK(mp);\r
1025         return r;\r
1026 }\r
1027 \r
1028 int ext4_fill_raw_inode(const char *mount_point, uint32_t ino,\r
1029                         struct ext4_inode *inode)\r
1030 {\r
1031         int r;\r
1032         struct ext4_inode_ref inode_ref;\r
1033         struct ext4_mountpoint *mp = ext4_get_mount(mount_point);\r
1034 \r
1035         if (!mp)\r
1036                 return ENOENT;\r
1037 \r
1038         EXT4_MP_LOCK(mp);\r
1039 \r
1040         /*Load parent*/\r
1041         r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);\r
1042         if (r != EOK) {\r
1043                 EXT4_MP_UNLOCK(mp);\r
1044                 return r;\r
1045         }\r
1046 \r
1047         memcpy(inode, inode_ref.inode, sizeof(struct ext4_inode));\r
1048 \r
1049         ext4_fs_put_inode_ref(&inode_ref);\r
1050         EXT4_MP_UNLOCK(mp);\r
1051         return r;\r
1052 }\r
1053 \r
1054 int ext4_fopen(ext4_file *f, const char *path, const char *flags)\r
1055 {\r
1056         struct ext4_mountpoint *mp = ext4_get_mount(path);\r
1057         int r;\r
1058 \r
1059         if (!mp)\r
1060                 return ENOENT;\r
1061 \r
1062         EXT4_MP_LOCK(mp);\r
1063         ext4_block_cache_write_back(mp->fs.bdev, 1);\r
1064         r = ext4_generic_open(f, path, flags, true, 0, 0);\r
1065         ext4_block_cache_write_back(mp->fs.bdev, 0);\r
1066         EXT4_MP_UNLOCK(mp);\r
1067         return r;\r
1068 }\r
1069 \r
1070 int ext4_fopen2(ext4_file *f, const char *path, int flags, bool file_expect)\r
1071 {\r
1072         struct ext4_mountpoint *mp = ext4_get_mount(path);\r
1073         int r;\r
1074         int filetype;\r
1075 \r
1076         if (!mp)\r
1077                 return ENOENT;\r
1078 \r
1079         if (file_expect == true)\r
1080                 filetype = EXT4_DIRECTORY_FILETYPE_REG_FILE;\r
1081         else\r
1082                 filetype = EXT4_DIRECTORY_FILETYPE_DIR;\r
1083 \r
1084         EXT4_MP_LOCK(mp);\r
1085         ext4_block_cache_write_back(mp->fs.bdev, 1);\r
1086         r = ext4_generic_open2(f, path, flags, filetype, 0, 0);\r
1087         ext4_block_cache_write_back(mp->fs.bdev, 0);\r
1088         EXT4_MP_UNLOCK(mp);\r
1089         return r;\r
1090 }\r
1091 \r
1092 int ext4_fclose(ext4_file *f)\r
1093 {\r
1094         ext4_assert(f && f->mp);\r
1095 \r
1096         f->mp = 0;\r
1097         f->flags = 0;\r
1098         f->inode = 0;\r
1099         f->fpos = f->fsize = 0;\r
1100 \r
1101         return EOK;\r
1102 }\r
1103 \r
1104 int ext4_ftruncate(ext4_file *f, uint64_t size)\r
1105 {\r
1106         struct ext4_inode_ref ref;\r
1107         int r;\r
1108 \r
1109         ext4_assert(f && f->mp);\r
1110 \r
1111         if (f->flags & O_RDONLY)\r
1112                 return EPERM;\r
1113 \r
1114         EXT4_MP_LOCK(f->mp);\r
1115 \r
1116         r = ext4_fs_get_inode_ref(&f->mp->fs, f->inode, &ref);\r
1117         if (r != EOK) {\r
1118                 EXT4_MP_UNLOCK(f->mp);\r
1119                 return r;\r
1120         }\r
1121 \r
1122         /*Sync file size*/\r
1123         f->fsize = ext4_inode_get_size(&f->mp->fs.sb, ref.inode);\r
1124         if (f->fsize <= size) {\r
1125                 r = EOK;\r
1126                 goto Finish;\r
1127         }\r
1128 \r
1129         /*Start write back cache mode.*/\r
1130         r = ext4_block_cache_write_back(f->mp->fs.bdev, 1);\r
1131         if (r != EOK)\r
1132                 goto Finish;\r
1133 \r
1134         r = ext4_fs_truncate_inode(&ref, size);\r
1135         if (r != EOK)\r
1136                 goto Finish;\r
1137 \r
1138         f->fsize = size;\r
1139         if (f->fpos > size)\r
1140                 f->fpos = size;\r
1141 \r
1142         /*Stop write back cache mode*/\r
1143         ext4_block_cache_write_back(f->mp->fs.bdev, 0);\r
1144 \r
1145         if (r != EOK)\r
1146                 goto Finish;\r
1147 \r
1148 Finish:\r
1149         ext4_fs_put_inode_ref(&ref);\r
1150         EXT4_MP_UNLOCK(f->mp);\r
1151         return r;\r
1152 }\r
1153 \r
1154 int ext4_fread(ext4_file *f, void *buf, uint32_t size, uint32_t *rcnt)\r
1155 {\r
1156         uint32_t u;\r
1157         uint32_t fblock;\r
1158         uint32_t fblock_start;\r
1159         uint32_t fblock_cnt;\r
1160         uint32_t sblock;\r
1161         uint32_t sblock_end;\r
1162         uint32_t block_size;\r
1163         uint8_t *u8_buf = buf;\r
1164         int r;\r
1165         struct ext4_block b;\r
1166         struct ext4_inode_ref ref;\r
1167 \r
1168         ext4_assert(f && f->mp);\r
1169 \r
1170         if (f->flags & O_WRONLY)\r
1171                 return EPERM;\r
1172 \r
1173         if (!size)\r
1174                 return EOK;\r
1175 \r
1176         EXT4_MP_LOCK(f->mp);\r
1177 \r
1178         if (rcnt)\r
1179                 *rcnt = 0;\r
1180 \r
1181         r = ext4_fs_get_inode_ref(&f->mp->fs, f->inode, &ref);\r
1182         if (r != EOK) {\r
1183                 EXT4_MP_UNLOCK(f->mp);\r
1184                 return r;\r
1185         }\r
1186 \r
1187         /*Sync file size*/\r
1188         f->fsize = ext4_inode_get_size(&f->mp->fs.sb, ref.inode);\r
1189 \r
1190         block_size = ext4_sb_get_block_size(&f->mp->fs.sb);\r
1191         size = size > (f->fsize - f->fpos) ? (f->fsize - f->fpos) : size;\r
1192         sblock = (f->fpos) / block_size;\r
1193         sblock_end = (f->fpos + size) / block_size;\r
1194         u = (f->fpos) % block_size;\r
1195 \r
1196         if (u) {\r
1197 \r
1198                 uint32_t ll = size > (block_size - u) ? (block_size - u) : size;\r
1199 \r
1200                 r = ext4_fs_get_inode_data_block_index(&ref, sblock, &fblock);\r
1201                 if (r != EOK)\r
1202                         goto Finish;\r
1203 \r
1204                 r = ext4_block_get(f->mp->fs.bdev, &b, fblock);\r
1205                 if (r != EOK)\r
1206                         goto Finish;\r
1207 \r
1208                 memcpy(u8_buf, b.data + u, ll);\r
1209 \r
1210                 r = ext4_block_set(f->mp->fs.bdev, &b);\r
1211                 if (r != EOK)\r
1212                         goto Finish;\r
1213 \r
1214                 u8_buf += ll;\r
1215                 size -= ll;\r
1216                 f->fpos += ll;\r
1217 \r
1218                 if (rcnt)\r
1219                         *rcnt += ll;\r
1220 \r
1221                 sblock++;\r
1222         }\r
1223 \r
1224         fblock_start = 0;\r
1225         fblock_cnt = 0;\r
1226         while (size >= block_size) {\r
1227                 while (sblock < sblock_end) {\r
1228                         r = ext4_fs_get_inode_data_block_index(&ref, sblock,\r
1229                                                                &fblock);\r
1230                         if (r != EOK)\r
1231                                 goto Finish;\r
1232 \r
1233                         sblock++;\r
1234 \r
1235                         if (!fblock_start) {\r
1236                                 fblock_start = fblock;\r
1237                         }\r
1238 \r
1239                         if ((fblock_start + fblock_cnt) != fblock)\r
1240                                 break;\r
1241 \r
1242                         fblock_cnt++;\r
1243                 }\r
1244 \r
1245                 r = ext4_blocks_get_direct(f->mp->fs.bdev, u8_buf, fblock_start,\r
1246                                            fblock_cnt);\r
1247                 if (r != EOK)\r
1248                         goto Finish;\r
1249 \r
1250                 size -= block_size * fblock_cnt;\r
1251                 u8_buf += block_size * fblock_cnt;\r
1252                 f->fpos += block_size * fblock_cnt;\r
1253 \r
1254                 if (rcnt)\r
1255                         *rcnt += block_size * fblock_cnt;\r
1256 \r
1257                 fblock_start = fblock;\r
1258                 fblock_cnt = 1;\r
1259         }\r
1260 \r
1261         if (size) {\r
1262                 r = ext4_fs_get_inode_data_block_index(&ref, sblock, &fblock);\r
1263                 if (r != EOK)\r
1264                         goto Finish;\r
1265 \r
1266                 r = ext4_block_get(f->mp->fs.bdev, &b, fblock);\r
1267                 if (r != EOK)\r
1268                         goto Finish;\r
1269 \r
1270                 memcpy(u8_buf, b.data, size);\r
1271 \r
1272                 r = ext4_block_set(f->mp->fs.bdev, &b);\r
1273                 if (r != EOK)\r
1274                         goto Finish;\r
1275 \r
1276                 f->fpos += size;\r
1277 \r
1278                 if (rcnt)\r
1279                         *rcnt += size;\r
1280         }\r
1281 \r
1282 Finish:\r
1283         ext4_fs_put_inode_ref(&ref);\r
1284         EXT4_MP_UNLOCK(f->mp);\r
1285         return r;\r
1286 }\r
1287 \r
1288 int ext4_fwrite(ext4_file *f, const void *buf, uint32_t size, uint32_t *wcnt)\r
1289 {\r
1290         uint32_t u;\r
1291         uint32_t fblock;\r
1292 \r
1293         uint32_t sblock;\r
1294         uint32_t sblock_end;\r
1295         uint32_t file_blocks;\r
1296         uint32_t block_size;\r
1297         uint32_t fblock_start;\r
1298         uint32_t fblock_cnt;\r
1299 \r
1300         struct ext4_block b;\r
1301         struct ext4_inode_ref ref;\r
1302         const uint8_t *u8_buf = buf;\r
1303         int r;\r
1304 \r
1305         ext4_assert(f && f->mp);\r
1306 \r
1307         if (f->flags & O_RDONLY)\r
1308                 return EPERM;\r
1309 \r
1310         if (!size)\r
1311                 return EOK;\r
1312 \r
1313         EXT4_MP_LOCK(f->mp);\r
1314 \r
1315         if (wcnt)\r
1316                 *wcnt = 0;\r
1317 \r
1318         r = ext4_fs_get_inode_ref(&f->mp->fs, f->inode, &ref);\r
1319         if (r != EOK) {\r
1320                 EXT4_MP_UNLOCK(f->mp);\r
1321                 return r;\r
1322         }\r
1323 \r
1324         /*Sync file size*/\r
1325         f->fsize = ext4_inode_get_size(&f->mp->fs.sb, ref.inode);\r
1326 \r
1327         block_size = ext4_sb_get_block_size(&f->mp->fs.sb);\r
1328 \r
1329         sblock_end = (f->fpos + size) > f->fsize ? (f->fpos + size) : f->fsize;\r
1330         sblock_end /= block_size;\r
1331         file_blocks = (f->fsize / block_size);\r
1332 \r
1333         if (f->fsize % block_size)\r
1334                 file_blocks++;\r
1335 \r
1336         sblock = (f->fpos) / block_size;\r
1337 \r
1338         u = (f->fpos) % block_size;\r
1339 \r
1340         if (u) {\r
1341                 uint32_t ll = size > (block_size - u) ? (block_size - u) : size;\r
1342 \r
1343                 r = ext4_fs_get_inode_data_block_index(&ref, sblock, &fblock);\r
1344                 if (r != EOK)\r
1345                         goto Finish;\r
1346 \r
1347                 r = ext4_block_get(f->mp->fs.bdev, &b, fblock);\r
1348                 if (r != EOK)\r
1349                         goto Finish;\r
1350 \r
1351                 memcpy(b.data + u, u8_buf, ll);\r
1352                 b.dirty = true;\r
1353 \r
1354                 r = ext4_block_set(f->mp->fs.bdev, &b);\r
1355                 if (r != EOK)\r
1356                         goto Finish;\r
1357 \r
1358                 u8_buf += ll;\r
1359                 size -= ll;\r
1360                 f->fpos += ll;\r
1361 \r
1362                 if (wcnt)\r
1363                         *wcnt += ll;\r
1364 \r
1365                 sblock++;\r
1366         }\r
1367 \r
1368         /*Start write back cache mode.*/\r
1369         r = ext4_block_cache_write_back(f->mp->fs.bdev, 1);\r
1370         if (r != EOK)\r
1371                 goto Finish;\r
1372 \r
1373         fblock_start = 0;\r
1374         fblock_cnt = 0;\r
1375         while (size >= block_size) {\r
1376 \r
1377                 while (sblock < sblock_end) {\r
1378                         if (sblock < file_blocks) {\r
1379                                 r = ext4_fs_get_inode_data_block_index(\r
1380                                     &ref, sblock, &fblock);\r
1381                                 if (r != EOK)\r
1382                                         break;\r
1383                         } else {\r
1384                                 r = ext4_fs_append_inode_block(&ref, &fblock,\r
1385                                                                &sblock);\r
1386                                 if (r != EOK)\r
1387                                         break;\r
1388                         }\r
1389 \r
1390                         sblock++;\r
1391 \r
1392                         if (!fblock_start) {\r
1393                                 fblock_start = fblock;\r
1394                         }\r
1395 \r
1396                         if ((fblock_start + fblock_cnt) != fblock)\r
1397                                 break;\r
1398 \r
1399                         fblock_cnt++;\r
1400                 }\r
1401 \r
1402                 r = ext4_blocks_set_direct(f->mp->fs.bdev, u8_buf, fblock_start,\r
1403                                            fblock_cnt);\r
1404                 if (r != EOK)\r
1405                         break;\r
1406 \r
1407                 size -= block_size * fblock_cnt;\r
1408                 u8_buf += block_size * fblock_cnt;\r
1409                 f->fpos += block_size * fblock_cnt;\r
1410 \r
1411                 if (wcnt)\r
1412                         *wcnt += block_size * fblock_cnt;\r
1413 \r
1414                 fblock_start = fblock;\r
1415                 fblock_cnt = 1;\r
1416         }\r
1417 \r
1418         /*Stop write back cache mode*/\r
1419         ext4_block_cache_write_back(f->mp->fs.bdev, 0);\r
1420 \r
1421         if (r != EOK)\r
1422                 goto Finish;\r
1423 \r
1424         if (size) {\r
1425                 if (sblock < file_blocks) {\r
1426                         r = ext4_fs_get_inode_data_block_index(&ref, sblock,\r
1427                                                                &fblock);\r
1428                         if (r != EOK)\r
1429                                 goto Finish;\r
1430                 } else {\r
1431                         r = ext4_fs_append_inode_block(&ref, &fblock, &sblock);\r
1432                         if (r != EOK)\r
1433                                 goto Finish;\r
1434                 }\r
1435 \r
1436                 r = ext4_block_get(f->mp->fs.bdev, &b, fblock);\r
1437                 if (r != EOK)\r
1438                         goto Finish;\r
1439 \r
1440                 memcpy(b.data, u8_buf, size);\r
1441                 b.dirty = true;\r
1442 \r
1443                 r = ext4_block_set(f->mp->fs.bdev, &b);\r
1444                 if (r != EOK)\r
1445                         goto Finish;\r
1446 \r
1447                 f->fpos += size;\r
1448 \r
1449                 if (wcnt)\r
1450                         *wcnt += size;\r
1451         }\r
1452 \r
1453         if (f->fpos > f->fsize) {\r
1454                 f->fsize = f->fpos;\r
1455                 ext4_inode_set_size(ref.inode, f->fsize);\r
1456                 ref.dirty = true;\r
1457         }\r
1458 \r
1459 Finish:\r
1460         ext4_fs_put_inode_ref(&ref);\r
1461         EXT4_MP_UNLOCK(f->mp);\r
1462         return r;\r
1463 }\r
1464 \r
1465 int ext4_fseek(ext4_file *f, uint64_t offset, uint32_t origin)\r
1466 {\r
1467         switch (origin) {\r
1468         case SEEK_SET:\r
1469                 if (offset > f->fsize)\r
1470                         return EINVAL;\r
1471 \r
1472                 f->fpos = offset;\r
1473                 return EOK;\r
1474         case SEEK_CUR:\r
1475                 if ((offset + f->fpos) > f->fsize)\r
1476                         return EINVAL;\r
1477 \r
1478                 f->fpos += offset;\r
1479                 return EOK;\r
1480         case SEEK_END:\r
1481                 if (offset > f->fsize)\r
1482                         return EINVAL;\r
1483 \r
1484                 f->fpos = f->fsize - offset;\r
1485                 return EOK;\r
1486         }\r
1487         return EINVAL;\r
1488 }\r
1489 \r
1490 uint64_t ext4_ftell(ext4_file *f) { return f->fpos; }\r
1491 \r
1492 uint64_t ext4_fsize(ext4_file *f) { return f->fsize; }\r
1493 \r
1494 int ext4_fchmod(ext4_file *f, uint32_t mode)\r
1495 {\r
1496         int r;\r
1497         uint32_t ino;\r
1498         struct ext4_sblock *sb;\r
1499         struct ext4_inode_ref inode_ref;\r
1500         struct ext4_mountpoint *mp = f->mp;\r
1501 \r
1502         if (!mp)\r
1503                 return ENOENT;\r
1504 \r
1505         EXT4_MP_LOCK(mp);\r
1506 \r
1507         ino = f->inode;\r
1508         r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);\r
1509         if (r != EOK) {\r
1510                 EXT4_MP_UNLOCK(mp);\r
1511                 return r;\r
1512         }\r
1513 \r
1514         sb = &f->mp->fs.sb;\r
1515         ext4_inode_set_mode(sb, inode_ref.inode, mode);\r
1516         inode_ref.dirty = true;\r
1517 \r
1518         ext4_fs_put_inode_ref(&inode_ref);\r
1519         EXT4_MP_UNLOCK(mp);\r
1520         return r;\r
1521 }\r
1522 \r
1523 int ext4_fchown(ext4_file *f, uint32_t uid, uint32_t gid)\r
1524 {\r
1525         int r;\r
1526         uint32_t ino;\r
1527         struct ext4_inode_ref inode_ref;\r
1528         struct ext4_mountpoint *mp = f->mp;\r
1529 \r
1530         if (!mp)\r
1531                 return ENOENT;\r
1532 \r
1533         EXT4_MP_LOCK(mp);\r
1534 \r
1535         ino = f->inode;\r
1536         r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);\r
1537         if (r != EOK) {\r
1538                 EXT4_MP_UNLOCK(mp);\r
1539                 return r;\r
1540         }\r
1541 \r
1542         ext4_inode_set_uid(inode_ref.inode, uid);\r
1543         ext4_inode_set_gid(inode_ref.inode, gid);\r
1544         inode_ref.dirty = true;\r
1545 \r
1546         ext4_fs_put_inode_ref(&inode_ref);\r
1547         EXT4_MP_UNLOCK(mp);\r
1548         return r;\r
1549 }\r
1550 \r
1551 int ext4_file_set_atime(ext4_file *f, uint32_t atime)\r
1552 {\r
1553         int r;\r
1554         uint32_t ino;\r
1555         struct ext4_inode_ref inode_ref;\r
1556         struct ext4_mountpoint *mp = f->mp;\r
1557 \r
1558         if (!mp)\r
1559                 return ENOENT;\r
1560 \r
1561         EXT4_MP_LOCK(mp);\r
1562 \r
1563         ino = f->inode;\r
1564         r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);\r
1565         if (r != EOK) {\r
1566                 EXT4_MP_UNLOCK(mp);\r
1567                 return r;\r
1568         }\r
1569 \r
1570         ext4_inode_set_access_time(inode_ref.inode, atime);\r
1571         inode_ref.dirty = true;\r
1572 \r
1573         ext4_fs_put_inode_ref(&inode_ref);\r
1574         EXT4_MP_UNLOCK(mp);\r
1575         return r;\r
1576 }\r
1577 \r
1578 int ext4_file_set_mtime(ext4_file *f, uint32_t mtime)\r
1579 {\r
1580         int r;\r
1581         uint32_t ino;\r
1582         struct ext4_inode_ref inode_ref;\r
1583         struct ext4_mountpoint *mp = f->mp;\r
1584 \r
1585         if (!mp)\r
1586                 return ENOENT;\r
1587 \r
1588         EXT4_MP_LOCK(mp);\r
1589 \r
1590         ino = f->inode;\r
1591         r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);\r
1592         if (r != EOK) {\r
1593                 EXT4_MP_UNLOCK(mp);\r
1594                 return r;\r
1595         }\r
1596 \r
1597         ext4_inode_set_modification_time(inode_ref.inode, mtime);\r
1598         inode_ref.dirty = true;\r
1599 \r
1600         ext4_fs_put_inode_ref(&inode_ref);\r
1601         EXT4_MP_UNLOCK(mp);\r
1602         return r;\r
1603 }\r
1604 \r
1605 int ext4_file_set_ctime(ext4_file *f, uint32_t ctime)\r
1606 {\r
1607         int r;\r
1608         uint32_t ino;\r
1609         struct ext4_inode_ref inode_ref;\r
1610         struct ext4_mountpoint *mp = f->mp;\r
1611 \r
1612         if (!mp)\r
1613                 return ENOENT;\r
1614 \r
1615         EXT4_MP_LOCK(mp);\r
1616 \r
1617         ino = f->inode;\r
1618         r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);\r
1619         if (r != EOK) {\r
1620                 EXT4_MP_UNLOCK(mp);\r
1621                 return r;\r
1622         }\r
1623 \r
1624         ext4_inode_set_change_inode_time(inode_ref.inode, ctime);\r
1625         inode_ref.dirty = true;\r
1626 \r
1627         ext4_fs_put_inode_ref(&inode_ref);\r
1628         EXT4_MP_UNLOCK(mp);\r
1629         return r;\r
1630 }\r
1631 \r
1632 /*********************************DIRECTORY OPERATION************************/\r
1633 \r
1634 int ext4_dir_rm(const char *path)\r
1635 {\r
1636         int r;\r
1637         int len;\r
1638         ext4_file f;\r
1639 \r
1640         struct ext4_mountpoint *mp = ext4_get_mount(path);\r
1641         struct ext4_inode_ref current;\r
1642         struct ext4_inode_ref child;\r
1643         struct ext4_directory_iterator it;\r
1644 \r
1645         uint32_t name_off;\r
1646         uint32_t inode_up;\r
1647         uint32_t inode_current;\r
1648         uint32_t depth = 1;\r
1649 \r
1650         bool has_children;\r
1651         bool is_goal;\r
1652         bool dir_end;\r
1653 \r
1654         if (!mp)\r
1655                 return ENOENT;\r
1656 \r
1657         EXT4_MP_LOCK(mp);\r
1658 \r
1659         /*Check if exist.*/\r
1660         r = ext4_generic_open(&f, path, "r", false, &inode_up, &name_off);\r
1661         if (r != EOK) {\r
1662                 EXT4_MP_UNLOCK(mp);\r
1663                 return r;\r
1664         }\r
1665 \r
1666         path += name_off;\r
1667         len = ext4_path_check(path, &is_goal);\r
1668 \r
1669         inode_current = f.inode;\r
1670         dir_end = false;\r
1671 \r
1672         ext4_block_cache_write_back(mp->fs.bdev, 1);\r
1673 \r
1674         do {\r
1675                 /*Load directory node.*/\r
1676                 r = ext4_fs_get_inode_ref(&f.mp->fs, inode_current, &current);\r
1677                 if (r != EOK) {\r
1678                         break;\r
1679                 }\r
1680 \r
1681                 /*Initialize iterator.*/\r
1682                 r = ext4_dir_iterator_init(&it, &current, 0);\r
1683                 if (r != EOK) {\r
1684                         ext4_fs_put_inode_ref(&current);\r
1685                         break;\r
1686                 }\r
1687 \r
1688                 while (r == EOK) {\r
1689 \r
1690                         if (!it.current) {\r
1691                                 dir_end = true;\r
1692                                 break;\r
1693                         }\r
1694 \r
1695                         /*Get up directory inode when ".." entry*/\r
1696                         if ((it.current->name_length == 2) &&\r
1697                             ext4_is_dots(it.current->name,\r
1698                                          it.current->name_length)) {\r
1699                                 inode_up = it.current->inode;\r
1700                         }\r
1701 \r
1702                         /*If directory or file entry,  but not "." ".." entry*/\r
1703                         if (!ext4_is_dots(it.current->name,\r
1704                                           it.current->name_length)) {\r
1705 \r
1706                                 /*Get child inode reference do unlink\r
1707                                  * directory/file.*/\r
1708                                 r = ext4_fs_get_inode_ref(\r
1709                                     &f.mp->fs, it.current->inode, &child);\r
1710                                 if (r != EOK)\r
1711                                         break;\r
1712 \r
1713                                 /*If directory with no leaf children*/\r
1714                                 r = ext4_has_children(&has_children, &child);\r
1715                                 if (r != EOK) {\r
1716                                         ext4_fs_put_inode_ref(&child);\r
1717                                         break;\r
1718                                 }\r
1719 \r
1720                                 if (has_children) {\r
1721                                         /*Has directory children. Go into this\r
1722                                          * directory.*/\r
1723                                         inode_up = inode_current;\r
1724                                         inode_current = it.current->inode;\r
1725                                         depth++;\r
1726                                         ext4_fs_put_inode_ref(&child);\r
1727                                         break;\r
1728                                 }\r
1729 \r
1730                                 /*No children in child directory or file. Just\r
1731                                  * unlink.*/\r
1732                                 r = ext4_unlink(f.mp, &current, &child,\r
1733                                                 (char *)it.current->name,\r
1734                                                 it.current->name_length);\r
1735                                 if (r != EOK) {\r
1736                                         ext4_fs_put_inode_ref(&child);\r
1737                                         break;\r
1738                                 }\r
1739 \r
1740                                 ext4_inode_set_deletion_time(child.inode,\r
1741                                                              0xFFFFFFFF);\r
1742                                 ext4_inode_set_links_count(child.inode, 0);\r
1743                                 child.dirty = true;\r
1744                                 /*Turncate*/\r
1745                                 r = ext4_fs_truncate_inode(&child, 0);\r
1746                                 if (r != EOK) {\r
1747                                         ext4_fs_put_inode_ref(&child);\r
1748                                         break;\r
1749                                 }\r
1750 \r
1751                                 r = ext4_fs_free_inode(&child);\r
1752                                 if (r != EOK) {\r
1753                                         ext4_fs_put_inode_ref(&child);\r
1754                                         break;\r
1755                                 }\r
1756 \r
1757                                 r = ext4_fs_put_inode_ref(&child);\r
1758                                 if (r != EOK)\r
1759                                         break;\r
1760                         }\r
1761 \r
1762                         r = ext4_dir_iterator_next(&it);\r
1763                 }\r
1764 \r
1765                 if (dir_end) {\r
1766                         /*Directory iterator reached last entry*/\r
1767                         ext4_has_children(&has_children, &current);\r
1768                         if (!has_children) {\r
1769                                 inode_current = inode_up;\r
1770                                 if (depth)\r
1771                                         depth--;\r
1772                         }\r
1773                         /*Last unlink*/\r
1774                         if (!depth) {\r
1775                                 /*Load parent.*/\r
1776                                 struct ext4_inode_ref parent;\r
1777                                 r = ext4_fs_get_inode_ref(&f.mp->fs, inode_up,\r
1778                                                           &parent);\r
1779                                 if (r != EOK)\r
1780                                         goto End;\r
1781 \r
1782                                 /* In this place all directories should be\r
1783                                  * unlinked.\r
1784                                  * Last unlink from root of current directory*/\r
1785                                 r = ext4_unlink(f.mp, &parent, &current,\r
1786                                                 (char *)path, len);\r
1787                                 if (r != EOK) {\r
1788                                         ext4_fs_put_inode_ref(&parent);\r
1789                                         goto End;\r
1790                                 }\r
1791 \r
1792                                 if (ext4_inode_get_links_count(current.inode) ==\r
1793                                     2) {\r
1794                                         ext4_inode_set_deletion_time(\r
1795                                             current.inode, 0xFFFFFFFF);\r
1796                                         ext4_inode_set_links_count(\r
1797                                             current.inode, 0);\r
1798                                         current.dirty = true;\r
1799                                         /*Turncate*/\r
1800                                         r = ext4_fs_truncate_inode(&current, 0);\r
1801                                         if (r != EOK) {\r
1802                                                 ext4_fs_put_inode_ref(&parent);\r
1803                                                 goto End;\r
1804                                         }\r
1805 \r
1806                                         r = ext4_fs_free_inode(&current);\r
1807                                         if (r != EOK) {\r
1808                                                 ext4_fs_put_inode_ref(&parent);\r
1809                                                 goto End;\r
1810                                         }\r
1811                                 }\r
1812 \r
1813                                 r = ext4_fs_put_inode_ref(&parent);\r
1814                                 if (r != EOK)\r
1815                                         goto End;\r
1816                         }\r
1817                 }\r
1818 \r
1819         End:\r
1820                 ext4_dir_iterator_fini(&it);\r
1821                 ext4_fs_put_inode_ref(&current);\r
1822                 dir_end = false;\r
1823 \r
1824                 /*When something goes wrong. End loop.*/\r
1825                 if (r != EOK)\r
1826                         break;\r
1827 \r
1828         } while (depth);\r
1829 \r
1830         ext4_block_cache_write_back(mp->fs.bdev, 0);\r
1831         EXT4_MP_UNLOCK(mp);\r
1832         return r;\r
1833 }\r
1834 \r
1835 int ext4_dir_mk(const char *path)\r
1836 {\r
1837         int r;\r
1838         ext4_file f;\r
1839 \r
1840         struct ext4_mountpoint *mp = ext4_get_mount(path);\r
1841 \r
1842         if (!mp)\r
1843                 return ENOENT;\r
1844 \r
1845         EXT4_MP_LOCK(mp);\r
1846 \r
1847         /*Check if exist.*/\r
1848         r = ext4_generic_open(&f, path, "r", false, 0, 0);\r
1849         if (r == EOK) {\r
1850                 /*Directory already created*/\r
1851                 EXT4_MP_UNLOCK(mp);\r
1852                 return r;\r
1853         }\r
1854 \r
1855         /*Create new dir*/\r
1856         r = ext4_generic_open(&f, path, "w", false, 0, 0);\r
1857         if (r != EOK) {\r
1858                 EXT4_MP_UNLOCK(mp);\r
1859                 return r;\r
1860         }\r
1861 \r
1862         EXT4_MP_UNLOCK(mp);\r
1863         return r;\r
1864 }\r
1865 \r
1866 int ext4_dir_open(ext4_dir *d, const char *path)\r
1867 {\r
1868         struct ext4_mountpoint *mp = ext4_get_mount(path);\r
1869         int r;\r
1870 \r
1871         if (!mp)\r
1872                 return ENOENT;\r
1873 \r
1874         EXT4_MP_LOCK(mp);\r
1875         r = ext4_generic_open(&d->f, path, "r", false, 0, 0);\r
1876         d->next_off = 0;\r
1877         EXT4_MP_UNLOCK(mp);\r
1878         return r;\r
1879 }\r
1880 \r
1881 int ext4_dir_close(ext4_dir *d) { return ext4_fclose(&d->f); }\r
1882 \r
1883 const ext4_direntry *ext4_dir_entry_next(ext4_dir *d)\r
1884 {\r
1885 #define EXT4_DIR_ENTRY_OFFSET_TERM (uint64_t)(-1)\r
1886 \r
1887         int r;\r
1888         ext4_direntry *de = 0;\r
1889         struct ext4_inode_ref dir;\r
1890         struct ext4_directory_iterator it;\r
1891 \r
1892         EXT4_MP_LOCK(d->f.mp);\r
1893 \r
1894         if (d->next_off == EXT4_DIR_ENTRY_OFFSET_TERM)\r
1895                 return 0;\r
1896 \r
1897         r = ext4_fs_get_inode_ref(&d->f.mp->fs, d->f.inode, &dir);\r
1898         if (r != EOK) {\r
1899                 goto Finish;\r
1900         }\r
1901 \r
1902         r = ext4_dir_iterator_init(&it, &dir, d->next_off);\r
1903         if (r != EOK) {\r
1904                 ext4_fs_put_inode_ref(&dir);\r
1905                 goto Finish;\r
1906         }\r
1907 \r
1908         memcpy(&d->de, it.current, sizeof(ext4_direntry));\r
1909         de = &d->de;\r
1910 \r
1911         ext4_dir_iterator_next(&it);\r
1912 \r
1913         d->next_off =\r
1914             it.current ? it.current_offset : EXT4_DIR_ENTRY_OFFSET_TERM;\r
1915 \r
1916         ext4_dir_iterator_fini(&it);\r
1917         ext4_fs_put_inode_ref(&dir);\r
1918 \r
1919 Finish:\r
1920         EXT4_MP_UNLOCK(d->f.mp);\r
1921         return de;\r
1922 }\r
1923 \r
1924 /**\r
1925  * @}\r
1926  */\r