From 7b2675a2a1e66f714b5d863d97480ad43ccb92cc Mon Sep 17 00:00:00 2001 From: Christian Dywan Date: Sun, 17 Feb 2019 19:55:06 +0100 Subject: Load extensions from ZIP archives (#255) * Load extensions from ZIP archives * Use regular int64 offset type with read_data_block * Don't use new load_bytes{,async} method * Add libarchive VAPI to address bugs in older Vala releases * Add extension_scheme method, d'oh * Drop unused extract/ group bits from libarchive.vapi * Go back to using Archive.int64_t and depend on posix * Also get background scripts through resources * Use int64 type exclusively * Double-check that injected resources are valid --- .circleci/config.yml | 2 +- CMakeLists.txt | 4 +- README.md | 4 +- extensions/web-extensions.vala | 163 +++++++++++++--- vapi/libarchive.vapi | 430 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 568 insertions(+), 35 deletions(-) create mode 100644 vapi/libarchive.vapi diff --git a/.circleci/config.yml b/.circleci/config.yml index 0a625924..5f4ff343 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -9,7 +9,7 @@ jobs: name: Install dependencies command: | apt-get update - apt-get install -y cmake xvfb valac libwebkit2gtk-4.0-dev libsoup-gnome2.4-dev libgcr-3-dev libpeas-dev libsqlite3-dev libjson-glib-dev intltool libxml2-utils + apt-get install -y cmake xvfb valac libwebkit2gtk-4.0-dev libsoup-gnome2.4-dev libgcr-3-dev libpeas-dev libsqlite3-dev libjson-glib-dev libarchive-dev intltool libxml2-utils - run: name: Build command: | diff --git a/CMakeLists.txt b/CMakeLists.txt index c1884e8d..66a27d0c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,10 +55,12 @@ pkg_check_modules(DEPS_GTK REQUIRED gcr-ui-3>=2.32 libpeas-gtk-1.0 json-glib-1.0>=0.12 + libarchive ) -set(PKGS ${PKGS} gtk+-3.0 libsoup-2.4 gcr-ui-3 libpeas-gtk-1.0 json-glib-1.0) +set(PKGS ${PKGS} gtk+-3.0 libsoup-2.4 gcr-ui-3 libpeas-gtk-1.0 json-glib-1.0 posix) set(EXTRA_VAPIS ${CMAKE_SOURCE_DIR}/vapi/config.vapi + ${CMAKE_SOURCE_DIR}/vapi/libarchive.vapi ${CMAKE_SOURCE_DIR}/vapi/webkit2gtk-4.0.vapi ${CMAKE_SOURCE_DIR}/vapi/javascriptcoregtk-4.0.vapi) diff --git a/README.md b/README.md index 26a0b772..4bc785e2 100644 --- a/README.md +++ b/README.md @@ -69,11 +69,11 @@ You can opt-in for the [beta release on the Play Store](https://play.google.com/ Install dependencies on Astian OS, Ubuntu, Debian or other Debian-based distros: - sudo apt install cmake valac libwebkit2gtk-4.0-dev libgcr-3-dev libpeas-dev libsqlite3-dev libjson-glib-dev intltool libxml2-utils + sudo apt install cmake valac libwebkit2gtk-4.0-dev libgcr-3-dev libpeas-dev libsqlite3-dev libjson-glib-dev libarchive-dev intltool libxml2-utils Install dependencies on openSUSE: - sudo zypper in cmake vala gcc webkit2gtk3-devel libgcr-devel libpeas-devel sqlite3-devel json-glib-devel fdupes gettext-tools intltool libxml2-devel + sudo zypper in cmake vala gcc webkit2gtk3-devel libgcr-devel libpeas-devel sqlite3-devel json-glib-devel libarchive-devel fdupes gettext-tools intltool libxml2-devel Use CMake to build Midori: diff --git a/extensions/web-extensions.vala b/extensions/web-extensions.vala index e0501ae1..6a472733 100644 --- a/extensions/web-extensions.vala +++ b/extensions/web-extensions.vala @@ -11,7 +11,9 @@ namespace WebExtension { public class Extension : Object { + HashTable _files; public File file { get; protected set; } + public string name { get; set; } public string description { get; set; } public string? background_page { get; owned set; } @@ -23,6 +25,31 @@ namespace WebExtension { public Extension (File file) { Object (file: file, name: file.get_basename ()); } + + public void add_resource (string resource, Bytes data) { + if (_files == null) { + _files = new HashTable (str_hash, str_equal); + } + _files.insert (resource, data); + } + + public async Bytes get_resource (string resource) throws Error { + // Strip ./ or / prefix + string _resource = resource.has_prefix (".") ? resource.substring (1, -1) : resource; + _resource = _resource.has_prefix ("/") ? _resource.substring (1, -1) : _resource; + + if (_files != null) { + return _files.lookup (_resource); + } + var child = file.get_child (_resource); + if (child.query_exists ()) { + uint8[] data; + if (yield child.load_contents_async (null, out data, null)) { + return new Bytes (data); + } + } + throw new FileError.IO ("Failed to open '%s': Not found in %s".printf (resource, name)); + } } public class Action : Object { @@ -37,7 +64,7 @@ namespace WebExtension { public class ExtensionManager : Object { static ExtensionManager? _default = null; - HashTable extensions; + internal HashTable extensions; public delegate void ExtensionManagerForeachFunc (Extension extension); public void @foreach (ExtensionManagerForeachFunc func) { @@ -84,17 +111,50 @@ namespace WebExtension { string id = Checksum.compute_for_string (ChecksumType.MD5, file.get_path ()); var extension = extensions.lookup (id); if (extension == null) { + InputStream? stream = null; extension = new Extension (file); - // If we find a manifest, this is a web extension - var manifest_file = file.get_child ("manifest.json"); - if (!manifest_file.query_exists ()) { - continue; - } - try { + // Try reading from a ZIP archive ie. .crx (Chrome/ Opera/ Vivaldi), .nex (Opera) or .xpi (Firefox) + if (Regex.match_simple ("\\.(crx|nex|xpi)", file.get_basename (), + RegexCompileFlags.CASELESS, RegexMatchFlags.NOTEMPTY)) { + var archive = new Archive.Read (); + archive.support_format_zip (); + if (archive.open_filename (file.get_path (), 10240) == Archive.Result.OK) { + unowned Archive.Entry entry; + while (archive.next_header (out entry) == Archive.Result.OK) { + if (entry.pathname () == "manifest.json") { + uint8[] buffer; + int64 offset; + archive.read_data_block (out buffer, out offset); + stream = new MemoryInputStream.from_data (buffer, free); + } else { + uint8[] buffer; + int64 offset; + archive.read_data_block (out buffer, out offset); + extension.add_resource (entry.pathname (), new Bytes (buffer)); + } + } + + if (stream == null) { + throw new FileError.IO ("Failed to open '%s': no manifest.json".printf (file.get_path ())); + } + } else { + throw new FileError.IO ("Failed to open '%s': %s".printf (file.get_path (), archive.error_string ())); + } + } else { + // If we find a manifest, this is a web extension + var manifest_file = file.get_child ("manifest.json"); + if (manifest_file.query_exists ()) { + stream = new DataInputStream (yield manifest_file.read_async ()); + } else { + continue; + } + } + var json = new Json.Parser (); - yield json.load_from_stream_async (new DataInputStream (manifest_file.read ())); + yield json.load_from_stream_async (stream); + var manifest = json.get_root ().get_object (); if (manifest.has_member ("name")) { extension.name = manifest.get_string_member ("name"); @@ -151,17 +211,23 @@ namespace WebExtension { } foreach (var filename in extension.content_scripts) { - uint8[] script; - yield file.get_child (filename).load_contents_async (null, out script, null); - content.add_script (new WebKit.UserScript ((string)script, + var script = yield extension.get_resource (filename); + if (script == null) { + warning ("Failed to inject content script for '%s': %s", extension.name, filename); + continue; + } + content.add_script (new WebKit.UserScript ((string)(script.get_data ()), WebKit.UserContentInjectedFrames.TOP_FRAME, WebKit.UserScriptInjectionTime.END, null, null)); } foreach (var filename in extension.content_styles) { - uint8[] stylesheet; - yield file.get_child (filename).load_contents_async (null, out stylesheet, null); - content.add_style_sheet (new WebKit.UserStyleSheet ((string)stylesheet, + var stylesheet = yield extension.get_resource (filename); + if (stylesheet == null) { + warning ("Failed to inject content stylesheet for '%s': %s", extension.name, filename); + continue; + } + content.add_style_sheet (new WebKit.UserStyleSheet ((string)(stylesheet.get_data ()), WebKit.UserContentInjectedFrames.TOP_FRAME, WebKit.UserStyleLevel.USER, null, null)); @@ -248,7 +314,8 @@ namespace WebExtension { manager.install_api (this); if (uri != null) { - load_uri (extension.file.get_child (uri).get_uri ()); + string id = Checksum.compute_for_string (ChecksumType.MD5, extension.file.get_path ()); + load_uri ("extension:///%s/%s".printf (id, uri)); } else { load_html ("", extension.file.get_uri ()); } @@ -285,19 +352,8 @@ namespace WebExtension { if (extension.browser_action.icon != null) { debug ("Icon for %s: %s\n", extension.name, - extension.file.get_child (extension.browser_action.icon).get_path ()); - // Ensure the icon fits the size of a button in the toolbar - int icon_width = 16, icon_height = 16; - Gtk.icon_size_lookup (Gtk.IconSize.BUTTON, out icon_width, out icon_height); - // Take scale factor into account - icon_width *= scale_factor; - icon_height *= scale_factor; - try { - string filename = extension.file.get_child (extension.browser_action.icon).get_path (); - icon.pixbuf = new Gdk.Pixbuf.from_file_at_scale (filename, icon_width, icon_height, true); - } catch (Error error) { - warning ("Failed to set icon for %s: %s", extension.name, error.message); - } + extension.browser_action.icon); + load_icon.begin (extension, icon); } if (extension.browser_action.popup != null) { popover = new Gtk.Popover (this); @@ -305,6 +361,23 @@ namespace WebExtension { } add (icon); } + + async void load_icon (Extension extension, Gtk.Image icon) { + // Ensure the icon fits the size of a button in the toolbar + int icon_width = 16, icon_height = 16; + Gtk.icon_size_lookup (Gtk.IconSize.BUTTON, out icon_width, out icon_height); + // Take scale factor into account + icon_width *= scale_factor; + icon_height *= scale_factor; + try { + var image = yield extension.get_resource (extension.browser_action.icon); + // Note: The from_bytes variant has no autodetection + var stream = new MemoryInputStream.from_data (image.get_data (), free); + icon.pixbuf = yield new Gdk.Pixbuf.from_stream_at_scale_async (stream, icon_width, icon_height, true); + } catch (Error error) { + warning ("Failed to set icon for %s: %s", extension.name, error.message); + } + } } static string? js_to_string (JS.GlobalContext context, JS.Value value) { @@ -320,6 +393,25 @@ namespace WebExtension { public class Browser : Object, Midori.BrowserActivatable { public Midori.Browser browser { owned get; set; } + async void extension_scheme (WebKit.URISchemeRequest request) { + string[] path = request.get_path ().substring (1, -1).split ("/", 2); + string id = path[0]; + string resource = path[1]; + var manager = ExtensionManager.get_default (); + var extension = manager.extensions.lookup (id); + try { + if (extension != null) { + var data = yield extension.get_resource (resource); + var stream = new MemoryInputStream.from_data (data.get_data (), free); + request.finish (stream, data.length, "text/html"); + } + } catch (Error error) { + request.finish_error (error); + critical ("Failed to render %s: %s", request.get_path (), error.message); + } + request.unref (); + } + async void install_extension (Extension extension) throws Error { if (extension.browser_action != null) { browser.add_button (new Button (extension as Extension)); @@ -335,9 +427,12 @@ namespace WebExtension { (((Gtk.Container)browser.get_child ())).add (background); foreach (var filename in extension.background_scripts) { - uint8[] script; - yield extension.file.get_child (filename).load_contents_async (null, out script, null); - background.get_user_content_manager ().add_script (new WebKit.UserScript ((string)script, + var script = yield extension.get_resource (filename); + if (script == null) { + warning ("Failed to load background script for '%s': %s", extension.name, filename); + continue; + } + background.get_user_content_manager ().add_script (new WebKit.UserScript ((string)(script.get_data ()), WebKit.UserContentInjectedFrames.TOP_FRAME, WebKit.UserScriptInjectionTime.END, null, null)); @@ -349,6 +444,12 @@ namespace WebExtension { return; } + var context = WebKit.WebContext.get_default (); + context.register_uri_scheme ("extension", (request) => { + request.ref (); + extension_scheme.begin (request); + }); + var manager = ExtensionManager.get_default (); manager.extension_added.connect ((extension) => { install_extension.begin ((Extension)extension); diff --git a/vapi/libarchive.vapi b/vapi/libarchive.vapi new file mode 100644 index 00000000..64bba768 --- /dev/null +++ b/vapi/libarchive.vapi @@ -0,0 +1,430 @@ +/* libarchive.vapi - Bindings for libarchive(3) (version 3). + * + * Copyright (C) 2009 Julian Andres Klode + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Julian Andres Klode + * + */ + + +[CCode (cprefix="ARCHIVE_", lower_case_cprefix="archive_", cheader_filename = "archive.h")] +namespace Archive { + public const int VERSION_NUMBER; + public const string VERSION_STRING; + public int version_number (); + public unowned string version_string (); + + [CCode (instance_pos = 1.9, cname="archive_read_callback")] + public delegate ssize_t ReadCallback (Archive archive,[CCode (array_length = false)] out unowned uint8[] buffer); + [CCode (instance_pos = 1.9, cname="archive_skip_callback")] + public delegate int64 SkipCallback (Archive archive, int64 request); + [CCode (instance_pos = 1.9, cname="archive_write_callback")] + public delegate ssize_t WriteCallback (Archive archive,[CCode (array_length_type = "size_t")] uint8[] buffer); + [CCode (instance_pos = 1.9, cname="archive_open_callback")] + public delegate int OpenCallback (Archive archive); + + [CCode (cname="archive_close_callback")] + public delegate int CloseCallback (Archive archive); + + [CCode (cprefix="ARCHIVE_", cname="int", has_type_id = false)] + public enum Result { + EOF, + OK, + RETRY, + WARN, + FAILED + } + + // see libarchive/archive.h, l. 218 ff. + [CCode (cname="int", has_type_id = false)] + public enum Filter { + NONE, + GZIP, + BZIP2, + COMPRESS, + PROGRAM, + LZMA, + XZ, + UU, + RPM, + LZIP, + LRZIP, + LZOP, + GRZIP + } + + [CCode (cname="int", has_type_id = false)] + public enum Format { + BASE_MASK, + CPIO, + CPIO_POSIX, + CPIO_BIN_LE, + CPIO_BIN_BE, + CPIO_SVR4_NOCRC, + CPIO_SVR4_CRC, + SHAR, + SHAR_BASE, + SHAR_DUMP, + TAR, + TAR_USTAR, + TAR_PAX_INTERCHANGE, + TAR_PAX_RESTRICTED, + TAR_GNUTAR, + ISO9660, + ISO9660_ROCKRIDGE, + ZIP, + EMPTY, + AR, + AR_GNU, + AR_BSD, + MTREE + } + + [CCode (cprefix="ARCHIVE_EXTRACT_", cname="int", has_type_id = false)] + public enum ExtractFlags { + OWNER, + PERM, + TIME, + NO_OVERWRITE, + UNLINK, + ACL, + FFLAGS, + XATTR, + SECURE_SYMLINKS, + SECURE_NODOTDOT, + NO_AUTODIR, + NO_OVERWRITE_NEWER, + SPARSE + } + + [Compact] + [CCode (cname="struct archive", cprefix="archive_")] + public class Archive { + public int64 position_compressed (); + public int64 position_uncompressed (); + + public Format format (); + // Filter #0 is the one closest to the format, -1 is a synonym + // for the last filter, which is always the pseudo-filter that + // wraps the client callbacks. (libarchive/archive.h, l. 955) + public Filter filter_code (int filter_no); + + public unowned string compression_name (); + public unowned string format_name (); + public unowned string filter_name (int filter_no = 0); + + public int filter_count (); + public int file_count (); + + public int errno (); + public unowned string error_string (); + public void clear_error (); + public void set_error (int err, string fmt, ...); + public void copy_error (Archive src); + } + + + [Compact] + [CCode (cname="struct archive", free_function="archive_read_free")] + public class Read : Archive { + public Read (); + public Result support_filter_all (); + public Result support_filter_bzip2 (); + public Result support_filter_compress (); + public Result support_filter_gzip (); + public Result support_filter_grzip (); + public Result support_filter_lrzip (); + public Result support_filter_lzip (); + public Result support_filter_lzma (); + public Result support_filter_lzop (); + public Result support_filter_none (); + public Result support_filter_program (string command); + // TODO support_filter_program_signature (string, const void *, size_t) + public Result support_filter_rpm (); + public Result support_filter_uu (); + public Result support_filter_xz (); + public Result support_format_7zip (); + public Result support_format_all (); + public Result support_format_ar (); + public Result support_format_by_code (Format format_code); + public Result support_format_cab (); + public Result support_format_cpio (); + public Result support_format_empty (); + public Result support_format_gnutar (); + public Result support_format_iso9660 (); + public Result support_format_lha (); + public Result support_format_mtree (); + public Result support_format_rar (); + public Result support_format_raw (); + public Result support_format_tar (); + public Result support_format_xar (); + public Result support_format_zip (); + public Result support_format_zip_streamable (); + public Result support_format_zip_seekable (); + + public Result set_format (Format format_code); + public Result append_filter (Filter filter_code); + public Result append_filter_program (string cmd); + // TODO append_filter_program_signature (string, const void *, size_t); + + public Result open ( + [CCode (delegate_target_pos = 0.9)] OpenCallback ocb, + [CCode (delegate_target_pos = 0.9)] ReadCallback rcb, + [CCode (delegate_target_pos = 0.9)] CloseCallback ccb + ); + + public Result open2 ( + [CCode (delegate_target_pos = 0.9)] OpenCallback ocb, + [CCode (delegate_target_pos = 0.9)] ReadCallback rcb, + [CCode (delegate_target_pos = 0.9)] SkipCallback scb, + [CCode (delegate_target_pos = 0.9)] CloseCallback ccb + ); + + public Result open_filename (string filename, size_t block_size); + public Result open_memory ([CCode (array_length_type = "size_t")] uint8[] buffer); + public Result open_fd (int fd, size_t block_size); + public Result open_FILE (GLib.FileStream file); + public Result next_header (out unowned Entry entry); + public int64 header_position (); + + [CCode (cname="archive_read_data")] + public ssize_t read_data ([CCode (array_length_type = "size_t")] uint8[] buffer); + [CCode (cname="archive_read_data_block")] + public Result read_data_block ([CCode (array_length_type = "size_t")] out unowned uint8[] buffer, out int64 offset); + [CCode (cname="archive_read_data_skip")] + public Result read_data_skip (); + [CCode (cname="archive_read_data_into_fd")] + public Result read_data_into_fd (int fd); + + public Result extract (Entry entry, ExtractFlags? flags=0); + public Result extract2 (Entry entry, Write dest); + public void extract_set_skip_file (int64 dev, int64 ino); + public Result close (); + } + + [Compact] + [CCode (cname = "struct archive", free_function="archive_read_free")] + public class ReadDisk : Read { + public ReadDisk (); + public Result set_symlink_logical (); + public Result set_symlink_physical (); + public Result set_symlink_hybrid (); + public unowned string gname (int64 gid); + public unowned string uname (int64 uid); + public Result set_standard_lookup (); + + // HACK, they have no name in C. May not work correctly. + [CCode (instance_pos = 0, cname="void")] + public delegate unowned string GNameLookup (int64 gid); + [CCode (instance_pos = 0, cname="void")] + public delegate unowned string UNameLookup (int64 uid); + [CCode (instance_pos = 0, cname="void")] + public delegate void Cleanup (); + + public Result set_gname_lookup ( + GNameLookup lookup, + Cleanup? cleanup = null + ); + + public Result set_uname_lookup ( + UNameLookup lookup, + Cleanup? cleanup = null + ); + } + + [CCode (cname = "struct archive", free_function="archive_write_free")] + public class Write : Archive { + public Write (); + public Result add_filter (Filter filter_code); + public Result add_filter_by_name (string name); + public Result add_filter_b64encode (); + public Result add_filter_bzip2 (); + public Result add_filter_compress (); + public Result add_filter_grzip (); + public Result add_filter_gzip (); + public Result add_filter_lrzip (); + public Result add_filter_lzip (); + public Result add_filter_lzma (); + public Result add_filter_lzop (); + public Result add_filter_none (); + public Result add_filter_program (string cmd); + public Result add_filter_uuencode (); + public Result add_filter_xz (); + public Result set_format (Format format); + public Result set_format_by_name (string name); + public Result set_format_7zip (); + public Result set_format_ar_bsd (); + public Result set_format_ar_svr4 (); + public Result set_format_cpio (); + public Result set_format_cpio_newc (); + public Result set_format_gnutar (); + public Result set_format_iso9660 (); + public Result set_format_mtree (); + public Result set_format_mtree_classic (); + public Result set_format_pax (); + public Result set_format_pax_restricted (); + public Result set_format_raw (); + public Result set_format_shar (); + public Result set_format_shar_dump (); + public Result set_format_ustar (); + public Result set_format_v7tar (); + public Result set_format_xar (); + public Result set_format_zip (); + + public Result set_bytes_per_block (int bytes_per_block); + public int get_bytes_per_block (); + public Result set_bytes_in_last_block (int bytes_in_last_block); + public int get_bytes_in_last_block (); + public Result set_skip_file (int64 dev, int64 ino); + + public Result open ( + [CCode (delegate_target_pos = 0.9)] OpenCallback ocb, + [CCode (delegate_target_pos = 0.9)] WriteCallback rcb, + [CCode (delegate_target_pos = 0.9)] CloseCallback ccb + ); + public Result open_fd (int fd); + public Result open_filename (string filename); + public Result open_FILE (GLib.FileStream file); + public Result open_memory ([CCode (array_length_type = "size_t")] uint8[] buffer, out size_t used); + + [CCode (cname="archive_write_header")] + public Result write_header (Entry entry); + [CCode (cname="archive_write_data")] + public ssize_t write_data ([CCode (array_length_type = "size_t")] uint8[] data); + [CCode (cname="archive_write_data_block")] + public ssize_t write_data_block ([CCode (array_length_type = "size_t")] uint8[] data, int64 offset); + + public Result finish_entry (); + public Result close (); + } + + [Compact] + [CCode (cname = "struct archive", free_function="archive_write_free")] + public class WriteDisk : Write { + public WriteDisk (); + + public Result set_skip_file (int64 dev, int64 ino); + public Result set_options (ExtractFlags flags); + public Result set_standard_lookup (); + } + + [CCode (cheader_filename = "archive_entry.h", cprefix = "AE_", cname = "__LA_MODE_T", has_type_id = false)] + public enum FileType { + IFMT, + IFREG, + IFLNK, + IFSOCK, + IFCHR, + IFBLK, + IFDIR, + IFIFO + } + + [Compact] + [CCode (cname = "struct archive_entry", cheader_filename = "archive_entry.h")] + public class Entry { + [CCode (cname="archive_entry_new2")] + public Entry (Archive? archive = null); + public time_t atime (); + public long atime_nsec (); + public bool atime_is_set (); + public time_t birthtime (); + public long birthtime_nsec (); + public bool birthtime_is_set (); + public time_t ctime (); + public long ctime_nsec (); + public bool ctime_is_set (); + public int64 dev (); + public int64 devmajor (); + public int64 devminor (); + public FileType filetype (); + public unowned string fflags_text (); + public int64 gid (); + public unowned string gname (); + public unowned string hardlink (); + public int64 ino (); + public FileType mode (); + public time_t mtime (); + public long mtime_nsec (); + public bool mtime_is_set (); + public uint nlink (); + public unowned string pathname (); + public int64 rdev (); + public int64 rdevmajor (); + public int64 rdevminor (); + public unowned string sourcepath (); + public int64 size (); + public bool size_is_set (); + public unowned string strmode (); + public unowned string symlink (); + public int64 uid (); + public unowned string uname (); + public void set_atime (time_t atime, long blah); + public void unset_atime (); + public void set_birthtime (time_t birthtime, long blah); + public void unset_birthtime (); + public void set_ctime (time_t atime, long blah); + public void unset_ctime (); + public void set_dev (int64 dev); + public void set_devmajor (int64 major); + public void set_devminor (int64 major); + public void set_filetype (uint filetype); + public void set_fflags (ulong set, ulong clear); + public unowned string copy_fflags_text (string text); + public void set_gid (int64 gid); + public void set_gname (string gname); + public Result update_gname_utf8 (string gname); + public void set_hardlink (string link); + public void set_ino (ulong ino); + public void set_link (string link); + public Result update_link_utf8 (string link); + public void set_mode (FileType mode); + public void set_mtime (time_t mtime, long blah); + public void unset_mtime (); + public void set_nlink (uint nlink); + public void set_pathname (string pathname); + public Result update_pathname_utf8 (string pathname); + public void set_perm (FileType mode); + public void set_rdev (int64 dev); + public void set_rdevmajor (int64 devmajor); + public void set_rdevminor (int64 devminor); + public void set_size (int64 size); + public void unset_size (); + public void copy_sourcepath (string sourcepath); + public void set_symlink (string symlink); + public void set_uid (int64 uid); + public void set_uname (string uname); + public Result update_uname_utf8 (string uname); + + public unowned Entry clear (); + public Entry clone (); + + public void xattr_clear(); + public void xattr_add_entry(string name, void* value, size_t size); + public int xattr_count(); + public Result xattr_reset(); + public Result xattr_next(out unowned string name, out void* value, out size_t size); + + [Compact] + public class LinkResolver { + public LinkResolver (); + public void set_strategy (Format format_code); + public void linkify (Entry a, Entry b); + } + } +} -- cgit v1.2.1