ext4: special inode creation support
authorKaho Ng <ngkaho1234@gmail.com>
Wed, 29 Jun 2016 21:17:33 +0000 (05:17 +0800)
committerKaho Ng <ngkaho1234@gmail.com>
Wed, 29 Jun 2016 21:40:06 +0000 (05:40 +0800)
include/ext4.h
include/ext4_inode.h
src/ext4.c
src/ext4_dir.c
src/ext4_fs.c
src/ext4_inode.c

index fede38f0273651f0833775a834d9eae69d794981..b1c7a6162359d78c4c9f49aaec801dc577207417 100644 (file)
@@ -373,6 +373,14 @@ int ext4_file_set_ctime(const char *path, uint32_t ctime);
  * @return standard error code*/
 int ext4_fsymlink(const char *target, const char *path);
 
+/**@brief Create special file
+ * @param path path to new file
+ * @param filetype The filetype of the new special file
+ *       (that must not be regular file, directory, or unknown type)
+ * @param dev if filetype is char device or block device,
+ *       the device number will become the payload in the inode
+ * @return standard error code*/
+int ext4_mknod(const char *path, int filetype, uint32_t dev);
 
 /**@brief Read symbolic link payload
  * @param path to symlink
index 9498f0a866e435677d69180b81f2f2014f2a0d90..e0ca6e38f81272b352e618990b89383a244f74fd 100644 (file)
@@ -267,6 +267,18 @@ uint32_t ext4_inode_get_indirect_block(struct ext4_inode *inode, uint32_t idx);
 void ext4_inode_set_indirect_block(struct ext4_inode *inode, uint32_t idx,
                                   uint32_t block);
 
+/**@brief Get device number
+ * @param inode  I-node to get device number from
+ * @return Device number
+ */
+uint32_t ext4_inode_get_dev(struct ext4_inode *inode);
+
+/**@brief Set device number
+ * @param inode  I-node to set device number to
+ * @param dev    Device number
+ */
+void ext4_inode_set_dev(struct ext4_inode *inode, uint32_t dev);
+
 /**@brief return the type of i-node
  * @param sb    Superblock
  * @param inode I-node to return the type of
index db7745f678043ef1a5565733533af2d74af95757..0f2f07af9a879bc9904fc5078377cccb0d3332f9 100644 (file)
@@ -2413,6 +2413,72 @@ Finish:
        return r;
 }
 
+static int ext4_mknod_set(ext4_file *f, uint32_t dev)
+{
+       struct ext4_inode_ref ref;
+       int r;
+
+       ext4_assert(f && f->mp);
+
+       r = ext4_fs_get_inode_ref(&f->mp->fs, f->inode, &ref);
+       if (r != EOK)
+               return r;
+
+       ext4_inode_set_dev(ref.inode, dev);
+
+       ext4_inode_set_size(ref.inode, 0);
+       ref.dirty = true;
+
+       f->fsize = 0;
+       f->fpos = 0;
+
+       r = ext4_fs_put_inode_ref(&ref);
+       return r;
+}
+
+int ext4_mknod(const char *path, int filetype, uint32_t dev)
+{
+       struct ext4_mountpoint *mp = ext4_get_mount(path);
+       int r;
+       ext4_file f;
+
+       if (!mp)
+               return ENOENT;
+
+       if (mp->fs.read_only)
+               return EROFS;
+
+       if (filetype == EXT4_DE_UNKNOWN ||
+           filetype == EXT4_DE_REG_FILE ||
+           filetype == EXT4_DE_DIR)
+               return EINVAL;
+
+       EXT4_MP_LOCK(mp);
+       ext4_trans_start(mp);
+
+       ext4_block_cache_write_back(mp->fs.bdev, 1);
+       r = ext4_generic_open2(&f, path, O_RDWR|O_CREAT, filetype, NULL, NULL);
+       if (r == EOK) {
+               if (filetype == EXT4_DE_CHRDEV ||
+                   filetype == EXT4_DE_BLKDEV)
+                       r = ext4_mknod_set(&f, dev);
+       } else
+               goto Finish;
+
+       ext4_fclose(&f);
+
+Finish:
+       ext4_block_cache_write_back(mp->fs.bdev, 0);
+
+       if (r != EOK)
+               ext4_trans_abort(mp);
+       else
+               ext4_trans_stop(mp);
+
+       EXT4_MP_UNLOCK(mp);
+       return r;
+}
+
 int ext4_setxattr(const char *path, const char *name, size_t name_len,
                  const void *data, size_t data_size, bool replace __unused)
 {
index 438d386e3c7b37ac67c8b4cfe6028765b3002810..4836d624c66c475ff8cd7f382ecf68d712741550 100644 (file)
@@ -314,9 +314,21 @@ void ext4_dir_write_entry(struct ext4_sblock *sb, struct ext4_dir_en *en,
        case EXT4_INODE_MODE_SOFTLINK:
                ext4_dir_en_set_inode_type(sb, en, EXT4_DE_SYMLINK);
                break;
+       case EXT4_INODE_MODE_CHARDEV:
+               ext4_dir_en_set_inode_type(sb, en, EXT4_DE_CHRDEV);
+               break;
+       case EXT4_INODE_MODE_BLOCKDEV:
+               ext4_dir_en_set_inode_type(sb, en, EXT4_DE_BLKDEV);
+               break;
+       case EXT4_INODE_MODE_FIFO:
+               ext4_dir_en_set_inode_type(sb, en, EXT4_DE_FIFO);
+               break;
+       case EXT4_INODE_MODE_SOCKET:
+               ext4_dir_en_set_inode_type(sb, en, EXT4_DE_SOCK);
+               break;
        default:
-               /* FIXME: right now we only support 3 inode type. */
-               ext4_assert(0);
+               /* FIXME: unsupported filetype */
+               ext4_dir_en_set_inode_type(sb, en, EXT4_DE_UNKNOWN);
        }
 
        /* Set basic attributes */
index 8a0be5b7756794f06f7d9a3ffbf89de0e4e45c6b..e8b296da9e54f6b32887708a57fa3128e470c5c4 100644 (file)
@@ -832,11 +832,17 @@ uint32_t ext4_fs_correspond_inode_mode(int filetype)
                return EXT4_INODE_MODE_FILE;
        case EXT4_DE_SYMLINK:
                return EXT4_INODE_MODE_SOFTLINK;
-       default:
-               /* FIXME: right now we only support 3 file type. */
-               ext4_assert(0);
+       case EXT4_DE_CHRDEV:
+               return EXT4_INODE_MODE_CHARDEV;
+       case EXT4_DE_BLKDEV:
+               return EXT4_INODE_MODE_BLOCKDEV;
+       case EXT4_DE_FIFO:
+               return EXT4_INODE_MODE_FIFO;
+       case EXT4_DE_SOCK:
+               return EXT4_INODE_MODE_SOCKET;
        }
-       return 0;
+       /* FIXME: unsupported filetype */
+       return EXT4_INODE_MODE_FILE;
 }
 
 int ext4_fs_alloc_inode(struct ext4_fs *fs, struct ext4_inode_ref *inode_ref,
@@ -844,6 +850,7 @@ int ext4_fs_alloc_inode(struct ext4_fs *fs, struct ext4_inode_ref *inode_ref,
 {
        /* Check if newly allocated i-node will be a directory */
        bool is_dir;
+       uint32_t type;
        uint16_t inode_size = ext4_get16(&fs->sb, inode_size);
 
        is_dir = (filetype == EXT4_DE_DIR);
@@ -876,6 +883,14 @@ int ext4_fs_alloc_inode(struct ext4_fs *fs, struct ext4_inode_ref *inode_ref,
 
                mode = 0777;
                mode |= EXT4_INODE_MODE_DIRECTORY;
+       } else if (filetype == EXT4_DE_SYMLINK) {
+               /*
+                * Default symbolic link permissions to be compatible with other systems
+                * 0777 (octal) == rwxrwxrwx
+                */
+
+               mode = 0777;
+               mode |= EXT4_INODE_MODE_SOFTLINK;
        } else {
                /*
                 * Default file permissions to be compatible with other systems
@@ -904,9 +919,13 @@ int ext4_fs_alloc_inode(struct ext4_fs *fs, struct ext4_inode_ref *inode_ref,
                ext4_inode_set_extra_isize(&fs->sb, inode, size);
        }
 
-       /* Reset blocks array. For symbolic link inode, just
+       /* Reset blocks array. For inode which is not directory or file, just
         * fill in blocks with 0 */
-       if (ext4_inode_is_type(&fs->sb, inode, EXT4_INODE_MODE_SOFTLINK)) {
+       type = ext4_inode_type(&fs->sb, inode_ref->inode);
+       if (type == EXT4_INODE_MODE_CHARDEV ||
+           type == EXT4_INODE_MODE_BLOCKDEV ||
+           type == EXT4_INODE_MODE_SOCKET ||
+           type == EXT4_INODE_MODE_SOFTLINK) {
                for (int i = 0; i < EXT4_INODE_BLOCKS; i++)
                        inode->blocks[i] = 0;
 
@@ -1169,6 +1188,7 @@ int ext4_fs_truncate_inode(struct ext4_inode_ref *inode_ref, uint64_t new_size)
        struct ext4_sblock *sb = &inode_ref->fs->sb;
        uint32_t i;
        int r;
+       bool v;
 
        /* Check flags, if i-node can be truncated */
        if (!ext4_inode_can_truncate(sb, inode_ref->inode))
@@ -1183,7 +1203,7 @@ int ext4_fs_truncate_inode(struct ext4_inode_ref *inode_ref, uint64_t new_size)
        if (old_size < new_size)
                return EINVAL;
 
-       bool v;
+       /* For symbolic link which is small enough */
        v = ext4_inode_is_type(sb, inode_ref->inode, EXT4_INODE_MODE_SOFTLINK);
        if (v && old_size < sizeof(inode_ref->inode->blocks) &&
            !ext4_inode_get_blocks_count(sb, inode_ref->inode)) {
@@ -1196,6 +1216,17 @@ int ext4_fs_truncate_inode(struct ext4_inode_ref *inode_ref, uint64_t new_size)
                return EOK;
        }
 
+       i = ext4_inode_type(sb, inode_ref->inode);
+       if (i == EXT4_INODE_MODE_CHARDEV ||
+           i == EXT4_INODE_MODE_BLOCKDEV ||
+           i == EXT4_INODE_MODE_SOCKET) {
+               inode_ref->inode->blocks[0] = 0;
+               inode_ref->inode->blocks[1] = 0;
+
+               inode_ref->dirty = true;
+               return EOK;
+       }
+
        /* Compute how many blocks will be released */
        uint32_t block_size = ext4_sb_get_block_size(sb);
        uint32_t new_blocks_cnt = (uint32_t)((new_size + block_size - 1) / block_size);
index c64ddbc765688cc0b2b2208fbd917e02cdb9756c..513da6015d08b3bd0fab4b818850e9584713889b 100644 (file)
@@ -328,6 +328,26 @@ void ext4_inode_set_indirect_block(struct ext4_inode *inode, uint32_t idx,
        inode->blocks[idx + EXT4_INODE_INDIRECT_BLOCK] = to_le32(block);
 }
 
+uint32_t ext4_inode_get_dev(struct ext4_inode *inode)
+{
+       uint32_t dev_0, dev_1;
+       dev_0 = ext4_inode_get_direct_block(inode, 0);
+       dev_1 = ext4_inode_get_direct_block(inode, 1);
+
+       if (dev_0)
+               return dev_0;
+       else
+               return dev_1;
+}
+
+void ext4_inode_set_dev(struct ext4_inode *inode, uint32_t dev)
+{
+       if (dev & ~0xFFFF)
+               ext4_inode_set_direct_block(inode, 1, dev);
+       else
+               ext4_inode_set_direct_block(inode, 0, dev);
+}
+
 uint32_t ext4_inode_type(struct ext4_sblock *sb, struct ext4_inode *inode)
 {
        return (ext4_inode_get_mode(sb, inode) & EXT4_INODE_MODE_TYPE_MASK);