shithub: lwext4

Download patch

ref: dfa54d1e23c32059b292bbbb99d4251bf15e7517
parent: 4e5365b36f1626bf784b35e09aaf6ecd47a103b3
parent: f4d662d77b4a0376d6a58475839f5a2a89b2c966
author: gkostka <kostka.grzegorz@gmail.com>
date: Tue Sep 22 03:36:56 EDT 2015

Merge pull request #5 from ngkaho1234/master

Initial Symbolic link support.(Creation, deletion, readlink, but no modification can be made.)

--- a/lwext4/ext4.c
+++ b/lwext4/ext4.c
@@ -284,7 +284,7 @@
 
 	/* Cannot unlink non-empty node */
 	if (has_children)
-		return ENOTSUP;
+		return ENOTEMPTY;
 
 	/* Remove entry from parent directory */
 	rc = ext4_dir_remove_entry(parent, name, name_len);
@@ -582,7 +582,7 @@
 			      uint32_t *name_off)
 {
 	bool is_goal = false;
-	uint8_t inode_type = EXT4_DIRECTORY_FILETYPE_DIR;
+	uint8_t inode_type;
 	uint32_t next_inode;
 
 	int r;
@@ -642,8 +642,8 @@
 			struct ext4_inode_ref child_ref;
 			r = ext4_fs_alloc_inode(
 			    &mp->fs, &child_ref,
-			    is_goal ? (filetype == EXT4_DIRECTORY_FILETYPE_DIR)
-				    : true);
+			    is_goal ? filetype
+				    : EXT4_DIRECTORY_FILETYPE_DIR);
 			if (r != EOK)
 				break;
 
@@ -1028,7 +1028,9 @@
 		return ENOENT;
 
 	EXT4_MP_LOCK(mp);
-	r = ext4_generic_open(&f, path, "r", true, &parent_inode, &name_off);
+	r = ext4_generic_open2(&f, path, O_RDWR,
+			       EXT4_DIRECTORY_FILETYPE_UNKNOWN,
+			       &parent_inode, &name_off);
 	if (r != EOK) {
 		EXT4_MP_UNLOCK(mp);
 		return r;
@@ -1147,6 +1149,28 @@
 	return r;
 }
 
+int ext4_fopen_all(ext4_file *f, const char *path, int flags)
+{
+	struct ext4_mountpoint *mp = ext4_get_mount(path);
+	int r;
+	int filetype;
+
+	if (!mp)
+		return ENOENT;
+
+	if (flags & O_CREAT)
+		return EINVAL;
+
+	filetype = EXT4_DIRECTORY_FILETYPE_UNKNOWN;
+
+	EXT4_MP_LOCK(mp);
+	ext4_block_cache_write_back(mp->fs.bdev, 1);
+	r = ext4_generic_open2(f, path, flags, filetype, 0, 0);
+	ext4_block_cache_write_back(mp->fs.bdev, 0);
+	EXT4_MP_UNLOCK(mp);
+	return r;
+}
+
 int ext4_fclose(ext4_file *f)
 {
 	ext4_assert(f && f->mp);
@@ -1178,23 +1202,6 @@
 		goto Finish;
 	}
 
-	if ((ext4_inode_get_mode(&f->mp->fs.sb, ref.inode) & EXT4_INODE_MODE_SOFTLINK)
-			== EXT4_INODE_MODE_SOFTLINK
-			&& f->fsize < sizeof(ref.inode->blocks)
-			&& !ext4_inode_get_blocks_count(&f->mp->fs.sb, ref.inode)) {
-		char *content = (char *)ref.inode->blocks;
-		memset(content + size, 0, sizeof(ref.inode->blocks) - size);
-		ext4_inode_set_size(ref.inode, size);
-		ref.dirty = true;
-
-		f->fsize = size;
-		if (f->fpos > size)
-			f->fpos = size;
-
-		r = EOK;
-		goto Finish;
-	}
-
 	/*Start write back cache mode.*/
 	r = ext4_block_cache_write_back(f->mp->fs.bdev, 1);
 	if (r != EOK)
@@ -1278,6 +1285,30 @@
 	sblock_end = (f->fpos + size) / block_size;
 	u = (f->fpos) % block_size;
 
+	/*If the size of symlink is smaller than 60 bytes*/
+	if (ext4_inode_is_type(&f->mp->fs.sb, ref.inode, EXT4_INODE_MODE_SOFTLINK)
+			&& f->fsize < sizeof(ref.inode->blocks)
+			&& !ext4_inode_get_blocks_count(&f->mp->fs.sb, ref.inode)) {
+		char *content = (char *)ref.inode->blocks;
+		if (f->fpos < f->fsize) {
+			r = (u + size > f->fsize)
+				?(f->fsize - u)
+				:(size);
+			memcpy(buf, content + u, r);
+			if (rcnt)
+				*rcnt = r;
+
+		} else {
+			r = 0;
+			if (rcnt)
+				*rcnt = 0;
+
+		}
+
+		r = EOK;
+		goto Finish;
+	}
+
 	if (u) {
 
 		uint32_t ll = size > (block_size - u) ? (block_size - u) : size;
@@ -1710,6 +1741,109 @@
 	inode_ref.dirty = true;
 
 	ext4_fs_put_inode_ref(&inode_ref);
+	EXT4_MP_UNLOCK(mp);
+	return r;
+}
+
+static int ext4_fsymlink_set(ext4_file *f, const void *buf, uint32_t size)
+{
+	struct ext4_block b;
+	struct ext4_inode_ref ref;
+	uint32_t sblock, fblock;
+	uint32_t block_size;
+	int r;
+
+	ext4_assert(f && f->mp);
+
+	if (!size)
+		return EOK;
+
+	r = ext4_fs_get_inode_ref(&f->mp->fs, f->inode, &ref);
+	if (r != EOK) {
+		EXT4_MP_UNLOCK(f->mp);
+		return r;
+	}
+
+	/*Sync file size*/
+	block_size = ext4_sb_get_block_size(&f->mp->fs.sb);
+	if (size > block_size) {
+		r = EINVAL;
+		goto Finish;
+	}
+	r = ext4_ftruncate_no_lock(f, 0);
+	if (r != EOK)
+		goto Finish;
+
+	/*Start write back cache mode.*/
+	r = ext4_block_cache_write_back(f->mp->fs.bdev, 1);
+	if (r != EOK)
+		goto Finish;
+
+	/*If the size of symlink is smaller than 60 bytes*/
+	if (size < sizeof(ref.inode->blocks)) {
+		char *content = (char *)ref.inode->blocks;
+		memset(content, 0, sizeof(ref.inode->blocks));
+		memcpy(content, buf, size);
+		ext4_inode_clear_flag(ref.inode, EXT4_INODE_FLAG_EXTENTS);
+	} else {
+		ext4_fs_inode_blocks_init(&f->mp->fs, &ref);
+		r = ext4_fs_append_inode_block(&ref, &fblock, &sblock);
+		if (r != EOK)
+			goto Finish;
+
+		r = ext4_block_get(f->mp->fs.bdev, &b, fblock);
+		if (r != EOK)
+			goto Finish;
+
+		memcpy(b.data, buf, size);
+		b.dirty = true;
+		r = ext4_block_set(f->mp->fs.bdev, &b);
+		if (r != EOK)
+			goto Finish;
+	}
+
+	/*Stop write back cache mode*/
+	ext4_block_cache_write_back(f->mp->fs.bdev, 0);
+
+	if (r != EOK)
+		goto Finish;
+
+	ext4_inode_set_size(ref.inode, size);
+	ref.dirty = true;
+
+	f->fsize = size;
+	if (f->fpos > size)
+		f->fpos = size;
+
+Finish:
+	ext4_fs_put_inode_ref(&ref);
+	return r;
+}
+
+int ext4_fsymlink(const char *target, const char *path)
+{
+	struct ext4_mountpoint *mp = ext4_get_mount(path);
+	int r;
+	ext4_file f;
+	int filetype;
+
+	if (!mp)
+		return ENOENT;
+
+	filetype = EXT4_DIRECTORY_FILETYPE_SYMLINK;
+
+	EXT4_MP_LOCK(mp);
+	ext4_block_cache_write_back(mp->fs.bdev, 1);
+	r = ext4_generic_open2(&f, path, O_RDWR|O_CREAT, filetype, 0, 0);
+	if (r == EOK)
+		r = ext4_fsymlink_set(&f, target, strlen(target));
+	else
+		goto Finish;
+
+	ext4_fclose(&f);
+
+Finish:
+	ext4_block_cache_write_back(mp->fs.bdev, 0);
 	EXT4_MP_UNLOCK(mp);
 	return r;
 }
--- a/lwext4/ext4.h
+++ b/lwext4/ext4.h
@@ -360,6 +360,8 @@
 int ext4_file_set_mtime(ext4_file *f, uint32_t mtime);
 int ext4_file_set_ctime(ext4_file *f, uint32_t ctime);
 
+int ext4_fsymlink(const char *target, const char *path);
+
 /*********************************DIRECTORY OPERATION***********************/
 
 /**@brief   Recursive directory remove.
--- a/lwext4/ext4_dir.c
+++ b/lwext4/ext4_dir.c
@@ -206,6 +206,25 @@
 	/* Check maximum entry length */
 	ext4_assert(entry_len <= ext4_sb_get_block_size(sb));
 
+	/* Set type of entry */
+	switch (ext4_inode_type(sb, child->inode)) {
+	case EXT4_INODE_MODE_DIRECTORY:
+		ext4_dir_entry_ll_set_inode_type(sb, entry,
+						 EXT4_DIRECTORY_FILETYPE_DIR);
+		break;
+	case EXT4_INODE_MODE_FILE:
+		ext4_dir_entry_ll_set_inode_type(
+		    sb, entry, EXT4_DIRECTORY_FILETYPE_REG_FILE);
+		break;
+	case EXT4_INODE_MODE_SOFTLINK:
+		ext4_dir_entry_ll_set_inode_type(
+		    sb, entry, EXT4_DIRECTORY_FILETYPE_SYMLINK);
+		break;
+	default:
+		/* FIXME: right now we only support 3 inode type. */
+		ext4_assert(0);
+	}
+
 	/* Set basic attributes */
 	ext4_dir_entry_ll_set_inode(entry, child->index);
 	ext4_dir_entry_ll_set_entry_length(entry, entry_len);
@@ -213,14 +232,6 @@
 
 	/* Write name */
 	memcpy(entry->name, name, name_len);
-
-	/* Set type of entry */
-	if (ext4_inode_is_type(sb, child->inode, EXT4_INODE_MODE_DIRECTORY))
-		ext4_dir_entry_ll_set_inode_type(sb, entry,
-						 EXT4_DIRECTORY_FILETYPE_DIR);
-	else
-		ext4_dir_entry_ll_set_inode_type(
-		    sb, entry, EXT4_DIRECTORY_FILETYPE_REG_FILE);
 }
 
 int ext4_dir_add_entry(struct ext4_inode_ref *parent, const char *name,
--- a/lwext4/ext4_errno.h
+++ b/lwext4/ext4_errno.h
@@ -41,41 +41,42 @@
 #if !CONFIG_HAVE_OWN_ERRNO
 #include <errno.h>
 #else
-#define EPERM 1    /* Operation not permitted */
-#define ENOENT 2   /* No such file or directory */
-#define ESRCH 3    /* No such process */
-#define EINTR 4    /* Interrupted system call */
-#define EIO 5      /* I/O error */
-#define ENXIO 6    /* No such device or address */
-#define E2BIG 7    /* Argument list too long */
-#define ENOEXEC 8  /* Exec format error */
-#define EBADF 9    /* Bad file number */
-#define ECHILD 10  /* No child processes */
-#define EAGAIN 11  /* Try again */
-#define ENOMEM 12  /* Out of memory */
-#define EACCES 13  /* Permission denied */
-#define EFAULT 14  /* Bad address */
-#define ENOTBLK 15 /* Block device required */
-#define EBUSY 16   /* Device or resource busy */
-#define EEXIST 17  /* File exists */
-#define EXDEV 18   /* Cross-device link */
-#define ENODEV 19  /* No such device */
-#define ENOTDIR 20 /* Not a directory */
-#define EISDIR 21  /* Is a directory */
-#define EINVAL 22  /* Invalid argument */
-#define ENFILE 23  /* File table overflow */
-#define EMFILE 24  /* Too many open files */
-#define ENOTTY 25  /* Not a typewriter */
-#define ETXTBSY 26 /* Text file busy */
-#define EFBIG 27   /* File too large */
-#define ENOSPC 28  /* No space left on device */
-#define ESPIPE 29  /* Illegal seek */
-#define EROFS 30   /* Read-only file system */
-#define EMLINK 31  /* Too many links */
-#define EPIPE 32   /* Broken pipe */
-#define EDOM 33    /* Math argument out of domain of func */
-#define ERANGE 34  /* Math result not representable */
-#define ENOTSUP 95 /* Not supported */
+#define EPERM 1      /* Operation not permitted */
+#define ENOENT 2     /* No such file or directory */
+#define ESRCH 3      /* No such process */
+#define EINTR 4      /* Interrupted system call */
+#define EIO 5        /* I/O error */
+#define ENXIO 6      /* No such device or address */
+#define E2BIG 7      /* Argument list too long */
+#define ENOEXEC 8    /* Exec format error */
+#define EBADF 9      /* Bad file number */
+#define ECHILD 10    /* No child processes */
+#define EAGAIN 11    /* Try again */
+#define ENOMEM 12    /* Out of memory */
+#define EACCES 13    /* Permission denied */
+#define EFAULT 14    /* Bad address */
+#define ENOTBLK 15   /* Block device required */
+#define EBUSY 16     /* Device or resource busy */
+#define EEXIST 17    /* File exists */
+#define EXDEV 18     /* Cross-device link */
+#define ENODEV 19    /* No such device */
+#define ENOTDIR 20   /* Not a directory */
+#define EISDIR 21    /* Is a directory */
+#define EINVAL 22    /* Invalid argument */
+#define ENFILE 23    /* File table overflow */
+#define EMFILE 24    /* Too many open files */
+#define ENOTTY 25    /* Not a typewriter */
+#define ETXTBSY 26   /* Text file busy */
+#define EFBIG 27     /* File too large */
+#define ENOSPC 28    /* No space left on device */
+#define ESPIPE 29    /* Illegal seek */
+#define EROFS 30     /* Read-only file system */
+#define EMLINK 31    /* Too many links */
+#define EPIPE 32     /* Broken pipe */
+#define EDOM 33      /* Math argument out of domain of func */
+#define ERANGE 34    /* Math result not representable */
+#define ENOTEMPTY 39 /* Directory not empty */
+#define ENOTSUP 95   /* Not supported */
 #endif
 
 #ifndef ENOTSUP
--- a/lwext4/ext4_fs.c
+++ b/lwext4/ext4_fs.c
@@ -613,14 +613,59 @@
 	return ext4_block_set(ref->fs->bdev, &ref->block);
 }
 
+void ext4_fs_inode_blocks_init(struct ext4_fs *fs, struct ext4_inode_ref *inode_ref)
+{
+	int i;
+	struct ext4_inode *inode = inode_ref->inode;
+
+	for (i = 0; i < EXT4_INODE_BLOCKS; i++)
+		inode->blocks[i] = 0;
+
+#if CONFIG_EXTENT_ENABLE
+	/* Initialize extents if needed */
+	if (ext4_sb_has_feature_incompatible(&fs->sb,
+				EXT4_FEATURE_INCOMPAT_EXTENTS)) {
+		ext4_inode_set_flag(inode, EXT4_INODE_FLAG_EXTENTS);
+
+		/* Initialize extent root header */
+		struct ext4_extent_header *header = ext4_inode_get_extent_header(inode);
+		ext4_extent_header_set_depth(header, 0);
+		ext4_extent_header_set_entries_count(header, 0);
+		ext4_extent_header_set_generation(header, 0);
+		ext4_extent_header_set_magic(header, EXT4_EXTENT_MAGIC);
+
+		uint16_t max_entries = (EXT4_INODE_BLOCKS * sizeof(uint32_t) -
+				sizeof(struct ext4_extent_header)) /
+			sizeof(struct ext4_extent);
+
+		ext4_extent_header_set_max_entries_count(header, max_entries);
+	}
+#endif
+}
+
+static uint32_t ext4_fs_correspond_inode_mode(int filetype)
+{
+	switch (filetype) {
+	case EXT4_DIRECTORY_FILETYPE_DIR:
+		return EXT4_INODE_MODE_DIRECTORY;
+	case EXT4_DIRECTORY_FILETYPE_REG_FILE:
+		return EXT4_INODE_MODE_FILE;
+	case EXT4_DIRECTORY_FILETYPE_SYMLINK:
+		return EXT4_INODE_MODE_SOFTLINK;
+	default:
+		/* FIXME: right now we only support 3 file type. */
+		ext4_assert(0);
+	}
+	return 0;
+}
+
 int ext4_fs_alloc_inode(struct ext4_fs *fs, struct ext4_inode_ref *inode_ref,
-			bool is_directory)
+			int filetype)
 {
 	/* Check if newly allocated i-node will be a directory */
-	uint32_t i;
 	bool is_dir;
 
-	is_dir = is_directory;
+	is_dir = (filetype == EXT4_DIRECTORY_FILETYPE_DIR);
 
 	/* Allocate inode by allocation algorithm */
 	uint32_t index;
@@ -638,7 +683,7 @@
 	/* Initialize i-node */
 	struct ext4_inode *inode = inode_ref->inode;
 
-	uint16_t mode;
+	uint32_t mode;
 	if (is_dir) {
 		/*
 		 * Default directory permissions to be compatible with other
@@ -648,7 +693,6 @@
 
 		mode = 0777;
 		mode |= EXT4_INODE_MODE_DIRECTORY;
-		ext4_inode_set_mode(&fs->sb, inode, mode);
 	} else {
 		/*
 		 * Default file permissions to be compatible with other systems
@@ -656,9 +700,9 @@
 		 */
 
 		mode = 0666;
-		mode |= EXT4_INODE_MODE_FILE;
-		ext4_inode_set_mode(&fs->sb, inode, mode);
+		mode |= ext4_fs_correspond_inode_mode(filetype);
 	}
+	ext4_inode_set_mode(&fs->sb, inode, mode);
 
 	ext4_inode_set_links_count(inode, 0);
 	ext4_inode_set_uid(inode, 0);
@@ -672,32 +716,15 @@
 	ext4_inode_set_flags(inode, 0);
 	ext4_inode_set_generation(inode, 0);
 
-	/* Reset blocks array */
-	for (i = 0; i < EXT4_INODE_BLOCKS; i++)
-		inode->blocks[i] = 0;
+	/* Reset blocks array. For symbolic link inode, just
+	 * fill in blocks with 0 */
+	if (ext4_inode_is_type(&fs->sb, inode, EXT4_INODE_MODE_SOFTLINK)) {
+		for (int i = 0; i < EXT4_INODE_BLOCKS; i++)
+			inode->blocks[i] = 0;
 
-#if CONFIG_EXTENT_ENABLE
-	/* Initialize extents if needed */
-	if (ext4_sb_has_feature_incompatible(&fs->sb,
-					     EXT4_FEATURE_INCOMPAT_EXTENTS)) {
-		ext4_inode_set_flag(inode, EXT4_INODE_FLAG_EXTENTS);
+	} else
+		ext4_fs_inode_blocks_init(fs, inode_ref);
 
-		/* Initialize extent root header */
-		struct ext4_extent_header *header =
-		    ext4_inode_get_extent_header(inode);
-		ext4_extent_header_set_depth(header, 0);
-		ext4_extent_header_set_entries_count(header, 0);
-		ext4_extent_header_set_generation(header, 0);
-		ext4_extent_header_set_magic(header, EXT4_EXTENT_MAGIC);
-
-		uint16_t max_entries = (EXT4_INODE_BLOCKS * sizeof(uint32_t) -
-					sizeof(struct ext4_extent_header)) /
-				       sizeof(struct ext4_extent);
-
-		ext4_extent_header_set_max_entries_count(header, max_entries);
-	}
-#endif
-
 	inode_ref->dirty = true;
 
 	return EOK;
@@ -867,6 +894,18 @@
 	/* It's not supported to make the larger file by truncate operation */
 	if (old_size < new_size)
 		return EINVAL;
+
+	if (ext4_inode_is_type(sb, inode_ref->inode, EXT4_INODE_MODE_SOFTLINK)
+			&& old_size < sizeof(inode_ref->inode->blocks)
+			&& !ext4_inode_get_blocks_count(sb, inode_ref->inode)) {
+		char *content = (char *)inode_ref->inode->blocks;
+		memset(content + new_size, 0,
+			sizeof(inode_ref->inode->blocks) - new_size);
+		ext4_inode_set_size(inode_ref->inode, new_size);
+		inode_ref->dirty = true;
+
+		return EOK;
+	}
 
 	/* Compute how many blocks will be released */
 	uint64_t size_diff = old_size - new_size;
--- a/lwext4/ext4_fs.h
+++ b/lwext4/ext4_fs.h
@@ -134,6 +134,12 @@
 int ext4_fs_get_inode_ref(struct ext4_fs *fs, uint32_t index,
 			  struct ext4_inode_ref *ref);
 
+/**@brief Reset blocks field of i-node.
+ * @param fs        Filesystem to reset blocks field of i-inode on
+ * @param inode_ref ref Pointer for inode to be operated on
+ */
+void ext4_fs_inode_blocks_init(struct ext4_fs *fs, struct ext4_inode_ref *inode_ref);
+
 /**@brief Put reference to i-node.
  * @param ref Pointer for reference to be put back
  * @return Error code
@@ -143,11 +149,11 @@
 /**@brief Allocate new i-node in the filesystem.
  * @param fs        Filesystem to allocated i-node on
  * @param inode_ref Output pointer to return reference to allocated i-node
- * @param flags     Flags to be set for newly created i-node
+ * @param filetype  File type of newly created i-node
  * @return Error code
  */
 int ext4_fs_alloc_inode(struct ext4_fs *fs, struct ext4_inode_ref *inode_ref,
-			bool is_directory);
+			int filetype);
 
 /**@brief Release i-node and mark it as free.
  * @param inode_ref I-node to be released
--- a/lwext4/ext4_inode.c
+++ b/lwext4/ext4_inode.c
@@ -290,11 +290,15 @@
 	inode->blocks[idx + EXT4_INODE_INDIRECT_BLOCK] = to_le32(block);
 }
 
+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);
+}
+
 bool ext4_inode_is_type(struct ext4_sblock *sb, struct ext4_inode *inode,
 			uint32_t type)
 {
-	return (ext4_inode_get_mode(sb, inode) & EXT4_INODE_MODE_TYPE_MASK) ==
-	       type;
+	return ext4_inode_type(sb, inode) == type;
 }
 
 bool ext4_inode_has_flag(struct ext4_inode *inode, uint32_t f)
@@ -323,7 +327,8 @@
 		return false;
 
 	if ((ext4_inode_is_type(sb, inode, EXT4_INODE_MODE_FILE)) ||
-	    (ext4_inode_is_type(sb, inode, EXT4_INODE_MODE_DIRECTORY)))
+	    (ext4_inode_is_type(sb, inode, EXT4_INODE_MODE_DIRECTORY)) ||
+	    (ext4_inode_is_type(sb, inode, EXT4_INODE_MODE_SOFTLINK)))
 		return true;
 
 	return false;
--- a/lwext4/ext4_inode.h
+++ b/lwext4/ext4_inode.h
@@ -245,6 +245,13 @@
 void ext4_inode_set_indirect_block(struct ext4_inode *inode, uint32_t idx,
 				   uint32_t block);
 
+/**@brief return the type of i-node
+ * @param sb    Superblock
+ * @param inode I-node to return the type of
+ * @return Result of check operation
+ */
+uint32_t ext4_inode_type(struct ext4_sblock *sb, struct ext4_inode *inode);
+
 /**@brief Check if i-node has specified type.
  * @param sb    Superblock
  * @param inode I-node to check type of