First version
[3rdparty/ote_partner/tlk.git] / lib / fs / ext2 / ext2.c
1 /*
2  * Copyright (c) 2007 Travis Geiselbrecht
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining
5  * a copy of this software and associated documentation files
6  * (the "Software"), to deal in the Software without restriction,
7  * including without limitation the rights to use, copy, modify, merge,
8  * publish, distribute, sublicense, and/or sell copies of the Software,
9  * and to permit persons to whom the Software is furnished to do so,
10  * subject to the following conditions:
11  * 
12  * The above copyright notice and this permission notice shall be
13  * included in all copies or substantial portions of the Software.
14  * 
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  */
23
24 #include <string.h>
25 #include <stdlib.h>
26 #include <debug.h>
27 #include <lib/fs/ext2.h>
28 #include "ext2_priv.h"
29
30 #define LOCAL_TRACE 0
31
32 static void endian_swap_superblock(struct ext2_super_block *sb)
33 {
34         LE32SWAP(sb->s_inodes_count);
35         LE32SWAP(sb->s_blocks_count);
36         LE32SWAP(sb->s_r_blocks_count);
37         LE32SWAP(sb->s_free_blocks_count);
38         LE32SWAP(sb->s_free_inodes_count);
39         LE32SWAP(sb->s_first_data_block);
40         LE32SWAP(sb->s_log_block_size);
41         LE32SWAP(sb->s_log_frag_size);
42         LE32SWAP(sb->s_blocks_per_group);
43         LE32SWAP(sb->s_frags_per_group);
44         LE32SWAP(sb->s_inodes_per_group);
45         LE32SWAP(sb->s_mtime);
46         LE32SWAP(sb->s_wtime);
47         LE16SWAP(sb->s_mnt_count);
48         LE16SWAP(sb->s_max_mnt_count);
49         LE16SWAP(sb->s_magic);
50         LE16SWAP(sb->s_state);
51         LE16SWAP(sb->s_errors);
52         LE16SWAP(sb->s_minor_rev_level);
53         LE32SWAP(sb->s_lastcheck);
54         LE32SWAP(sb->s_checkinterval);
55         LE32SWAP(sb->s_creator_os);
56         LE32SWAP(sb->s_rev_level);
57         LE16SWAP(sb->s_def_resuid);
58         LE16SWAP(sb->s_def_resgid);
59         LE32SWAP(sb->s_first_ino);
60         LE16SWAP(sb->s_inode_size);
61         LE16SWAP(sb->s_block_group_nr);
62         LE32SWAP(sb->s_feature_compat);
63         LE32SWAP(sb->s_feature_incompat);
64         LE32SWAP(sb->s_feature_ro_compat);
65         LE32SWAP(sb->s_algorithm_usage_bitmap);
66
67         /* ext3 journal stuff */
68         LE32SWAP(sb->s_journal_inum);
69         LE32SWAP(sb->s_journal_dev);
70         LE32SWAP(sb->s_last_orphan);
71         LE32SWAP(sb->s_default_mount_opts);
72         LE32SWAP(sb->s_first_meta_bg);
73 }
74
75 static void endian_swap_inode(struct ext2_inode *inode)
76 {
77         LE16SWAP(inode->i_mode);
78         LE16SWAP(inode->i_uid_low);
79         LE32SWAP(inode->i_size);
80         LE32SWAP(inode->i_atime);
81         LE32SWAP(inode->i_ctime);
82         LE32SWAP(inode->i_mtime);
83         LE32SWAP(inode->i_dtime);
84         LE16SWAP(inode->i_gid_low);
85         LE16SWAP(inode->i_links_count);
86         LE32SWAP(inode->i_blocks);
87         LE32SWAP(inode->i_flags);
88
89         // leave block pointers/symlink data alone
90
91         LE32SWAP(inode->i_generation);
92         LE32SWAP(inode->i_file_acl);
93         LE32SWAP(inode->i_dir_acl);
94         LE32SWAP(inode->i_faddr);
95
96         LE16SWAP(inode->i_uid_high);
97         LE16SWAP(inode->i_gid_high);
98 }
99
100 static void endian_swap_group_desc(struct ext2_group_desc *gd)
101 {
102         LE32SWAP(gd->bg_block_bitmap);
103         LE32SWAP(gd->bg_inode_bitmap);
104         LE32SWAP(gd->bg_inode_table);
105         LE16SWAP(gd->bg_free_blocks_count);
106         LE16SWAP(gd->bg_free_inodes_count);
107         LE16SWAP(gd->bg_used_dirs_count);
108 }
109
110 int ext2_mount(bdev_t *dev, fscookie *cookie)
111 {
112         int err;
113
114         LTRACEF("dev %p\n", dev);
115
116         ext2_t *ext2 = malloc(sizeof(ext2_t));
117         ext2->dev = dev;
118
119         err = bio_read(dev, &ext2->sb, 1024, sizeof(struct ext2_super_block));
120         if (err < 0)
121                 goto err;
122
123         endian_swap_superblock(&ext2->sb);
124
125         /* see if the superblock is good */
126         if (ext2->sb.s_magic != EXT2_SUPER_MAGIC) {
127                 err = -1;
128                 return err;
129         }
130
131         /* calculate group count, rounded up */
132         ext2->s_group_count = (ext2->sb.s_blocks_count + ext2->sb.s_blocks_per_group - 1) / ext2->sb.s_blocks_per_group;
133
134         /* print some info */
135         LTRACEF("rev level %d\n", ext2->sb.s_rev_level);
136         LTRACEF("compat features 0x%x\n", ext2->sb.s_feature_compat);
137         LTRACEF("incompat features 0x%x\n", ext2->sb.s_feature_incompat);
138         LTRACEF("ro compat features 0x%x\n", ext2->sb.s_feature_ro_compat);
139         LTRACEF("block size %d\n", EXT2_BLOCK_SIZE(ext2->sb));
140         LTRACEF("inode size %d\n", EXT2_INODE_SIZE(ext2->sb));
141         LTRACEF("block count %d\n", ext2->sb.s_blocks_count);
142         LTRACEF("blocks per group %d\n", ext2->sb.s_blocks_per_group);
143         LTRACEF("group count %d\n", ext2->s_group_count);
144         LTRACEF("inodes per group %d\n", ext2->sb.s_inodes_per_group);
145
146         /* we only support dynamic revs */
147         if (ext2->sb.s_rev_level > EXT2_DYNAMIC_REV) {
148                 err = -2;
149                 return err;
150         }
151
152         /* make sure it doesn't have any ro features we don't support */
153         if (ext2->sb.s_feature_ro_compat & ~(EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER|EXT2_FEATURE_RO_COMPAT_LARGE_FILE)) {
154                 err = -3;
155                 return err;
156         }
157
158         /* read in all the group descriptors */
159         ext2->gd = malloc(sizeof(struct ext2_group_desc) * ext2->s_group_count);
160         err = bio_read(ext2->dev, (void *)ext2->gd, 
161                         (EXT2_BLOCK_SIZE(ext2->sb) == 4096) ? 4096 : 2048, 
162                         sizeof(struct ext2_group_desc) * ext2->s_group_count);
163         if (err < 0) {
164                 err = -4;
165                 return err;
166         }
167
168         int i;
169         for (i=0; i < ext2->s_group_count; i++) {
170                 endian_swap_group_desc(&ext2->gd[i]);
171                 LTRACEF("group %d:\n", i);
172                 LTRACEF("\tblock bitmap %d\n", ext2->gd[i].bg_block_bitmap);
173                 LTRACEF("\tinode bitmap %d\n", ext2->gd[i].bg_inode_bitmap);
174                 LTRACEF("\tinode table %d\n", ext2->gd[i].bg_inode_table);
175                 LTRACEF("\tfree blocks %d\n", ext2->gd[i].bg_free_blocks_count);
176                 LTRACEF("\tfree inodes %d\n", ext2->gd[i].bg_free_inodes_count);
177                 LTRACEF("\tused dirs %d\n", ext2->gd[i].bg_used_dirs_count);
178         }
179
180         /* initialize the block cache */
181         ext2->cache = bcache_create(ext2->dev, EXT2_BLOCK_SIZE(ext2->sb), 4);
182
183         /* load the first inode */
184         err = ext2_load_inode(ext2, EXT2_ROOT_INO, &ext2->root_inode);
185         if (err < 0)
186                 goto err;
187
188 //      TRACE("successfully mounted volume\n");
189
190         *cookie = ext2;
191
192         return 0;
193
194 err:
195         LTRACEF("exiting with err code %d\n", err);
196
197         free(ext2);
198         return err;
199 }
200
201 int ext2_unmount(fscookie cookie)
202 {
203         // free it up
204         ext2_t *ext2 = (ext2_t *)cookie;
205
206         bcache_destroy(ext2->cache);
207         free(ext2->gd);
208         free(ext2);
209
210         return 0;
211 }
212
213 static void get_inode_addr(ext2_t *ext2, inodenum_t num, blocknum_t *block, size_t *block_offset)
214 {
215         num--;
216
217         uint32_t group = num / ext2->sb.s_inodes_per_group;
218
219         // calculate the start of the inode table for the group it's in
220         *block = ext2->gd[group].bg_inode_table;
221
222         // add the offset of the inode within the group
223         size_t offset = (num % EXT2_INODES_PER_GROUP(ext2->sb)) * EXT2_INODE_SIZE(ext2->sb);
224         *block_offset = offset % EXT2_BLOCK_SIZE(ext2->sb);
225         *block += offset / EXT2_BLOCK_SIZE(ext2->sb);
226 }
227
228 int ext2_load_inode(ext2_t *ext2, inodenum_t num, struct ext2_inode *inode)
229 {
230         int err;
231         
232         LTRACEF("num %d, inode %p\n", num, inode);
233
234         blocknum_t bnum;
235         size_t block_offset;
236         get_inode_addr(ext2, num, &bnum, &block_offset);
237
238         LTRACEF("bnum %u, offset %zd\n", bnum, block_offset);
239
240         /* get a pointer to the cache block */
241         void *cache_ptr;
242         err = bcache_get_block(ext2->cache, &cache_ptr, bnum);
243         if (err < 0)
244                 return err;
245
246         /* copy the inode out */
247         memcpy(inode, (uint8_t *)cache_ptr + block_offset, sizeof(struct ext2_inode));
248
249         /* put the cache block */
250         bcache_put_block(ext2->cache, bnum);
251
252         /* endian swap it */
253         endian_swap_inode(inode);
254
255         LTRACEF("read inode: mode 0x%x, size %d\n", inode->i_mode, inode->i_size);
256
257         return 0;
258 }
259