/libsquash

Portable, user-land SquashFS that can be easily linked and embedded within your application.

Primary LanguageCOtherNOASSERTION

Libsquash

Portable, user-land SquashFS that can be easily linked and embedded within your application.

Build status: Linux and Darwin Build status: Windows

Design

This project was derived from https://github.com/vasi/squashfuse with the following modifications.

  1. NO FUSE REQUIRED, so that it can be so portable that even systems without fuse could use it.
  2. Read data from the memory instead of from a file by passing a pointer to an array of bytes, which could be generated by an independently installed mksquashfs tool and loaded into memory in advance.
  3. Introduced virtual file descriptor (vfd) as a handle for follow-up libsquash operations. The vfd is generated via dup(0) so that it could live together with other ordinary file descriptors of the process.
  4. Added new API that mirror the calling style of common system calls; also added is a sample on how to use libsquash in an unobtrusive way by utilizing those API.
  5. Made it compile on 3 platforms simultanesly: Windows, macOS and Linux.
  6. Added CMake so that Xcode and Vistual Studio Projects could be easily generated.
  7. Added tests for both the old and new API of the library.
  8. Eliminate dependencies on <stdbool.h> so that Windows XP and Visual C++ 2010 could use this library.

Building

On most systems you could build the library using the following commands,

mkdir build
cd build
cmake ..
cmake --build .

Use cmake -DBUILD_TESTS=ON .. to build the tests in addition and use ctest --verbose to run them.

API

squash_stat(fs, path, buf)

Obtains information about the file pointed to by path of a SquashFS fs. The buf argument is a pointer to a stat structure as defined by <sys/stat.h> and into which information is placed concerning the file. Upon successful completion a value of 0 is returned. Otherwise, a value of -1 is returned and errno is set to the reason of the error.

squash_lstat(fs, path, buf)

Acts like squash_stat() except in the case where the named file is a symbolic link; squash_lstat() returns information about the link, while squash_stat() returns information about the file the link references.

squash_fstat(vfd, buf)

Obtains the same information as squash_stat() about an open file known by the virtual file descriptor vfd.

squash_open(fs, path)

Opens the file name specified by path of a SquashFS fs for reading. If successful, squash_open() returns a non-negative integer, termed a vfd(virtual file descriptor). It returns -1 on failure and sets errno to the reason of the error. The file pointer (used to mark the current position within the file) is set to the beginning of the file. The returned vfd should later be closed by squash_close().

squash_close(vfd)

Deletes a vfd(virtual file descriptor) from the per-process object reference table. Upon successful completion, a value of 0 is returned. Otherwise, a value of -1 is returned and errno is set to the reason of the error.

squash_read(vfd, buf, nbyte)

Attempts to read nbyte bytes of data from the object referenced by vfs into the buffer pointed to by buf, starting at a position given by the pointer associated with vfd (see squash_lseek), which is then incremented by the number of bytes actually read upon return. When successful it returns the number of bytes actually read and placed in the buffer; upon reading end-of-file, zero is returned; Otherwise, a value of -1 is returned and errno is set to the reason of the error.

squash_lseek(vfd, offset, whence)

Repositions the offset of vfs to the argument offset, according to the directive whence. If whence is SQUASH_SEEK_SET then the offset is set to offset bytes; if whence is SQUASH_SEEK_CUR, the offset is set to its current location plus offset bytes; if whence is SQUASH_SEEK_END, the offset is set to the size of the file and subsequent reads of the data return bytes of zeros. The argument fildes must be an open virtual file descriptor. Upon successful completion, it returns the resulting offset location as measured in bytes from the beginning of the file. Otherwise, a value of -1 is returned and errno is set to the reason of the error.

squash_readlink(fs, path, buf, bufsize)

Places the contents of the symbolic link path of a SquashFS fs in the buffer buf, which has size bufsize. It does not append a NUL character to buf. If it succeeds the call returns the count of characters placed in the buffer; otherwise -1 is returned and errno is set to the reason of the error.

squash_opendir(fs, filename)

Opens the directory named by filename of a SquashFS fs, associates a directory stream with it and returns a pointer to be used to identify the directory stream in subsequent operations. The pointer NULL is returned if filename cannot be accessed, or if it cannot allocate enough memory to hold the whole thing, and sets errno to the reason of the error. The returned resource should later be closed by squash_closedir().

squash_closedir(dirp)

Closes the named directory stream and frees the structure associated with the dirp pointer, returning 0 on success. On failure, -1 is returned and errno is set to the reason of the error.

squash_readdir(dirp)

Returns a pointer to the next directory entry. It returns NULL upon reaching the end of the directory or on error. In the event of an error, errno is set to the reason of the error.

squash_telldir(dirp)

Returns the current location associated with the named directory stream.

squash_seekdir(dirp, loc)

Sets the position of the next squash_readdir() operation on the directory stream. The new position reverts to the one associated with the directory stream when the squash_telldir() operation was performed.

squash_rewinddir(dirp)

Resets the position of the named directory stream to the beginning of the directory.

squash_dirfd(dirp)

Returns the integer Libsquash file descriptor associated with the named directory stream. On failure, -1 is returned and errno is set to the reason of the error.

squash_scandir(fs, dirname, namelist, select, compar)

Reads the directory dirname of a SquashFS fs and builds an array of pointers to directory entries using malloc. If successful it returns the number of entries in the array; otherwise -1 is returned and errno is set to the reason of the error. A pointer to the array of directory entries is stored in the location referenced by namelist (even if the number of entries is 0), which should later be freed via free() by freeing each pointer in the array and then the array itself. The select argument is a pointer to a user supplied subroutine which is called by scandir to select which entries are to be included in the array. The select routine is passed a pointer to a directory entry and should return a non-zero value if the directory entry is to be included in the array. If select is NULL, then all the directory entries will be included. The compar argument is a pointer to a user supplied subroutine which is passed to qsort to sort the completed array. If this pointer is NULL, then the array is not sorted.

squash_extract(fs, path, ext_name)

Extracts the file path from fs to a temporary file inside the temporary folder. Upon successful completion the path of the extracted temporary file is returned. Otherwise, a value of NULL is returned and errno is set to the reason of the error. The returned path is referenced by an internal cache and must not be freed.

To-do

  • Test Wide character directories and file names on Windows
  • Benchmark and tweet cache size and block size of libsquash
  • Organize core API's and freeze
  • Make public struct's opaque
  • Seperate headers into private headers and public headers
  • Make intercepting system calls a core functionality instead of in the sample/
  • Test under ARM architecture as well
  • Support Unicode paths
  • Embed mksquashfs logic instead of relying on outside tool
  • Intercept dynamically without compiling via the LD_PRELOAD trick

Acknowledgment

Thank you Dave Vasilevsky for the excellent work of squashfuse!

See Also

  • Node.js Packer: Packing your Node.js application into a single executable.
  • Ruby Packer: Packing your Ruby application into a single executable.