diff mbox series

[U-Boot,RESEND,v5,5/7] fs: ext4: Add support for the creation of symbolic links

Message ID 20190213111527.1525-6-jjhiblot@ti.com
State Accepted
Delegated to: Tom Rini
Headers show
Series Add support for symlink creation in EXT4 | expand

Commit Message

Jean-Jacques Hiblot Feb. 13, 2019, 11:15 a.m. UTC
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>

---

Changes in v5:
- Fix bug in ext4fs_delete_file(). The type must be read from the
  inode.

Changes in v4: None
Changes in v3: None
Changes in v2:
- Fix issue in ext4fs_delete_file() when target in not stored in an
  allocated block

 fs/ext4/ext4_common.c |  2 +-
 fs/ext4/ext4_write.c  | 51 ++++++++++++++++++++++++++++++++++++-------
 include/ext4fs.h      |  3 ++-
 3 files changed, 46 insertions(+), 10 deletions(-)

Comments

Tom Rini April 10, 2019, 12:19 p.m. UTC | #1
On Wed, Feb 13, 2019 at 12:15:25PM +0100, Jean-Jacques Hiblot wrote:

> 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>

Applied to u-boot/master, thanks!
diff mbox series

Patch

diff --git a/fs/ext4/ext4_common.c b/fs/ext4/ext4_common.c
index 1ff2cd28a6..4bc23ab92e 100644
--- a/fs/ext4/ext4_common.c
+++ b/fs/ext4/ext4_common.c
@@ -607,7 +607,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));
diff --git a/fs/ext4/ext4_write.c b/fs/ext4/ext4_write.c
index 51ca1c0916..cf2ac7ed41 100644
--- a/fs/ext4/ext4_write.c
+++ b/fs/ext4/ext4_write.c
@@ -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_ext4fs_init;
@@ -893,8 +906,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++;
@@ -907,7 +928,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 */
@@ -915,14 +936,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,
@@ -1015,7 +1045,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;
@@ -1030,3 +1060,8 @@  fail:
 
 	return -1;
 }
+
+int ext4fs_create_link(const char *target, const char *fname)
+{
+	return ext4fs_write(fname, target, strlen(target), FILETYPE_SYMLINK);
+}
diff --git a/include/ext4fs.h b/include/ext4fs.h
index d4dcefaa96..946dc6a302 100644
--- a/include/ext4fs.h
+++ b/include/ext4fs.h
@@ -129,9 +129,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);