Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / openbios / fs / ext2 / ext2_utils.c
1 /*
2  *
3  * (c) 2008-2009 Laurent Vivier <Laurent@lvivier.info>
4  *
5  * This file has been copied from EMILE, http://emile.sf.net
6  *
7  */
8
9 #include "libext2.h"
10 #include "ext2_utils.h"
11 #include "libopenbios/bindings.h"
12 #include "libc/diskio.h"
13 #include "libc/byteorder.h"
14
15 int ext2_probe(int fd, long long offset)
16 {
17         struct ext2_super_block *super;
18
19         super = (struct ext2_super_block*)malloc(sizeof(struct ext2_super_block));
20         seek_io(fd, 2 * 512 + offset);
21         read_io(fd, super, sizeof (*super));
22
23         if (__le16_to_cpu(super->s_magic) != EXT2_SUPER_MAGIC) {
24                 free(super);
25                 return 0;
26         }
27
28         free(super);
29         return -1;
30 }
31
32 void ext2_get_super(int fd, struct ext2_super_block *super)
33 {
34         seek_io(fd, 2 * 512);
35         read_io(fd, super, sizeof (*super));
36
37         super->s_inodes_count = __le32_to_cpu(super->s_inodes_count);
38         super->s_blocks_count = __le32_to_cpu(super->s_blocks_count);
39         super->s_r_blocks_count = __le32_to_cpu(super->s_r_blocks_count);
40         super->s_free_blocks_count = __le32_to_cpu(super->s_free_blocks_count);
41         super->s_free_inodes_count = __le32_to_cpu(super->s_free_inodes_count);
42         super->s_first_data_block = __le32_to_cpu(super->s_first_data_block);
43         super->s_log_block_size = __le32_to_cpu(super->s_log_block_size);
44         super->s_log_frag_size = __le32_to_cpu(super->s_log_frag_size);
45         super->s_blocks_per_group = __le32_to_cpu(super->s_blocks_per_group);
46         super->s_frags_per_group = __le32_to_cpu(super->s_frags_per_group);
47         super->s_inodes_per_group = __le32_to_cpu(super->s_inodes_per_group);
48         super->s_mtime = __le32_to_cpu(super->s_mtime);
49         super->s_wtime = __le32_to_cpu(super->s_wtime);
50         super->s_mnt_count = __le16_to_cpu(super->s_mnt_count);
51         super->s_max_mnt_count = __le16_to_cpu(super->s_max_mnt_count);
52         super->s_magic = __le16_to_cpu(super->s_magic);
53         super->s_state = __le16_to_cpu(super->s_state);
54         super->s_errors = __le16_to_cpu(super->s_errors);
55         super->s_minor_rev_level = __le16_to_cpu(super->s_minor_rev_level);
56         super->s_lastcheck = __le32_to_cpu(super->s_lastcheck);
57         super->s_checkinterval = __le32_to_cpu(super->s_checkinterval);
58         super->s_creator_os = __le32_to_cpu(super->s_creator_os);
59         super->s_rev_level = __le32_to_cpu(super->s_rev_level);
60         super->s_def_resuid = __le16_to_cpu(super->s_def_resuid);
61         super->s_def_resgid = __le16_to_cpu(super->s_def_resgid);
62         super->s_first_ino = __le32_to_cpu(super->s_first_ino);
63         super->s_inode_size = __le16_to_cpu(super->s_inode_size);
64         super->s_block_group_nr = __le16_to_cpu(super->s_block_group_nr);
65         super->s_feature_compat = __le32_to_cpu(super->s_feature_compat);
66         super->s_feature_incompat = __le32_to_cpu(super->s_feature_incompat);
67         super->s_feature_ro_compat = __le32_to_cpu(super->s_feature_ro_compat);
68         super->s_algorithm_usage_bitmap =
69                                 __le32_to_cpu(super->s_algorithm_usage_bitmap);
70         super->s_journal_inum = __le32_to_cpu(super->s_journal_inum);
71         super->s_journal_dev = __le32_to_cpu(super->s_journal_dev);
72         super->s_last_orphan = __le32_to_cpu(super->s_last_orphan);
73         super->s_hash_seed[0] = __le32_to_cpu(super->s_hash_seed[0]);
74         super->s_hash_seed[1] = __le32_to_cpu(super->s_hash_seed[1]);
75         super->s_hash_seed[2] = __le32_to_cpu(super->s_hash_seed[2]);
76         super->s_hash_seed[3] = __le32_to_cpu(super->s_hash_seed[3]);
77         super->s_default_mount_opts =
78                                 __le32_to_cpu(super->s_default_mount_opts);
79         super->s_first_meta_bg = __le32_to_cpu(super->s_first_meta_bg);
80 }
81
82 void ext2_read_block(ext2_VOLUME* volume, unsigned int fsblock)
83 {
84         long long offset;
85
86         if (fsblock == volume->current)
87                 return;
88
89         volume->current = fsblock;
90         offset = fsblock * EXT2_BLOCK_SIZE(volume->super);
91
92         seek_io(volume->fd, offset);
93         read_io(volume->fd, volume->buffer, EXT2_BLOCK_SIZE(volume->super));
94 }
95
96 void ext2_get_group_desc(ext2_VOLUME* volume,
97                    int group_id, struct ext2_group_desc *gdp)
98 {
99         unsigned int block, offset;
100         struct ext2_group_desc *le_gdp;
101
102         block = 1 + volume->super->s_first_data_block;
103         block += group_id / EXT2_DESC_PER_BLOCK(volume->super);
104         ext2_read_block(volume,  block);
105
106         offset = group_id % EXT2_DESC_PER_BLOCK(volume->super);
107         offset *= sizeof(*gdp);
108
109         le_gdp = (struct ext2_group_desc *)(volume->buffer + offset);
110
111         gdp->bg_block_bitmap = __le32_to_cpu(le_gdp->bg_block_bitmap);
112         gdp->bg_inode_bitmap = __le32_to_cpu(le_gdp->bg_inode_bitmap);
113         gdp->bg_inode_table = __le32_to_cpu(le_gdp->bg_inode_table);
114         gdp->bg_free_blocks_count = __le16_to_cpu(le_gdp->bg_free_blocks_count);
115         gdp->bg_free_inodes_count = __le16_to_cpu(le_gdp->bg_free_inodes_count);
116         gdp->bg_used_dirs_count = __le16_to_cpu(le_gdp->bg_used_dirs_count);
117 }
118
119 int ext2_get_inode(ext2_VOLUME* volume,
120                     unsigned int ino, struct ext2_inode *inode)
121 {
122         struct ext2_group_desc desc;
123         unsigned int block;
124         unsigned int group_id;
125         unsigned int offset;
126         struct ext2_inode *le_inode;
127         int i;
128
129         ino--;
130
131         group_id = ino / EXT2_INODES_PER_GROUP(volume->super);
132         ext2_get_group_desc(volume, group_id, &desc);
133
134         ino %= EXT2_INODES_PER_GROUP(volume->super);
135
136         block = desc.bg_inode_table;
137         block += ino / (EXT2_BLOCK_SIZE(volume->super) /
138                         EXT2_INODE_SIZE(volume->super));
139         ext2_read_block(volume, block);
140
141         offset = ino % (EXT2_BLOCK_SIZE(volume->super) /
142                         EXT2_INODE_SIZE(volume->super));
143         offset *= EXT2_INODE_SIZE(volume->super);
144
145         le_inode = (struct ext2_inode *)(volume->buffer + offset);
146
147         inode->i_mode = __le16_to_cpu(le_inode->i_mode);
148         inode->i_uid = __le16_to_cpu(le_inode->i_uid);
149         inode->i_size = __le32_to_cpu(le_inode->i_size);
150         inode->i_atime = __le32_to_cpu(le_inode->i_atime);
151         inode->i_ctime = __le32_to_cpu(le_inode->i_ctime);
152         inode->i_mtime = __le32_to_cpu(le_inode->i_mtime);
153         inode->i_dtime = __le32_to_cpu(le_inode->i_dtime);
154         inode->i_gid = __le16_to_cpu(le_inode->i_gid);
155         inode->i_links_count = __le16_to_cpu(le_inode->i_links_count);
156         inode->i_blocks = __le32_to_cpu(le_inode->i_blocks);
157         inode->i_flags = __le32_to_cpu(le_inode->i_flags);
158         if (S_ISLNK(inode->i_mode)) {
159                 memcpy(inode->i_block, le_inode->i_block, EXT2_N_BLOCKS * 4);
160         } else {
161                 for (i = 0; i < EXT2_N_BLOCKS; i++)
162                         inode->i_block[i] = __le32_to_cpu(le_inode->i_block[i]);
163         }
164         inode->i_generation = __le32_to_cpu(le_inode->i_generation);
165         inode->i_file_acl = __le32_to_cpu(le_inode->i_file_acl);
166         inode->i_dir_acl = __le32_to_cpu(le_inode->i_dir_acl);
167         inode->i_faddr = __le32_to_cpu(le_inode->i_faddr);
168         inode->osd2.linux2.l_i_frag = le_inode->osd2.linux2.l_i_frag;
169         inode->osd2.linux2.l_i_fsize = le_inode->osd2.linux2.l_i_fsize;
170         inode->osd2.linux2.l_i_uid_high =
171                         __le16_to_cpu(le_inode->osd2.linux2.l_i_uid_high);
172         inode->osd2.linux2.l_i_gid_high =
173                         __le16_to_cpu(le_inode->osd2.linux2.l_i_gid_high);
174         return 0;
175 }
176
177 unsigned int ext2_get_block_addr(ext2_VOLUME* volume, struct ext2_inode *inode,
178                                  unsigned int logical)
179 {
180         unsigned int physical;
181         unsigned int addr_per_block;
182
183         /* direct */
184
185         if (logical < EXT2_NDIR_BLOCKS) {
186                 physical = inode->i_block[logical];
187                 return physical;
188         }
189
190         /* indirect */
191
192         logical -= EXT2_NDIR_BLOCKS;
193
194         addr_per_block = EXT2_ADDR_PER_BLOCK (volume->super);
195         if (logical < addr_per_block) {
196                 ext2_read_block(volume, inode->i_block[EXT2_IND_BLOCK]);
197                 physical = __le32_to_cpu(((unsigned int *)volume->buffer)[logical]);
198                 return physical;
199         }
200
201         /* double indirect */
202
203         logical -=  addr_per_block;
204
205         if (logical < addr_per_block * addr_per_block) {
206                 ext2_read_block(volume, inode->i_block[EXT2_DIND_BLOCK]);
207                 physical = __le32_to_cpu(((unsigned int *)volume->buffer)
208                                                 [logical / addr_per_block]);
209                 ext2_read_block(volume, physical);
210                 physical = __le32_to_cpu(((unsigned int *)volume->buffer)
211                                                 [logical % addr_per_block]);
212                 return physical;
213         }
214
215         /* triple indirect */
216
217         logical -= addr_per_block * addr_per_block;
218         ext2_read_block(volume, inode->i_block[EXT2_DIND_BLOCK]);
219         physical = __le32_to_cpu(((unsigned int *)volume->buffer)
220                                 [logical / (addr_per_block * addr_per_block)]);
221         ext2_read_block(volume, physical);
222         logical = logical % (addr_per_block * addr_per_block);
223         physical = __le32_to_cpu(((unsigned int *)volume->buffer)[logical / addr_per_block]);
224         ext2_read_block(volume, physical);
225         physical = __le32_to_cpu(((unsigned int *)volume->buffer)[logical % addr_per_block]);
226         return physical;
227 }
228
229 int ext2_read_data(ext2_VOLUME* volume, struct ext2_inode *inode,
230                    off_t offset, char *buffer, size_t length)
231 {
232         unsigned int logical, physical;
233         int blocksize = EXT2_BLOCK_SIZE(volume->super);
234         int shift;
235         size_t read;
236
237         if (offset >= inode->i_size)
238                 return -1;
239
240         if (offset + length >= inode->i_size)
241                 length = inode->i_size - offset;
242
243         read = 0;
244         logical = offset / blocksize;
245         shift = offset % blocksize;
246
247         if (shift) {
248                 physical = ext2_get_block_addr(volume, inode, logical);
249                 ext2_read_block(volume, physical);
250
251                 if (length < blocksize - shift) {
252                         memcpy(buffer, volume->buffer + shift, length);
253                         return length;
254                 }
255                 read += blocksize - shift;
256                 memcpy(buffer, volume->buffer + shift, read);
257
258                 buffer += read;
259                 length -= read;
260                 logical++;
261         }
262
263         while (length) {
264                 physical = ext2_get_block_addr(volume, inode, logical);
265                 ext2_read_block(volume, physical);
266
267                 if (length < blocksize) {
268                         memcpy(buffer, volume->buffer, length);
269                         read += length;
270                         return read;
271                 }
272                 memcpy(buffer, volume->buffer, blocksize);
273
274                 buffer += blocksize;
275                 length -= blocksize;
276                 read += blocksize;
277                 logical++;
278         }
279
280         return read;
281 }
282
283 off_t ext2_dir_entry(ext2_VOLUME *volume, struct ext2_inode *inode,
284                      off_t index, struct ext2_dir_entry_2 *entry)
285 {
286         int ret;
287
288         ret = ext2_read_data(volume, inode, index,
289                              (char*)entry, sizeof(*entry));
290         if (ret == -1)
291                 return -1;
292
293         entry->inode = __le32_to_cpu(entry->inode);
294         entry->rec_len = __le16_to_cpu(entry->rec_len);
295         return index + entry->rec_len;
296 }
297
298 unsigned int ext2_seek_name(ext2_VOLUME *volume, const char *name)
299 {
300         struct ext2_inode inode;
301         int ret;
302         unsigned int ino;
303         off_t index;
304         struct ext2_dir_entry_2 entry;
305
306         ino = EXT2_ROOT_INO;
307         while(1) {
308                 while (*name == '\\')
309                         name++;
310                 if (!*name)
311                     break;
312                 ret = ext2_get_inode(volume, ino, &inode);
313                 if (ret == -1)
314                         return 0;
315                 index = 0;
316                 while (1) {
317                         index = ext2_dir_entry(volume, &inode, index, &entry);
318                         if (index == -1)
319                                 return 0;
320                         ret = strncmp(name, entry.name, entry.name_len);
321                         if (ret == 0  &&
322                             (name[entry.name_len] == 0 ||
323                              name[entry.name_len] == '\\')) {
324                                 ino = entry.inode;
325                                 break;
326                         }
327                 }
328                 name += entry.name_len;
329         }
330
331         return ino;
332 }