relocated binaries and paths not found (AN INteresting problem)

SailSync ownCloud makes use of the official owncloudcmd binary to perform file synchronisation. SailSync ownCloud was designed from the beginning to use owncloudcmd binary because it ensured that synchronisation would work correctly (as expected according to the official solution).

About the Build Environment

The build environment for SailfishOS is provided in a virtual machine, which is run via VirtualBox.

The virtual machine itself hosts a scratchbox2 environment for cross-compiling the software for different platforms (e.g. x86 and ARM).

The buildroot

The buildroot is the location where application files are placed (either copied or built to) prior to packaging as an RPM. The buildroot for SailfishOS projects is located under /home/deploy/installroot/ . As part of the packaging process the build tools will search for, and aim to relocate paths referencing the buildroot (i.e. /home/deploy/installroot/usr/share/qml/ will become /usr/share/qml/) . If the build tools are unable to handle the relocation, they will warn the developer that the buildroot path has been found in a build artifact (e.g. a compiled binary, or Python file references the buildroot path).

Relocating the binary

I built the owncloudcmd binary under the buildroot. In the past with my Python-only projects (e.g. AirSail Transfer) I could remove references of the build root by performing a simple find and replace (AirSail Transfer RPM spec, Line 48):

find %{buildroot} -type f -exec sed -i "s|%{buildroot}||g" "{}" \;

However, because owncloudcmd is a binary file, more care needs to be taken so as not to change the size of the binary. My solution (which can be found in harbour-io.edin.project.sailsync-owncloud.spec) was as follows (notice that the path was padded with 24 NULL bytes — the length of the string “/home/deploy/installroot” — to ensure that the binary’s size did not change).

# /home/deploy/installroot/usr/share/harbour-io.edin.projects.sailsync-owncloud/([^\x0]*)
# TO
# /usr/share/harbour-io.edin.projects.sailsync-owncloud/$1\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0
sed -i "s|%{buildroot}/\?\(%{_datadir}/\?%{name}/\?[^\x0]*\)|\1\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0|g" %{buildroot}/%{_datadir}/%{name}/lib/owncloud/lib/[0-9].[0-9].[0-9]

The issue

The compiled owncloudcmd program is run manually on a SailfishOS device, but it fails with the following error:

[F] main:532 - Cannot load system exclude list or list supplied via --exclude

This is the block of code that is executed before failing (cmd.cpp Line 531):

 if (!engine.excludedFiles().reloadExcludeFiles()) {
        qFatal("Cannot load system exclude list or list supplied via --exclude");
        return EXIT_FAILURE;

The debugging process leads to the system exclude file path, generated in ConfigFile::excludeFileFromSystem . The system exclude file path is generated by concatenating:

  • system configuration directory (SYSCONFDIR, which is defined as /usr/share/harbour-io.edin.projects.sailsync-owncloud/lib/owncloud/etc/)
  • the app name (Theme::instance()->appName(), which returns ownCloud)
  • the exclude file (exclFile, hard-coded to the string “sync-exclude.lst“)

The final version of the system exclude file path should be: /usr/share/harbour-io.edin.projects.sailsync-owncloud/lib/owncloud/etc/ownCloud/sync-exclude.lst (a length of 96 characters)

Debugging the system exclude file path string proves this not to be the case:

# system exclude file absolute path (note: no more characters are printed after the first NULL bytes is encountered)
# system exclude file absolute path length

In this particular instance it appears that the relocated path defined in SYSCONFIG (/usr/share/harbour-io.edin.projects.sailsync-owncloud/$1\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0, totalling 95 characters) was being concatenated with the application name (ownCloud, 8 characters) and exclude file name (“sync-exclude.lst“, 16 characters) and a path delimiter (/, 1 character) to produce a path of total length of 120.

The fix

Building paths by concatenating strings is generally discouraged. For example in Python there is os.path.join and more recently Pathlib to help build paths.

The fix for this issue is to build the path with the relevant helper functions instead of concatenating strings together (link to patch):

-    fi.setFile(QString(SYSCONFDIR "/" + Theme::instance()->appName()), exclFile);
+    QString sysconfdir = QString(SYSCONFDIR);
+    fi.setFile(QDir(sysconfdir).filePath(Theme::instance()->appName()), exclFile);

Applying the above patch resolved the “Cannot load system exclude list” failure when owncloudcmd is run. The owncloudcmd could then be run successfully.