Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / openbios / fs / ext2 / ext2_utils.c
diff --git a/qemu/roms/openbios/fs/ext2/ext2_utils.c b/qemu/roms/openbios/fs/ext2/ext2_utils.c
new file mode 100644 (file)
index 0000000..64563c8
--- /dev/null
@@ -0,0 +1,332 @@
+/*
+ *
+ * (c) 2008-2009 Laurent Vivier <Laurent@lvivier.info>
+ *
+ * This file has been copied from EMILE, http://emile.sf.net
+ *
+ */
+
+#include "libext2.h"
+#include "ext2_utils.h"
+#include "libopenbios/bindings.h"
+#include "libc/diskio.h"
+#include "libc/byteorder.h"
+
+int ext2_probe(int fd, long long offset)
+{
+       struct ext2_super_block *super;
+
+       super = (struct ext2_super_block*)malloc(sizeof(struct ext2_super_block));
+       seek_io(fd, 2 * 512 + offset);
+       read_io(fd, super, sizeof (*super));
+
+       if (__le16_to_cpu(super->s_magic) != EXT2_SUPER_MAGIC) {
+               free(super);
+               return 0;
+       }
+
+       free(super);
+       return -1;
+}
+
+void ext2_get_super(int fd, struct ext2_super_block *super)
+{
+       seek_io(fd, 2 * 512);
+       read_io(fd, super, sizeof (*super));
+
+       super->s_inodes_count = __le32_to_cpu(super->s_inodes_count);
+       super->s_blocks_count = __le32_to_cpu(super->s_blocks_count);
+       super->s_r_blocks_count = __le32_to_cpu(super->s_r_blocks_count);
+       super->s_free_blocks_count = __le32_to_cpu(super->s_free_blocks_count);
+       super->s_free_inodes_count = __le32_to_cpu(super->s_free_inodes_count);
+       super->s_first_data_block = __le32_to_cpu(super->s_first_data_block);
+       super->s_log_block_size = __le32_to_cpu(super->s_log_block_size);
+       super->s_log_frag_size = __le32_to_cpu(super->s_log_frag_size);
+       super->s_blocks_per_group = __le32_to_cpu(super->s_blocks_per_group);
+       super->s_frags_per_group = __le32_to_cpu(super->s_frags_per_group);
+       super->s_inodes_per_group = __le32_to_cpu(super->s_inodes_per_group);
+       super->s_mtime = __le32_to_cpu(super->s_mtime);
+       super->s_wtime = __le32_to_cpu(super->s_wtime);
+       super->s_mnt_count = __le16_to_cpu(super->s_mnt_count);
+       super->s_max_mnt_count = __le16_to_cpu(super->s_max_mnt_count);
+       super->s_magic = __le16_to_cpu(super->s_magic);
+       super->s_state = __le16_to_cpu(super->s_state);
+       super->s_errors = __le16_to_cpu(super->s_errors);
+       super->s_minor_rev_level = __le16_to_cpu(super->s_minor_rev_level);
+       super->s_lastcheck = __le32_to_cpu(super->s_lastcheck);
+       super->s_checkinterval = __le32_to_cpu(super->s_checkinterval);
+       super->s_creator_os = __le32_to_cpu(super->s_creator_os);
+       super->s_rev_level = __le32_to_cpu(super->s_rev_level);
+       super->s_def_resuid = __le16_to_cpu(super->s_def_resuid);
+       super->s_def_resgid = __le16_to_cpu(super->s_def_resgid);
+       super->s_first_ino = __le32_to_cpu(super->s_first_ino);
+       super->s_inode_size = __le16_to_cpu(super->s_inode_size);
+       super->s_block_group_nr = __le16_to_cpu(super->s_block_group_nr);
+       super->s_feature_compat = __le32_to_cpu(super->s_feature_compat);
+       super->s_feature_incompat = __le32_to_cpu(super->s_feature_incompat);
+       super->s_feature_ro_compat = __le32_to_cpu(super->s_feature_ro_compat);
+       super->s_algorithm_usage_bitmap =
+                               __le32_to_cpu(super->s_algorithm_usage_bitmap);
+       super->s_journal_inum = __le32_to_cpu(super->s_journal_inum);
+       super->s_journal_dev = __le32_to_cpu(super->s_journal_dev);
+       super->s_last_orphan = __le32_to_cpu(super->s_last_orphan);
+       super->s_hash_seed[0] = __le32_to_cpu(super->s_hash_seed[0]);
+       super->s_hash_seed[1] = __le32_to_cpu(super->s_hash_seed[1]);
+       super->s_hash_seed[2] = __le32_to_cpu(super->s_hash_seed[2]);
+       super->s_hash_seed[3] = __le32_to_cpu(super->s_hash_seed[3]);
+       super->s_default_mount_opts =
+                               __le32_to_cpu(super->s_default_mount_opts);
+       super->s_first_meta_bg = __le32_to_cpu(super->s_first_meta_bg);
+}
+
+void ext2_read_block(ext2_VOLUME* volume, unsigned int fsblock)
+{
+       long long offset;
+
+       if (fsblock == volume->current)
+               return;
+
+       volume->current = fsblock;
+       offset = fsblock * EXT2_BLOCK_SIZE(volume->super);
+
+       seek_io(volume->fd, offset);
+       read_io(volume->fd, volume->buffer, EXT2_BLOCK_SIZE(volume->super));
+}
+
+void ext2_get_group_desc(ext2_VOLUME* volume,
+                  int group_id, struct ext2_group_desc *gdp)
+{
+       unsigned int block, offset;
+       struct ext2_group_desc *le_gdp;
+
+       block = 1 + volume->super->s_first_data_block;
+       block += group_id / EXT2_DESC_PER_BLOCK(volume->super);
+       ext2_read_block(volume,  block);
+
+       offset = group_id % EXT2_DESC_PER_BLOCK(volume->super);
+       offset *= sizeof(*gdp);
+
+       le_gdp = (struct ext2_group_desc *)(volume->buffer + offset);
+
+       gdp->bg_block_bitmap = __le32_to_cpu(le_gdp->bg_block_bitmap);
+       gdp->bg_inode_bitmap = __le32_to_cpu(le_gdp->bg_inode_bitmap);
+       gdp->bg_inode_table = __le32_to_cpu(le_gdp->bg_inode_table);
+       gdp->bg_free_blocks_count = __le16_to_cpu(le_gdp->bg_free_blocks_count);
+       gdp->bg_free_inodes_count = __le16_to_cpu(le_gdp->bg_free_inodes_count);
+       gdp->bg_used_dirs_count = __le16_to_cpu(le_gdp->bg_used_dirs_count);
+}
+
+int ext2_get_inode(ext2_VOLUME* volume,
+                   unsigned int ino, struct ext2_inode *inode)
+{
+       struct ext2_group_desc desc;
+       unsigned int block;
+       unsigned int group_id;
+       unsigned int offset;
+       struct ext2_inode *le_inode;
+       int i;
+
+       ino--;
+
+       group_id = ino / EXT2_INODES_PER_GROUP(volume->super);
+       ext2_get_group_desc(volume, group_id, &desc);
+
+       ino %= EXT2_INODES_PER_GROUP(volume->super);
+
+       block = desc.bg_inode_table;
+       block += ino / (EXT2_BLOCK_SIZE(volume->super) /
+                       EXT2_INODE_SIZE(volume->super));
+       ext2_read_block(volume, block);
+
+       offset = ino % (EXT2_BLOCK_SIZE(volume->super) /
+                       EXT2_INODE_SIZE(volume->super));
+       offset *= EXT2_INODE_SIZE(volume->super);
+
+       le_inode = (struct ext2_inode *)(volume->buffer + offset);
+
+       inode->i_mode = __le16_to_cpu(le_inode->i_mode);
+       inode->i_uid = __le16_to_cpu(le_inode->i_uid);
+       inode->i_size = __le32_to_cpu(le_inode->i_size);
+       inode->i_atime = __le32_to_cpu(le_inode->i_atime);
+       inode->i_ctime = __le32_to_cpu(le_inode->i_ctime);
+       inode->i_mtime = __le32_to_cpu(le_inode->i_mtime);
+       inode->i_dtime = __le32_to_cpu(le_inode->i_dtime);
+       inode->i_gid = __le16_to_cpu(le_inode->i_gid);
+       inode->i_links_count = __le16_to_cpu(le_inode->i_links_count);
+       inode->i_blocks = __le32_to_cpu(le_inode->i_blocks);
+       inode->i_flags = __le32_to_cpu(le_inode->i_flags);
+       if (S_ISLNK(inode->i_mode)) {
+               memcpy(inode->i_block, le_inode->i_block, EXT2_N_BLOCKS * 4);
+       } else {
+               for (i = 0; i < EXT2_N_BLOCKS; i++)
+                       inode->i_block[i] = __le32_to_cpu(le_inode->i_block[i]);
+        }
+       inode->i_generation = __le32_to_cpu(le_inode->i_generation);
+       inode->i_file_acl = __le32_to_cpu(le_inode->i_file_acl);
+       inode->i_dir_acl = __le32_to_cpu(le_inode->i_dir_acl);
+       inode->i_faddr = __le32_to_cpu(le_inode->i_faddr);
+       inode->osd2.linux2.l_i_frag = le_inode->osd2.linux2.l_i_frag;
+       inode->osd2.linux2.l_i_fsize = le_inode->osd2.linux2.l_i_fsize;
+       inode->osd2.linux2.l_i_uid_high =
+                       __le16_to_cpu(le_inode->osd2.linux2.l_i_uid_high);
+       inode->osd2.linux2.l_i_gid_high =
+                       __le16_to_cpu(le_inode->osd2.linux2.l_i_gid_high);
+       return 0;
+}
+
+unsigned int ext2_get_block_addr(ext2_VOLUME* volume, struct ext2_inode *inode,
+                                unsigned int logical)
+{
+       unsigned int physical;
+       unsigned int addr_per_block;
+
+       /* direct */
+
+       if (logical < EXT2_NDIR_BLOCKS) {
+               physical = inode->i_block[logical];
+               return physical;
+       }
+
+       /* indirect */
+
+       logical -= EXT2_NDIR_BLOCKS;
+
+       addr_per_block = EXT2_ADDR_PER_BLOCK (volume->super);
+       if (logical < addr_per_block) {
+               ext2_read_block(volume, inode->i_block[EXT2_IND_BLOCK]);
+               physical = __le32_to_cpu(((unsigned int *)volume->buffer)[logical]);
+               return physical;
+       }
+
+       /* double indirect */
+
+       logical -=  addr_per_block;
+
+       if (logical < addr_per_block * addr_per_block) {
+               ext2_read_block(volume, inode->i_block[EXT2_DIND_BLOCK]);
+               physical = __le32_to_cpu(((unsigned int *)volume->buffer)
+                                               [logical / addr_per_block]);
+               ext2_read_block(volume, physical);
+               physical = __le32_to_cpu(((unsigned int *)volume->buffer)
+                                               [logical % addr_per_block]);
+               return physical;
+       }
+
+       /* triple indirect */
+
+       logical -= addr_per_block * addr_per_block;
+       ext2_read_block(volume, inode->i_block[EXT2_DIND_BLOCK]);
+       physical = __le32_to_cpu(((unsigned int *)volume->buffer)
+                               [logical / (addr_per_block * addr_per_block)]);
+       ext2_read_block(volume, physical);
+       logical = logical % (addr_per_block * addr_per_block);
+       physical = __le32_to_cpu(((unsigned int *)volume->buffer)[logical / addr_per_block]);
+       ext2_read_block(volume, physical);
+       physical = __le32_to_cpu(((unsigned int *)volume->buffer)[logical % addr_per_block]);
+       return physical;
+}
+
+int ext2_read_data(ext2_VOLUME* volume, struct ext2_inode *inode,
+                  off_t offset, char *buffer, size_t length)
+{
+       unsigned int logical, physical;
+       int blocksize = EXT2_BLOCK_SIZE(volume->super);
+       int shift;
+       size_t read;
+
+       if (offset >= inode->i_size)
+               return -1;
+
+       if (offset + length >= inode->i_size)
+               length = inode->i_size - offset;
+
+       read = 0;
+       logical = offset / blocksize;
+       shift = offset % blocksize;
+
+       if (shift) {
+               physical = ext2_get_block_addr(volume, inode, logical);
+               ext2_read_block(volume, physical);
+
+               if (length < blocksize - shift) {
+                       memcpy(buffer, volume->buffer + shift, length);
+                       return length;
+               }
+               read += blocksize - shift;
+               memcpy(buffer, volume->buffer + shift, read);
+
+               buffer += read;
+               length -= read;
+               logical++;
+       }
+
+       while (length) {
+               physical = ext2_get_block_addr(volume, inode, logical);
+               ext2_read_block(volume, physical);
+
+               if (length < blocksize) {
+                       memcpy(buffer, volume->buffer, length);
+                       read += length;
+                       return read;
+               }
+               memcpy(buffer, volume->buffer, blocksize);
+
+               buffer += blocksize;
+               length -= blocksize;
+               read += blocksize;
+               logical++;
+       }
+
+       return read;
+}
+
+off_t ext2_dir_entry(ext2_VOLUME *volume, struct ext2_inode *inode,
+                    off_t index, struct ext2_dir_entry_2 *entry)
+{
+       int ret;
+
+       ret = ext2_read_data(volume, inode, index,
+                            (char*)entry, sizeof(*entry));
+       if (ret == -1)
+               return -1;
+
+        entry->inode = __le32_to_cpu(entry->inode);
+        entry->rec_len = __le16_to_cpu(entry->rec_len);
+       return index + entry->rec_len;
+}
+
+unsigned int ext2_seek_name(ext2_VOLUME *volume, const char *name)
+{
+       struct ext2_inode inode;
+       int ret;
+       unsigned int ino;
+       off_t index;
+       struct ext2_dir_entry_2 entry;
+
+       ino = EXT2_ROOT_INO;
+       while(1) {
+               while (*name == '\\')
+                       name++;
+               if (!*name)
+                   break;
+               ret = ext2_get_inode(volume, ino, &inode);
+               if (ret == -1)
+                       return 0;
+               index = 0;
+               while (1) {
+                       index = ext2_dir_entry(volume, &inode, index, &entry);
+                       if (index == -1)
+                               return 0;
+                       ret = strncmp(name, entry.name, entry.name_len);
+                       if (ret == 0  &&
+                           (name[entry.name_len] == 0 ||
+                            name[entry.name_len] == '\\')) {
+                               ino = entry.inode;
+                               break;
+                       }
+               }
+               name += entry.name_len;
+       }
+
+       return ino;
+}