Skip to content

Commit

Permalink
Add basic SquashFS support.
Browse files Browse the repository at this point in the history
Only listing/extracting is implemented.
  • Loading branch information
OscarL committed Apr 6, 2024
1 parent cd494ad commit 9696b1a
Show file tree
Hide file tree
Showing 10 changed files with 467 additions and 1 deletion.
15 changes: 15 additions & 0 deletions Docs/UserGuide/addons/SquashFS.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

==============
SquashFS AddOn
==============


The squashfs archiver gives you the following options:

**Process attributes**

Allows adding/extracting of extended file attributes ("xattr").
Its generally best to leave this option turned ON.

For information on saving these settings to the archive or as defaults
read the :ref:`ArchiveWindow:\< archiver \>` menu information.
1 change: 1 addition & 0 deletions Docs/UserGuide/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ Welcome to the Beezer User Guide
Hpkg <addons/Hpkg>
Lha <addons/Lha>
Rar <addons/Rar>
SquashFS <addons/SquashFS>
Xz <addons/Xz>
Zip <addons/Zip>
Zstd <addons/Zstd>
Expand Down
3 changes: 2 additions & 1 deletion Source/Beezer/Beezer.rdef
Original file line number Diff line number Diff line change
Expand Up @@ -1631,7 +1631,8 @@ resource file_types message {
"types" = "application/x-xz",
"types" = "application/x-7zip-compressed",
"types" = "application/x-7z-compressed",
"types" = "application/x-arj-compressed"
"types" = "application/x-arj-compressed",
"types" = "application/x-squashfs-image"
};

resource(3038, "Img:Background") #'PNG ' array {
Expand Down
4 changes: 4 additions & 0 deletions Source/Beezer/RuleDefaults.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ const char* kDefaultRules = "\
#application/x-zstd=.zstd\n\
\n\
#application/x-vnd.haiku-package=.hpkg\n\
\n\
#application/x-squashfs-image=.sfs\n\
#application/x-squashfs-image=.sqfs\n\
#application/x-squashfs-image=.squashfs\n\
";

#endif // _RULE_DEFAULTS_H_
1 change: 1 addition & 0 deletions Source/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ add_subdirectory(BZipArchiver)
add_subdirectory(GZipArchiver)
add_subdirectory(LhaArchiver)
add_subdirectory(RarArchiver)
add_subdirectory(SquashFSArchiver)
add_subdirectory(TarArchiver)
add_subdirectory(XzArchiver)
add_subdirectory(z7Archiver)
Expand Down
13 changes: 13 additions & 0 deletions Source/SquashFSArchiver/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@

haiku_add_addon(ark_squashfs SquashFSArchiver.cpp SquashFSArchiver.rdef)

#target_link_libraries(ark_squashfs)

set_property(TARGET ark_squashfs PROPERTY LIBRARY_OUTPUT_DIRECTORY ${BEEZER_BUILD_ADDONS_DIR})

if(HAIKU_ENABLE_I18N)
set("ark_squashfs-APP_MIME_SIG" "x-vnd.BeezerAddOn-SquashFSArchiver")
set("ark_squashfs-LOCALES" "en")
target_link_libraries(ark_squashfs "localestub")
haiku_add_i18n(ark_squashfs)
endif()
311 changes: 311 additions & 0 deletions Source/SquashFSArchiver/SquashFSArchiver.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,311 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2002 Ramshankar (aka Teknomancer).
// Copyright (c) 2011 Chris Roberts.
// Copyright (c) 2024 Oscar Lesta.
// All rights reserved.

#include "SquashFSArchiver.h"
#include "AppUtils.h"
#include "ArchiveEntry.h"
#include "KeyedMenuItem.h"

#include <NodeInfo.h>
#include <Messenger.h>
#include <MenuItem.h>

#ifdef HAIKU_ENABLE_I18N
#include <Catalog.h>

#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "SquashFSArchiver"
#else
#define B_TRANSLATE(x) x
#define B_TRANSLATE_MARK(x) x
#define B_TRANSLATE_NOCOLLECT(x) x
#endif

#include <cstdlib> // gcc2


// keep track of our custom options/menuitems
static const char* kProcessAttrs = B_TRANSLATE_MARK("Process attributes");


Archiver* load_archiver(BMessage* metaDataMsg)
{
return new SquashFSArchiver(metaDataMsg);
}


// #pragma mark -


SquashFSArchiver::SquashFSArchiver(BMessage* metaDataMsg)
: Archiver(metaDataMsg)
{
m_error = BZR_BINARY_MISSING;
if (GetBinaryPath(m_unsquashfsPath, "unsquashfs") == true)
m_error = BZR_DONE;
}


void SquashFSArchiver::BuildMenu(BMessage& message)
{
m_settingsMenu = new BMenu(m_typeStr);
m_settingsMenu->AddItem(
new KeyedMenuItem("bzr:ProcessAttrs", B_TRANSLATE_NOCOLLECT(kProcessAttrs),
message, true, new BMessage(BZR_MENUITEM_SELECTED)));
}


BList SquashFSArchiver::HiddenColumns(BList const& columns) const
{
// Indices are: 0-name 1-size 2-packed 3-ratio 4-path 5-date 6-method 7-crc
BList hiddenColumns(columns);
hiddenColumns.RemoveItems(0, 2); // Remove name and size

// Now list has 0-packed 1-ratio 2-path 3-date 4-method 5-crc
hiddenColumns.RemoveItems(2, 2); // Remove path and date

// Now list has 0-packed 1-ratio 2-method 3-crc <-- these columns are to be hidden
return hiddenColumns;
}


// #pragma mark -


status_t SquashFSArchiver::Open(entry_ref* ref, BMessage* /*fileList*/)
{
m_archiveRef = *ref;
m_archivePath.SetTo(ref);

m_pipeMgr.FlushArgs();
m_pipeMgr << m_unsquashfsPath << "-d" << "" << "-llc" << m_archivePath.Path();

FILE* out, *err;
int outdes[2], errdes[2];
thread_id tid = m_pipeMgr.Pipe(outdes, errdes);

if (tid == B_ERROR || tid == B_NO_MEMORY)
return B_ERROR;

resume_thread(tid);

close(errdes[1]);
close(outdes[1]);

out = fdopen(outdes[0], "r");
status_t exitCode = ReadOpen(out);

close(outdes[0]);
fclose(out);

err = fdopen(errdes[0], "r");
exitCode = Archiver::ReadErrStream(err, NULL);
close(errdes[0]);
fclose(err);

return exitCode;
}


status_t SquashFSArchiver::ReadOpen(FILE* fp)
{
char lineString[B_PATH_NAME_LENGTH + 512];
char permStr[15];
char ownerStr[100];
char sizeStr[15];
char dayStr[5];
char monthStr[5];
char yearStr[8];
char hourStr[5];
char minuteStr[5];
char pathStr[2 * B_PATH_NAME_LENGTH + 10];
uint16 const len = sizeof(lineString);

while (fgets(lineString, len, fp))
{
lineString[strlen(lineString) - 1] = '\0';

sscanf(lineString,
"%[^ ] %[^ ] %[0-9] %[0-9]-%[0-9]-%[0-9] %[0-9]:%[0-9] %[^\n]",
permStr, ownerStr, sizeStr, yearStr, monthStr, dayStr, hourStr, minuteStr, pathStr);

struct tm timeStruct; time_t timeValue;
MakeTime(&timeStruct, &timeValue, dayStr, monthStr, yearStr, hourStr, minuteStr, "00");

BString pathString = pathStr;
pathString.RemoveFirst("/"); // avoids having an empty first-level dir.

// Handle linked files/folders
if (permStr[0] == 'l')
{
BString fullPath = pathString;
uint16 foundIndex = fullPath.FindLast(" -> ");
fullPath.Remove(foundIndex, fullPath.Length() - foundIndex);
pathString = fullPath.String();
}

// Check for emtpy dirs:
bool isDir = permStr[0] == 'd';

if (isDir)
pathString.Append("/"); // Without this Beezer doesn't shows the entry for some reason.

m_entriesList.AddItem(
new ArchiveEntry(isDir, pathString.String(), sizeStr, "-", timeValue, "-", "-"));
}

return BZR_DONE;
}


status_t SquashFSArchiver::Extract(entry_ref* refToDir, BMessage* message, BMessenger* progress,
volatile bool* cancel)
{
if (progress)
{
BEntry dirEntry(refToDir);
if (dirEntry.Exists() == false || dirEntry.IsDirectory() == false)
return BZR_EXTRACT_DIR_INIT_ERROR;
}

BPath dirPath(refToDir);
BEntry archiveEntry(&m_archiveRef, true);
if (archiveEntry.Exists() == false)
return BZR_ARCHIVE_PATH_INIT_ERROR;

int32 count = 0L;
if (message)
{
uint32 type;
message->GetInfo(kPath, &type, &count);
if (type != B_STRING_TYPE)
return BZR_UNKNOWN;
}

m_pipeMgr.FlushArgs();

// Arguments we use here:
// quiet, no percentage, print info, overwrite files (needed if the dirPath already exists).
m_pipeMgr << m_unsquashfsPath << "-q" << "-n" << "-i" << "-f" << "-d" << dirPath.Path();

if (m_settingsMenu->FindItem(B_TRANSLATE_NOCOLLECT(kProcessAttrs))->IsMarked() == false)
m_pipeMgr << "-no-xattrs";

m_pipeMgr << m_archivePath.Path();

for (int32 i = 0; i < count; i ++)
{
const char* pathString = NULL;
if (message->FindString(kPath, i, &pathString) == B_OK)
m_pipeMgr << SupressWildcards(pathString);
}

int outdes[2], errdes[2];
thread_id tid = m_pipeMgr.Pipe(outdes, errdes);

if (tid == B_ERROR || tid == B_NO_MEMORY)
return B_ERROR;

if (progress)
resume_thread(tid);
else
{
status_t threadExitCode;
wait_for_thread(tid, &threadExitCode);
}

close(errdes[1]);
close(outdes[1]);

status_t exitCode = BZR_DONE;
if (progress)
{
FILE* outFile = fdopen(outdes[0], "r");
exitCode = ReadExtract(outFile, progress, cancel);
fclose(outFile);
}

close(outdes[0]);
close(errdes[0]);

// Send signal to quit archiver only AFTER pipes are closed
if (exitCode == BZR_CANCEL_ARCHIVER)
TerminateThread(tid);

m_pipeMgr.FlushArgs();
return exitCode;
}


status_t SquashFSArchiver::ReadExtract(FILE* fp, BMessenger* progress, volatile bool* cancel)
{
// Reads output while extracting files and updates progress window (thru messenger)
char lineString[999];

// Prepare message to update the progress bar
BMessage updateMessage(BZR_UPDATE_PROGRESS), reply('DUMB');
updateMessage.AddFloat("delta", 1.0f);

while (fgets(lineString, 998, fp))
{
if (cancel && *cancel == true)
return BZR_CANCEL_ARCHIVER;

int32 len = strlen(lineString);
lineString[--len] = '\0';
if (len >= 1 && lineString[len - 1] != '/')
{
updateMessage.RemoveName("text");
updateMessage.AddString("text", LeafFromPath(lineString));

progress->SendMessage(&updateMessage, &reply);
}
}

return BZR_DONE;
}


// #pragma mark -


status_t SquashFSArchiver::Test(char*& /*outputStr*/, BMessenger* /*progress*/, volatile bool* /*cancel*/)
{
return BZR_NOT_SUPPORTED;
}


status_t SquashFSArchiver::Add(bool /*createMode*/, const char* /*relativePath*/, BMessage* /*message*/,
BMessage* /*addedPaths*/, BMessenger* /*progress*/, volatile bool* /*cancel*/)
{
return BZR_NOT_SUPPORTED;
}


status_t SquashFSArchiver::Delete(char*& /*outputStr*/, BMessage* /*message*/, BMessenger* /*progress*/,
volatile bool* /*cancel*/)
{
return BZR_NOT_SUPPORTED;
}


status_t SquashFSArchiver::Create(BPath* /*archivePath*/, const char* /*relPath*/, BMessage* /*fileList*/,
BMessage* /*addedPaths*/, BMessenger* /*progress*/, volatile bool* /*cancel*/)
{
return BZR_NOT_SUPPORTED;
}


bool SquashFSArchiver::CanAddFiles() const
{
return false;
}


bool SquashFSArchiver::CanDeleteFiles() const
{
return false;
}
Loading

0 comments on commit 9696b1a

Please sign in to comment.