fs: ext4: Add support for the creation of symbolic links
authorJean-Jacques Hiblot <jjhiblot@ti.com>
Wed, 13 Feb 2019 11:15:25 +0000 (12:15 +0100)
committerTom Rini <trini@konsulko.com>
Wed, 10 Apr 2019 00:03:30 +0000 (20:03 -0400)
Re-use the functions used to write/create a file, to support creation of a
symbolic link.
The difference with a regular file are small:
- The inode mode is flagged with S_IFLNK instead of S_IFREG
- The ext2_dirent's filetype is FILETYPE_SYMLINK instead of FILETYPE_REG
- Instead of storing the content of a file in allocated blocks, the path
to the target is stored. And if the target's path is short enough, no block
is allocated and the target's path is stored in ext2_inode.b.symlink

As with regulars files, if a file/symlink with the same name exits, it is
unlinked first and then re-created.

Signed-off-by: Jean-Jacques Hiblot <jjhiblot@ti.com>
Reviewed-by: Tom Rini <trini@konsulko.com>
[trini: Fix ext4 env code]
Signed-off-by: Tom Rini <trini@konsulko.com>
env/ext4.c
fs/ext4/ext4_common.c
fs/ext4/ext4_write.c
include/ext4fs.h

index 09c5e4a491018a35adaf1fba3294b3778f9d70e6..388474a11c750070e444609e2676878db71aa4bd 100644 (file)
@@ -60,7 +60,7 @@ static int env_ext4_save(void)
        }
 
        err = ext4fs_write(CONFIG_ENV_EXT4_FILE, (void *)&env_new,
-                          sizeof(env_t));
+                          sizeof(env_t), FILETYPE_REG);
        ext4fs_close();
 
        if (err == -1) {
index e9123785f116bc70a1091583c11d0a775db7341c..59ad6c8f8c188562c2274a05618668fe152a2303 100644 (file)
@@ -608,7 +608,7 @@ restart_read:
                dir->direntlen = cpu_to_le16(fs->blksz - totalbytes);
 
        dir->namelen = strlen(filename);
-       dir->filetype = FILETYPE_REG;   /* regular file */
+       dir->filetype = file_type;
        temp_dir = (char *)dir;
        temp_dir = temp_dir + sizeof(struct ext2_dirent);
        memcpy(temp_dir, filename, strlen(filename));
index b5b7ee813369a546d6ec0f04be044fd115696c0c..504d23a8956cc157f843de22a9c15e72c2290595 100644 (file)
@@ -465,6 +465,15 @@ static int ext4fs_delete_file(int inodeno)
        if (le32_to_cpu(inode.size) % fs->blksz)
                no_blocks++;
 
+       /*
+        * special case for symlinks whose target are small enough that
+        *it fits in struct ext2_inode.b.symlink: no block had been allocated
+        */
+       if ((le16_to_cpu(inode.mode) & S_IFLNK) &&
+           le32_to_cpu(inode.size) <= sizeof(inode.b.symlink)) {
+               no_blocks = 0;
+       }
+
        if (le32_to_cpu(inode.flags) & EXT4_EXTENTS_FL) {
                /* FIXME delete extent index blocks, i.e. eh_depth >= 1 */
                struct ext4_extent_header *eh =
@@ -830,7 +839,7 @@ static int ext4fs_write_file(struct ext2_inode *file_inode,
 }
 
 int ext4fs_write(const char *fname, const char *buffer,
-                unsigned long sizebytes)
+                unsigned long sizebytes, int type)
 {
        int ret = 0;
        struct ext2_inode *file_inode = NULL;
@@ -853,8 +862,12 @@ int ext4fs_write(const char *fname, const char *buffer,
        struct ext2_block_group *bgd = NULL;
        struct ext_filesystem *fs = get_fs();
        ALLOC_CACHE_ALIGN_BUFFER(char, filename, 256);
+       bool store_link_in_inode = false;
        memset(filename, 0x00, 256);
 
+       if (type != FILETYPE_REG && type != FILETYPE_SYMLINK)
+               return -1;
+
        g_parent_inode = zalloc(fs->inodesz);
        if (!g_parent_inode)
                goto fail;
@@ -892,8 +905,16 @@ int ext4fs_write(const char *fname, const char *buffer,
                if (ret)
                        goto fail;
        }
-       /* calucalate how many blocks required */
-       bytes_reqd_for_file = sizebytes;
+
+       /* calculate how many blocks required */
+       if (type == FILETYPE_SYMLINK &&
+           sizebytes <= sizeof(file_inode->b.symlink)) {
+               store_link_in_inode = true;
+               bytes_reqd_for_file = 0;
+       } else {
+               bytes_reqd_for_file = sizebytes;
+       }
+
        blks_reqd_for_file = lldiv(bytes_reqd_for_file, fs->blksz);
        if (do_div(bytes_reqd_for_file, fs->blksz) != 0) {
                blks_reqd_for_file++;
@@ -906,7 +927,7 @@ int ext4fs_write(const char *fname, const char *buffer,
                goto fail;
        }
 
-       inodeno = ext4fs_update_parent_dentry(filename, FILETYPE_REG);
+       inodeno = ext4fs_update_parent_dentry(filename, type);
        if (inodeno == -1)
                goto fail;
        /* prepare file inode */
@@ -914,14 +935,23 @@ int ext4fs_write(const char *fname, const char *buffer,
        if (!inode_buffer)
                goto fail;
        file_inode = (struct ext2_inode *)inode_buffer;
-       file_inode->mode = cpu_to_le16(S_IFREG | S_IRWXU |
-           S_IRGRP | S_IROTH | S_IXGRP | S_IXOTH);
+       file_inode->size = cpu_to_le32(sizebytes);
+       if (type == FILETYPE_SYMLINK) {
+               file_inode->mode = cpu_to_le16(S_IFLNK | S_IRWXU | S_IRWXG |
+                                              S_IRWXO);
+               if (store_link_in_inode) {
+                       strncpy(file_inode->b.symlink, buffer, sizebytes);
+                       sizebytes = 0;
+               }
+       } else {
+               file_inode->mode = cpu_to_le16(S_IFREG | S_IRWXU | S_IRGRP |
+                                              S_IROTH | S_IXGRP | S_IXOTH);
+       }
        /* ToDo: Update correct time */
        file_inode->mtime = cpu_to_le32(timestamp);
        file_inode->atime = cpu_to_le32(timestamp);
        file_inode->ctime = cpu_to_le32(timestamp);
        file_inode->nlinks = cpu_to_le16(1);
-       file_inode->size = cpu_to_le32(sizebytes);
 
        /* Allocate data blocks */
        ext4fs_allocate_blocks(file_inode, blocks_remaining,
@@ -1013,7 +1043,7 @@ int ext4_write_file(const char *filename, void *buf, loff_t offset,
                return -1;
        }
 
-       ret = ext4fs_write(filename, buf, len);
+       ret = ext4fs_write(filename, buf, len, FILETYPE_REG);
        if (ret) {
                printf("** Error ext4fs_write() **\n");
                goto fail;
@@ -1028,3 +1058,8 @@ fail:
 
        return -1;
 }
+
+int ext4fs_create_link(const char *target, const char *fname)
+{
+       return ext4fs_write(fname, target, strlen(target), FILETYPE_SYMLINK);
+}
index 7d48b2bc4653828cafb2f3409f2383bd91e8e5db..34585d407d06d9aa861ace1144af627163b77554 100644 (file)
@@ -135,9 +135,10 @@ int ext4fs_init(void);
 void ext4fs_deinit(void);
 int ext4fs_filename_unlink(char *filename);
 int ext4fs_write(const char *fname, const char *buffer,
-                unsigned long sizebytes);
+                                unsigned long sizebytes, int type);
 int ext4_write_file(const char *filename, void *buf, loff_t offset, loff_t len,
                    loff_t *actwrite);
+int ext4fs_create_link(const char *target, const char *fname);
 #endif
 
 struct ext_filesystem *get_fs(void);