[Libguestfs] [PATCH supermin] ext2: Create symlinks properly (RHBZ#1470157).

Richard W.M. Jones rjones at redhat.com
Wed Jul 12 16:39:49 UTC 2017


The ext2 filesystem on disk format has two ways to store symlinks.
For symlinks >= 60 bytes in length, they are stored as files
(so-called "slow symlinks").  For shorter symlinks the symlink is
stored in the inode ("fast symlinks").

Previously we only created slow symlinks even if there are shorter
than 60 bytes.  This didn't matter until recently, when a change went
upstream which assumes that symlinks shorter than 60 bytes are always
stored in the inode, thus breaking the filesystems that we created
before:

https://bugzilla.redhat.com/show_bug.cgi?id=1470157#c4

This changes the code to use the ext2fs_symlink function instead which
creates fast and slow symlinks properly.

This fix is required if you use supermin with any Linux kernel >= 4.13.

The actual fix is two lines replacing ext2_write_file with a simpler
call to ext2fs_symlink.  The majority of this fix is removing the
ext2_write_file function which is no longer referenced after that
change.

Thanks: Eric Sandeen
---
 src/ext2fs-c.c | 50 +++++---------------------------------------------
 1 file changed, 5 insertions(+), 45 deletions(-)

diff --git a/src/ext2fs-c.c b/src/ext2fs-c.c
index 2743da7..0b3b29e 100644
--- a/src/ext2fs-c.c
+++ b/src/ext2fs-c.c
@@ -191,7 +191,6 @@ supermin_ext2fs_read_bitmaps (value fsv)
 
 static void ext2_mkdir (ext2_filsys fs, ext2_ino_t dir_ino, const char *dirname, const char *basename, mode_t mode, uid_t uid, gid_t gid, time_t ctime, time_t atime, time_t mtime);
 static void ext2_empty_inode (ext2_filsys fs, ext2_ino_t dir_ino, const char *dirname, const char *basename, mode_t mode, uid_t uid, gid_t gid, time_t ctime, time_t atime, time_t mtime, int major, int minor, int dir_ft, ext2_ino_t *ino_ret);
-static void ext2_write_file (ext2_filsys fs, ext2_ino_t ino, const char *buf, size_t size, const char *filename);
 static void ext2_write_host_file (ext2_filsys fs, ext2_ino_t ino, const char *src, const char *filename);
 static void ext2_link (ext2_filsys fs, ext2_ino_t dir_ino, const char *basename, ext2_ino_t ino, int dir_ft);
 static void ext2_clean_path (ext2_filsys fs, ext2_ino_t dir_ino, const char *dirname, const char *basename, int isdir);
@@ -468,49 +467,9 @@ ext2_empty_inode (ext2_filsys fs,
     *ino_ret = ino;
 }
 
-/* You must create the file first with ext2_empty_inode. */
-static void
-ext2_write_file (ext2_filsys fs,
-                 ext2_ino_t ino, const char *buf, size_t size,
-                 const char *filename)
-{
-  errcode_t err;
-  ext2_file_t file;
-  err = ext2fs_file_open2 (fs, ino, NULL, EXT2_FILE_WRITE, &file);
-  if (err != 0)
-    ext2_error_to_exception ("ext2fs_file_open2", err, filename);
-
-  /* ext2fs_file_write cannot deal with partial writes.  You have
-   * to write the entire file in a single call.
-   */
-  unsigned int written;
-  err = ext2fs_file_write (file, buf, size, &written);
-  if (err != 0)
-    ext2_error_to_exception ("ext2fs_file_write", err, filename);
-  if ((size_t) written != size)
-    caml_failwith ("ext2fs_file_write: file size != bytes written");
-
-  err = ext2fs_file_flush (file);
-  if (err != 0)
-    ext2_error_to_exception ("ext2fs_file_flush", err, filename);
-  err = ext2fs_file_close (file);
-  if (err != 0)
-    ext2_error_to_exception ("ext2fs_file_close", err, filename);
-
-  /* Update the true size in the inode. */
-  struct ext2_inode inode;
-  err = ext2fs_read_inode (fs, ino, &inode);
-  if (err != 0)
-    ext2_error_to_exception ("ext2fs_read_inode", err, filename);
-  inode.i_size = size;
-  err = ext2fs_write_inode (fs, ino, &inode);
-  if (err != 0)
-    ext2_error_to_exception ("ext2fs_write_inode", err, filename);
-}
-
-/* Same as ext2_write_file, but it copies the file contents from the
- * host.  You must create the file first with ext2_empty_inode, and
- * the host file must be a regular file.
+/* Copies the file contents from the host.  You must create the file
+ * first with ext2_empty_inode, and the host file must be a regular
+ * file.
  */
 static void
 ext2_write_host_file (ext2_filsys fs,
@@ -833,7 +792,8 @@ ext2_copy_file (struct ext2_data *data, const char *src, const char *dest)
     ssize_t r = readlink (src, buf, sizeof buf);
     if (r == -1)
       unix_error (errno, (char *) "readlink", caml_copy_string (src));
-    ext2_write_file (data->fs, ino, buf, r, dest);
+    buf[r] = '\0';
+    ext2fs_symlink (data->fs, dir_ino, ino, dest, buf);
   }
   /* Create directory. */
   else if (S_ISDIR (statbuf.st_mode))
-- 
2.13.1




More information about the Libguestfs mailing list