CRUX-ARM : Home

Home :: Documentation :: Download :: Development :: Community :: Ports :: Packages :: Bugs :: Links :: About :: Donors
- Added pkginfo to .gitignore
[pkgutils-cross.git] / pkgutil.cc
index 39f68f5d3eb707c1fe7276f5ffe92c84d591fac5..1bbb83d518fd7e49f0c3655ca76d5e18b3e6121f 100644 (file)
@@ -2,6 +2,7 @@
 //  pkgutils
 // 
 //  Copyright (c) 2000-2005 Per Liden
+//  Copyright (c) 2006-2013 by CRUX team (http://crux.nu)
 // 
 //  This program is free software; you can redistribute it and/or modify
 //  it under the terms of the GNU General Public License as published by
 #include <sys/param.h>
 #include <unistd.h>
 #include <fcntl.h>
-#include <zlib.h>
 #include <libgen.h>
 #include <archive.h>
 #include <archive_entry.h>
 
+#define INIT_ARCHIVE(ar) \
+       archive_read_support_filter_gzip((ar)); \
+       archive_read_support_filter_bzip2((ar)); \
+       archive_read_support_filter_xz((ar)); \
+       archive_read_support_format_tar((ar))
+
+#define DEFAULT_BYTES_PER_BLOCK (20 * 512)
+
 using __gnu_cxx::stdio_filebuf;
 
 pkgutil::pkgutil(const string& name)
@@ -343,26 +351,24 @@ pair<string, pkgutil::pkginfo_t> pkgutil::pkg_open(const string& filename) const
        result.second.version = version;
 
        archive = archive_read_new();
-       archive_read_support_compression_all(archive);
-       archive_read_support_format_all(archive);
+       INIT_ARCHIVE(archive);
 
        if (archive_read_open_filename(archive,
-           const_cast<char*>(filename.c_str()),
-           ARCHIVE_DEFAULT_BYTES_PER_BLOCK) != ARCHIVE_OK)
-               throw runtime_error_with_errno("could not open " + filename);
+           filename.c_str(),
+           DEFAULT_BYTES_PER_BLOCK) != ARCHIVE_OK)
+               throw runtime_error_with_errno("could not open " + filename, archive_errno(archive));
 
        for (i = 0; archive_read_next_header(archive, &entry) ==
             ARCHIVE_OK; ++i) {
-               const struct stat* status;
 
                result.second.files.insert(result.second.files.end(),
                                           archive_entry_pathname(entry));
 
-               status = archive_entry_stat(entry);
+               mode_t mode = archive_entry_mode(entry);
 
-               if (S_ISREG(status->st_mode) &&
+               if (S_ISREG(mode) &&
                    archive_read_data_skip(archive) != ARCHIVE_OK)
-                       throw runtime_error_with_errno("could not read " + filename);
+                       throw runtime_error_with_errno("could not read " + filename, archive_errno(archive));
        }
    
        if (i == 0) {
@@ -372,7 +378,7 @@ pair<string, pkgutil::pkginfo_t> pkgutil::pkg_open(const string& filename) const
                        throw runtime_error("could not read " + filename);
        }
 
-       archive_read_finish(archive);
+       archive_read_free(archive);
 
        return result;
 }
@@ -382,34 +388,36 @@ void pkgutil::pkg_install(const string& filename, const set<string>& keep_list,
        struct archive* archive;
        struct archive_entry* entry;
        unsigned int i;
+       char buf[PATH_MAX];
+       string absroot;
 
        archive = archive_read_new();
-       archive_read_support_compression_all(archive);
-       archive_read_support_format_all(archive);
+       INIT_ARCHIVE(archive);
 
        if (archive_read_open_filename(archive,
-           const_cast<char*>(filename.c_str()),
-           ARCHIVE_DEFAULT_BYTES_PER_BLOCK) != ARCHIVE_OK)
-               throw runtime_error_with_errno("could not open " + filename);
+           filename.c_str(),
+           DEFAULT_BYTES_PER_BLOCK) != ARCHIVE_OK)
+               throw runtime_error_with_errno("could not open " + filename, archive_errno(archive));
 
        chdir(root.c_str());
+       absroot = getcwd(buf, sizeof(buf));
 
        for (i = 0; archive_read_next_header(archive, &entry) ==
             ARCHIVE_OK; ++i) {
                string archive_filename = archive_entry_pathname(entry);
-               string reject_dir = trim_filename(root + string("/") + string(PKG_REJECTED));
-               string original_filename = trim_filename(root + string("/") + archive_filename);
+               string reject_dir = trim_filename(absroot + string("/") + string(PKG_REJECTED));
+               string original_filename = trim_filename(absroot + string("/") + archive_filename);
                string real_filename = original_filename;
 
                // Check if file is filtered out via INSTALL
                if (non_install_list.find(archive_filename) != non_install_list.end()) {
-                       const struct stat* status;
+                       mode_t mode;
 
                        cout << utilname << ": ignoring " << archive_filename << endl;
 
-                       status = archive_entry_stat(entry);
+                       mode = archive_entry_mode(entry);
 
-                       if (S_ISREG(status->st_mode))
+                       if (S_ISREG(mode))
                                archive_read_data_skip(archive);
 
                        continue;
@@ -423,8 +431,9 @@ void pkgutil::pkg_install(const string& filename, const set<string>& keep_list,
                                           (real_filename.c_str()));
 
                // Extract file
-               if (archive_read_extract(archive, entry, 0) !=
-                   ARCHIVE_OK) {
+               unsigned int flags = ARCHIVE_EXTRACT_OWNER | ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_UNLINK;
+
+               if (archive_read_extract(archive, entry, flags) != ARCHIVE_OK) {
                        // If a file fails to install we just print an error message and
                        // continue trying to install the rest of the package.
                        const char* msg = archive_error_string(archive);
@@ -435,12 +444,10 @@ void pkgutil::pkg_install(const string& filename, const set<string>& keep_list,
                // Check rejected file
                if (real_filename != original_filename) {
                        bool remove_file = false;
-                       const struct stat* status;
-
-                       status = archive_entry_stat(entry);
+                       mode_t mode = archive_entry_mode(entry);
 
                        // Directory
-                       if (S_ISDIR(status->st_mode))
+                       if (S_ISDIR(mode))
                                remove_file = permissions_equal(real_filename, original_filename);
                        // Other files
                        else
@@ -462,7 +469,7 @@ void pkgutil::pkg_install(const string& filename, const set<string>& keep_list,
                        throw runtime_error("could not read " + filename);
        }
 
-       archive_read_finish(archive);
+       archive_read_free(archive);
 }
 
 void pkgutil::ldconfig() const
@@ -488,32 +495,69 @@ void pkgutil::ldconfig() const
 
 void pkgutil::pkg_footprint(string& filename) const
 {
-        unsigned int i;
+       unsigned int i;
        struct archive* archive;
        struct archive_entry* entry;
 
+       map<string, mode_t> hardlink_target_modes;
+
+       // We first do a run over the archive and remember the modes
+       // of regular files.
+       // In the second run, we print the footprint - using the stored
+       // modes for hardlinks.
+       //
+       // FIXME the code duplication here is butt ugly
        archive = archive_read_new();
-       archive_read_support_compression_all(archive);
-       archive_read_support_format_all(archive);
+       INIT_ARCHIVE(archive);
 
        if (archive_read_open_filename(archive,
-           const_cast<char*>(filename.c_str()),
-           ARCHIVE_DEFAULT_BYTES_PER_BLOCK) != ARCHIVE_OK)
-                throw runtime_error_with_errno("could not open " + filename);
+           filename.c_str(),
+           DEFAULT_BYTES_PER_BLOCK) != ARCHIVE_OK)
+                throw runtime_error_with_errno("could not open " + filename, archive_errno(archive));
 
        for (i = 0; archive_read_next_header(archive, &entry) ==
             ARCHIVE_OK; ++i) {
-               const struct stat* status;
 
-               status = archive_entry_stat(entry);
+               mode_t mode = archive_entry_mode(entry);
+
+               if (!archive_entry_hardlink(entry)) {
+                       const char *s = archive_entry_pathname(entry);
+
+                       hardlink_target_modes[s] = mode;
+               }
+
+               if (S_ISREG(mode) && archive_read_data_skip(archive))
+                       throw runtime_error_with_errno("could not read " + filename, archive_errno(archive));
+       }
+
+       archive_read_free(archive);
+
+       // Too bad, there doesn't seem to be a way to reuse our archive
+       // instance
+       archive = archive_read_new();
+       INIT_ARCHIVE(archive);
+
+       if (archive_read_open_filename(archive,
+           filename.c_str(),
+           DEFAULT_BYTES_PER_BLOCK) != ARCHIVE_OK)
+                throw runtime_error_with_errno("could not open " + filename, archive_errno(archive));
+
+       for (i = 0; archive_read_next_header(archive, &entry) ==
+            ARCHIVE_OK; ++i) {
+               mode_t mode = archive_entry_mode(entry);
 
                // Access permissions
-               if (S_ISLNK(status->st_mode)) {
+               if (S_ISLNK(mode)) {
                        // Access permissions on symlinks differ among filesystems, e.g. XFS and ext2 have different.
                        // To avoid getting different footprints we always use "lrwxrwxrwx".
                        cout << "lrwxrwxrwx";
                } else {
-                       cout << mtos(archive_entry_mode(entry));
+                       const char *h = archive_entry_hardlink(entry);
+
+                       if (h)
+                               cout << mtos(hardlink_target_modes[h]);
+                       else
+                               cout << mtos(mode);
                }
 
                cout << '\t';
@@ -540,16 +584,16 @@ void pkgutil::pkg_footprint(string& filename) const
                cout << '\t' << archive_entry_pathname(entry);
 
                // Special cases
-               if (S_ISLNK(status->st_mode)) {
+               if (S_ISLNK(mode)) {
                        // Symlink
                        cout << " -> " << archive_entry_symlink(entry);
-               } else if (S_ISCHR(status->st_mode) ||
-                          S_ISBLK(status->st_mode)) {
+               } else if (S_ISCHR(mode) ||
+                          S_ISBLK(mode)) {
                        // Device
                        cout << " (" << archive_entry_rdevmajor(entry)
                             << ", " << archive_entry_rdevminor(entry)
                             << ")";
-               } else if (S_ISREG(status->st_mode) &&
+               } else if (S_ISREG(mode) &&
                           archive_entry_size(entry) == 0) {
                        // Empty regular file
                        cout << " (EMPTY)";
@@ -557,19 +601,18 @@ void pkgutil::pkg_footprint(string& filename) const
 
                cout << '\n';
                
-               if (S_ISREG(status->st_mode) &&
-                   archive_read_data_skip(archive))
-                        throw runtime_error_with_errno("could not read " + filename);
-        }
+               if (S_ISREG(mode) && archive_read_data_skip(archive))
+                       throw runtime_error_with_errno("could not read " + filename, archive_errno(archive));
+       }
    
-        if (i == 0) {
+       if (i == 0) {
                if (archive_errno(archive) == 0)
-                        throw runtime_error("empty package");
-                else
-                        throw runtime_error("could not read " + filename);
-        }
+                       throw runtime_error("empty package");
+               else
+                       throw runtime_error("could not read " + filename);
+       }
 
-       archive_read_finish(archive);
+       archive_read_free(archive);
 }
 
 void pkgutil::print_version() const
@@ -663,42 +706,6 @@ string mtos(mode_t mode)
        return s;
 }
 
-int unistd_gzopen(char* pathname, int flags, mode_t mode)
-{
-       char* gz_mode;
-   
-       switch (flags & O_ACCMODE) {
-       case O_WRONLY:
-               gz_mode = "w";
-               break;
-
-       case O_RDONLY:
-               gz_mode = "r";
-               break;
-
-       case O_RDWR:
-       default:
-               errno = EINVAL;
-               return -1;
-       }
-
-       int fd;
-       gzFile gz_file;
-
-       if ((fd = open(pathname, flags, mode)) == -1)
-               return -1;
-   
-       if ((flags & O_CREAT) && fchmod(fd, mode))
-               return -1;
-   
-       if (!(gz_file = gzdopen(fd, gz_mode))) {
-               errno = ENOMEM;
-               return -1;
-       }
-   
-       return (int)gz_file;
-}
-
 string trim_filename(const string& filename)
 {
        string search("//");