[mojosetup] Simple patches for handling hardlinks and GNU ././@LongLink entries

Alexey Dokuchaev danfe at nsu.ru
Wed Dec 1 07:17:47 EST 2010


I found that MojoSsetup tar backend lacks support for hard links and
long names (GNU tar extension).  Attached as two quick patches I've come
up with.  Then can probably be improved, but seem to work.  Only tested
on Unix.

./danfe
-------------- next part --------------
Index: scripts/mojosetup_mainline.lua
===================================================================
--- scripts/mojosetup_mainline.lua	(revision 3289)
+++ scripts/mojosetup_mainline.lua	(revision 3290)
@@ -471,6 +471,18 @@
 end
 
 
+local function install_hardlink(dest, lndest, manifestkey)
+    if not MojoSetup.platform.hardlink(dest, lndest) then
+        MojoSetup.logerror("Failed to create hardlink '" .. dest .. "'")
+        MojoSetup.fatal(_("Hardlink creation failed!"))
+    end
+
+    -- !!! FIXME: no checksum will be recorded (is it needed?)
+    manifest_add(MojoSetup.manifest, dest, manifestkey, "file", perms, nil, nil)
+    MojoSetup.loginfo("Created hardlink '" .. dest .. "' -> '" .. lndest .. "'")
+end
+
+
 -- !!! FIXME: we should probably pump the GUI queue here, in case there are
 -- !!! FIXME:  thousands of dirs in a row or something.
 local function install_directory(dest, perms, manifestkey)
@@ -566,9 +578,11 @@
         install_directory(dest, perms, manifestkey)
     elseif ent.type == "symlink" then
         install_symlink(dest, ent.linkdest, manifestkey)
+    elseif ent.type == "hardlink" then
+        install_hardlink(dest, MojoSetup.destination .. "/" .. ent.linkdest, manifestkey)
     else  -- !!! FIXME: device nodes, etc...
         -- !!! FIXME: should this be fatal?
-        MojoSetup.fatal(_("Unknown file type in archive"))
+        MojoSetup.fatal(_("Unknown file type in archive") .. ": " .. ent.type)
     end
 end
 
Index: archive_tar.c
===================================================================
--- archive_tar.c	(revision 3289)
+++ archive_tar.c	(revision 3290)
@@ -262,9 +262,11 @@
         ar->prevEnum.type = MOJOARCHIVE_ENTRY_FILE;
     else if (type == TAR_TYPE_DIRECTORY)
         ar->prevEnum.type = MOJOARCHIVE_ENTRY_DIR;
-    else if (type == TAR_TYPE_SYMLINK)
+    else if (type == TAR_TYPE_SYMLINK || type == TAR_TYPE_HARDLINK)
     {
-        ar->prevEnum.type = MOJOARCHIVE_ENTRY_SYMLINK;
+        // ugly assignment of type below allows to unify both symlink and
+        // hardlink handling under one ``if (...) { ... }'' clause
+        ar->prevEnum.type = TAR_TYPE_SYMLINK - type + MOJOARCHIVE_ENTRY_SYMLINK;
         memcpy(scratch, &block[TAR_LINKNAME], TAR_LINKNAMELEN);
         scratch[TAR_LINKNAMELEN] = '\0';  // just in case.
         ar->prevEnum.linkdest = xstrdup((const char *) scratch);
Index: platform_unix.c
===================================================================
--- platform_unix.c	(revision 3289)
+++ platform_unix.c	(revision 3290)
@@ -565,6 +565,12 @@
 } // MojoPlatform_symlink
 
 
+boolean MojoPlatform_hardlink(const char *src, const char *dst)
+{
+    return (link(dst, src) == 0);
+} // MojoPlatform_hardlink
+
+
 boolean MojoPlatform_mkdir(const char *path, uint16 perms)
 {
     // !!! FIXME: error if already exists?
Index: platform.h
===================================================================
--- platform.h	(revision 3289)
+++ platform.h	(revision 3290)
@@ -49,6 +49,11 @@
 //  syscall! Returns true if link was created, false otherwise.
 boolean MojoPlatform_symlink(const char *src, const char *dst);
 
+// Create a hardlink in the physical filesystem. (src) is the NAME OF THE LINK
+//  and (dst) is WHAT IT POINTS TO. This is backwards from the unix link()
+//  syscall! Returns true if link was created, false otherwise.
+boolean MojoPlatform_hardlink(const char *src, const char *dst);
+
 // Read the destination from symlink (linkname). Returns a pointer
 //  allocated with xmalloc() containing the linkdest on success, and NULL
 //  on failure. The caller is responsible for freeing the returned pointer!
Index: fileio.h
===================================================================
--- fileio.h	(revision 3289)
+++ fileio.h	(revision 3290)
@@ -55,6 +55,7 @@
     MOJOARCHIVE_ENTRY_FILE,
     MOJOARCHIVE_ENTRY_DIR,
     MOJOARCHIVE_ENTRY_SYMLINK,
+    MOJOARCHIVE_ENTRY_HARDLINK,
 } MojoArchiveEntryType;
 
 // Abstract archive interface. Archives, directories, etc.
Index: lua_glue.c
===================================================================
--- lua_glue.c	(revision 3289)
+++ lua_glue.c	(revision 3290)
@@ -989,6 +989,8 @@
             typestr = "dir";
         else if (entinfo->type == MOJOARCHIVE_ENTRY_SYMLINK)
             typestr = "symlink";
+        else if (entinfo->type == MOJOARCHIVE_ENTRY_HARDLINK)
+            typestr = "hardlink";
         else
             typestr = "unknown";
 
@@ -1069,6 +1071,14 @@
 } // luahook_platform_symlink
 
 
+static int luahook_platform_hardlink(lua_State *L)
+{
+    const char *src = luaL_checkstring(L, 1);
+    const char *dst = luaL_checkstring(L, 2);
+    return retvalBoolean(L, MojoPlatform_hardlink(src, dst));
+} // luahook_platform_hardlink
+
+
 static int luahook_platform_mkdir(lua_State *L)
 {
     const int argc = lua_gettop(L);
@@ -1830,6 +1840,7 @@
             set_cfunc(luaState, luahook_platform_issymlink, "issymlink");
             set_cfunc(luaState, luahook_platform_isfile, "isfile");
             set_cfunc(luaState, luahook_platform_symlink, "symlink");
+            set_cfunc(luaState, luahook_platform_hardlink, "hardlink");
             set_cfunc(luaState, luahook_platform_mkdir, "mkdir");
             set_cfunc(luaState, luahook_platform_installdesktopmenuitem, "installdesktopmenuitem");
             set_cfunc(luaState, luahook_platform_uninstalldesktopmenuitem, "uninstalldesktopmenuitem");
-------------- next part --------------
Index: archive_tar.c
===================================================================
--- archive_tar.c	(revision 3299)
+++ archive_tar.c	(revision 3303)
@@ -157,6 +157,8 @@
 #define TAR_TYPE_BLOCKDEV '4'
 #define TAR_TYPE_DIRECTORY '5'
 #define TAR_TYPE_FIFO '6'
+#define TAR_TYPE_LONGLINK 'K'
+#define TAR_TYPE_LONGNAME 'L'
 
 static boolean is_ustar(const uint8 *block)
 {
@@ -199,9 +201,10 @@
     size_t fnamelen = 0;
     int type = 0;
 
+    MojoArchive_resetEntry(&ar->prevEnum);
+longlink:
     memset(scratch, '\0', sizeof (scratch));
 
-    MojoArchive_resetEntry(&ar->prevEnum);
     if (info->input != NULL)
         fatal("BUG: tar entry still open on new enumeration");
 
@@ -217,6 +220,7 @@
             return NULL;  // !!! FIXME: fatal() ?
         zeroes = (memcmp(block, scratch, sizeof (block)) == 0);
     } // while
+    zeroes = true;  // allow reentrancy for ././@LongLink handling
 
     // !!! FIXME We should probably check the checksum.
 
@@ -229,19 +233,25 @@
     if (ar->prevEnum.filesize % 512)
         info->nextEnumPos += 512 - (ar->prevEnum.filesize % 512);
 
-    // We count on (scratch) being zeroed out here!
-    // prefix of filename is at the end for legacy compat.
-    if (ustar)
-        memcpy(scratch, &block[TAR_FNAMEPRE], TAR_FNAMEPRELEN);
-    fnamelen = strlen((const char *) scratch);
-    memcpy(&scratch[fnamelen], &block[TAR_FNAME], TAR_FNAMELEN);
-    fnamelen += strlen((const char *) &scratch[fnamelen]);
-
+    // Non-zero fnamelen means we're dealing with long name, which should be
+    // already set on previous interation
     if (fnamelen == 0)
-        return NULL;   // corrupt file.  !!! FIXME: fatal() ?
+    {
+        // We count on (scratch) being zeroed out here!
+        // prefix of filename is at the end for legacy compat.
+        if (ustar)
+            memcpy(scratch, &block[TAR_FNAMEPRE], TAR_FNAMEPRELEN);
+        fnamelen = strlen((const char *) scratch);
+        memcpy(&scratch[fnamelen], &block[TAR_FNAME], TAR_FNAMELEN);
+        fnamelen += strlen((const char *) &scratch[fnamelen]);
 
-    ar->prevEnum.filename = xstrdup((const char *) scratch);
+        if (fnamelen == 0)
+            return NULL;   // corrupt file.  !!! FIXME: fatal() ?
+    }
 
+    if (ar->prevEnum.filename == NULL)
+        ar->prevEnum.filename = xstrdup((const char *) scratch);
+
     type = block[TAR_TYPE];
     if (type == 0)  // some archivers do the file type as 0 instead of '0'.
         type = TAR_TYPE_FILE;
@@ -269,7 +279,23 @@
         ar->prevEnum.type = TAR_TYPE_SYMLINK - type + MOJOARCHIVE_ENTRY_SYMLINK;
         memcpy(scratch, &block[TAR_LINKNAME], TAR_LINKNAMELEN);
         scratch[TAR_LINKNAMELEN] = '\0';  // just in case.
-        ar->prevEnum.linkdest = xstrdup((const char *) scratch);
+        if (ar->prevEnum.linkdest == NULL)
+            ar->prevEnum.linkdest = xstrdup((const char *) scratch);
+    }
+    else if (type == TAR_TYPE_LONGLINK || type == TAR_TYPE_LONGNAME)
+    {
+        // Such a header (with name `././@LongLink') is GNU way of storing
+        // file long file names (>=100 chars); contents (data) will be the
+        // name of the *next* file or link in the archive
+        if (!ar->io->seek(ar->io, info->curFileStart))
+            return NULL;
+        if (ar->io->read(ar->io, block, sizeof (block)) != sizeof (block))
+            return NULL;  // !!! FIXME: fatal() ?
+        if (type == TAR_TYPE_LONGNAME)
+            ar->prevEnum.filename = xstrdup((const char *) block);
+        else
+            ar->prevEnum.linkdest = xstrdup((const char *) block);
+        goto longlink;
     } // else if
 
     return &ar->prevEnum;


More information about the mojosetup mailing list