aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDarren Hart <dvhart@infradead.org>2013-01-04 12:00:58 -0800
committerTheodore Ts'o <tytso@mit.edu>2013-01-16 14:09:17 -0500
commitf01c1a6bce5e6674cbfa5260db1691bca01a4410 (patch)
tree077beeb6e772bc6b8bef8ebbb3f88ea2afa0e6bc
parent53f2a1eaf051b9ef8a2927cdd7d6de281c8d5a03 (diff)
downloade2fsprogs-f01c1a6bce5e6674cbfa5260db1691bca01a4410.tar.gz
e2fsprogs-f01c1a6bce5e6674cbfa5260db1691bca01a4410.tar.xz
e2fsprogs-f01c1a6bce5e6674cbfa5260db1691bca01a4410.zip
libext2fs: add the ext2fs_symlink() function
Creating symlinks is a complex affair when accounting for slowlinks. Create a new function, ext2fs_symlink(), modeled after ext2fs_mkdir(). Like ext2fs_mkdir(), ext2fs_symlink() takes on the task of allocating a new inode and block (for slowlinks), setting up sane default values in the inode, copying the target path to either the inode (for fastlinks) or to the first block (for slowlinks), and accounting for the inode and block stats. Disallow link targets longer than blocksize as the Linux kernel prevents this. It does not attempt to expand the parent directory, instead returning EXT2_ET_DIR_NO_SPACE and leaving it to the caller to expand just as ext2fs_mkdir() does. Ideally, I think both of these functions should make a single attempt to expand the directory. [ Fixed a few bugs discovered when creating a test case for ext2fs_symlink() ] Signed-off-by: Darren Hart <dvhart@infradead.org> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu> Cc: "Darrick J. Wong" <darrick.wong@oracle.com> Cc: Andreas Dilger <adilger@dilger.ca>
-rw-r--r--debian/e2fslibs.symbols1
-rw-r--r--lib/ext2fs/Makefile.in8
-rw-r--r--lib/ext2fs/ext2_err.et.in3
-rw-r--r--lib/ext2fs/symlink.c159
4 files changed, 171 insertions, 0 deletions
diff --git a/debian/e2fslibs.symbols b/debian/e2fslibs.symbols
index 947bafa8..b8eea529 100644
--- a/debian/e2fslibs.symbols
+++ b/debian/e2fslibs.symbols
@@ -416,6 +416,7 @@ libext2fs.so.2 e2fslibs #MINVER#
ext2fs_swab16@Base 1.37
ext2fs_swab32@Base 1.37
ext2fs_swab64@Base 1.40
+ ext2fs_symlink@Base 1.42.7
ext2fs_sync_device@Base 1.37
ext2fs_tdb_append@Base 1.40
ext2fs_tdb_chainlock@Base 1.40
diff --git a/lib/ext2fs/Makefile.in b/lib/ext2fs/Makefile.in
index 75d6125d..cc0fc7e7 100644
--- a/lib/ext2fs/Makefile.in
+++ b/lib/ext2fs/Makefile.in
@@ -79,6 +79,7 @@ OBJS= $(DEBUGFS_LIB_OBJS) $(RESIZE_LIB_OBJS) $(E2IMAGE_LIB_OBJS) \
res_gdt.o \
rw_bitmaps.o \
swapfs.o \
+ symlink.o \
tdb.o \
undo_io.o \
unix_io.o \
@@ -153,6 +154,7 @@ SRCS= ext2_err.c \
$(srcdir)/res_gdt.c \
$(srcdir)/rw_bitmaps.c \
$(srcdir)/swapfs.c \
+ $(srcdir)/symlink.c \
$(srcdir)/tdb.c \
$(srcdir)/test_io.c \
$(srcdir)/tst_badblocks.c \
@@ -884,6 +886,12 @@ swapfs.o: $(srcdir)/swapfs.c $(top_builddir)/lib/config.h \
$(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
$(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
$(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h
+symlink.o: $(srcdir)/symlink.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h
tdb.o: $(srcdir)/tdb.c $(top_builddir)/lib/config.h \
$(top_builddir)/lib/dirpaths.h $(srcdir)/tdb.h
test_io.o: $(srcdir)/test_io.c $(top_builddir)/lib/config.h \
diff --git a/lib/ext2fs/ext2_err.et.in b/lib/ext2fs/ext2_err.et.in
index c99a167d..d20c6b73 100644
--- a/lib/ext2fs/ext2_err.et.in
+++ b/lib/ext2fs/ext2_err.et.in
@@ -473,4 +473,7 @@ ec EXT2_ET_UNKNOWN_CSUM,
ec EXT2_ET_MMP_CSUM_INVALID,
"MMP block checksum does not match MMP block"
+ec EXT2_ET_FILE_EXISTS,
+ "Ext2 file already exists"
+
end
diff --git a/lib/ext2fs/symlink.c b/lib/ext2fs/symlink.c
new file mode 100644
index 00000000..da6e3a8a
--- /dev/null
+++ b/lib/ext2fs/symlink.c
@@ -0,0 +1,159 @@
+/*
+ * symlink.c --- make a symlink in the filesystem, based on mkdir.c
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ * All Rights Reserved.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Library
+ * General Public License, version 2.
+ * %End-Header%
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+errcode_t ext2fs_symlink(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t ino,
+ const char *name, char *target)
+{
+ ext2_extent_handle_t handle;
+ errcode_t retval;
+ struct ext2_inode inode;
+ ext2_ino_t scratch_ino;
+ blk64_t blk;
+ int fastlink;
+ int target_len;
+ char *block_buf = 0;
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ /* The Linux kernel doesn't allow for links longer than a block */
+ target_len = strlen(target);
+ if (target_len > fs->blocksize) {
+ retval = EXT2_ET_INVALID_ARGUMENT;
+ goto cleanup;
+ }
+
+ /*
+ * Allocate a data block for slow links
+ */
+ fastlink = (target_len < sizeof(inode.i_block));
+ if (!fastlink) {
+ retval = ext2fs_new_block2(fs, 0, 0, &blk);
+ if (retval)
+ goto cleanup;
+ retval = ext2fs_get_mem(fs->blocksize, &block_buf);
+ if (retval)
+ goto cleanup;
+ }
+
+ /*
+ * Allocate an inode, if necessary
+ */
+ if (!ino) {
+ retval = ext2fs_new_inode(fs, parent, LINUX_S_IFLNK | 0755,
+ 0, &ino);
+ if (retval)
+ goto cleanup;
+ }
+
+ /*
+ * Create the inode structure....
+ */
+ memset(&inode, 0, sizeof(struct ext2_inode));
+ inode.i_mode = LINUX_S_IFLNK | 0777;
+ inode.i_uid = inode.i_gid = 0;
+ ext2fs_iblk_set(fs, &inode, fastlink ? 0 : 1);
+ inode.i_links_count = 1;
+ inode.i_size = target_len;
+ /* The time fields are set by ext2fs_write_new_inode() */
+
+ if (fastlink) {
+ /* Fast symlinks, target stored in inode */
+ strcpy((char *)&inode.i_block, target);
+ } else {
+ /* Slow symlinks, target stored in the first block */
+ memset(block_buf, 0, fs->blocksize);
+ strcpy(block_buf, target);
+ if (fs->super->s_feature_incompat &
+ EXT3_FEATURE_INCOMPAT_EXTENTS) {
+ /*
+ * The extent bmap is setup after the inode and block
+ * have been written out below.
+ */
+ inode.i_flags |= EXT4_EXTENTS_FL;
+ } else {
+ inode.i_block[0] = blk;
+ }
+ }
+
+ /*
+ * Write out the inode and inode data block. The inode generation
+ * number is assigned by write_new_inode, which means that the
+ * operations using ino must come after it.
+ */
+ retval = ext2fs_write_new_inode(fs, ino, &inode);
+ if (retval)
+ goto cleanup;
+
+ if (!fastlink) {
+ retval = io_channel_write_blk(fs->io, blk, 1, block_buf);
+ if (retval)
+ goto cleanup;
+
+ if (fs->super->s_feature_incompat &
+ EXT3_FEATURE_INCOMPAT_EXTENTS) {
+ retval = ext2fs_extent_open2(fs, ino, &inode, &handle);
+ if (retval)
+ goto cleanup;
+ retval = ext2fs_extent_set_bmap(handle, 0, blk, 0);
+ ext2fs_extent_free(handle);
+ if (retval)
+ goto cleanup;
+ }
+ }
+
+ /*
+ * Link the symlink into the filesystem hierarchy
+ */
+ if (name) {
+ retval = ext2fs_lookup(fs, parent, name, strlen(name), 0,
+ &scratch_ino);
+ if (!retval) {
+ retval = EXT2_ET_FILE_EXISTS;
+ goto cleanup;
+ }
+ if (retval != EXT2_ET_FILE_NOT_FOUND)
+ goto cleanup;
+ retval = ext2fs_link(fs, parent, name, ino, EXT2_FT_SYMLINK);
+ if (retval)
+ goto cleanup;
+ }
+
+ /*
+ * Update accounting....
+ */
+ if (!fastlink)
+ ext2fs_block_alloc_stats2(fs, blk, +1);
+ ext2fs_inode_alloc_stats2(fs, ino, +1, 0);
+
+cleanup:
+ if (block_buf)
+ ext2fs_free_mem(&block_buf);
+ return retval;
+}