From: Fabian Groffen Date: Thu, 9 Feb 2012 07:58:56 +0000 (+0100) Subject: chpathtool: switch from C to Python version X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=47c5b25cab2c01efe725b0651f8d500b965883e4;p=portage.git chpathtool: switch from C to Python version To resolve bug #402413, use master's Python implementation of chpathtool. Removed the C implementation, which makes the need for configure a lot less. --- diff --git a/Makefile.am b/Makefile.am index 1d7c2430d..aef9678db 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,6 +1,6 @@ SHELL = @PORTAGE_BASH@ -SUBDIRS = src man bin pym cnf +SUBDIRS = man bin pym cnf AUTOMAKE_OPTIONS = dist-bzip2 no-dist-gzip diff --git a/bin/chpathtool.py b/bin/chpathtool.py index d0d49cb6d..1ae4e7c19 100755 --- a/bin/chpathtool.py +++ b/bin/chpathtool.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!@PREFIX_PORTAGE_PYTHON@ # Copyright 2011 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 diff --git a/configure.in b/configure.in index 5b909be8d..d18e59e94 100644 --- a/configure.in +++ b/configure.in @@ -45,108 +45,6 @@ GENTOO_PATH_GNUPROG(PORTAGE_FIND, [find]) GENTOO_PATH_GNUPROG(PORTAGE_XARGS, [xargs]) GENTOO_PATH_GNUPROG(PORTAGE_GREP, [grep]) -dnl Checks for header files. -AC_CHECK_HEADERS([string.h strings.h errno.h unistd.h stdio.h stdlib.h]) -AC_CHECK_HEADERS([sys/stat.h sys/types.h dirent.h sys/time.h alloca.h]) -AC_CHECK_HEADERS([fcntl.h]) -AC_CHECK_HEADERS([utime.h]) - -AC_SEARCH_LIBS([readdir], [c]) -AC_SEARCH_LIBS([strcpy], [c]) -AC_SEARCH_LIBS([strstr], [c]) -AC_SEARCH_LIBS([memcpy], [c]) -AC_CHECK_FUNCS([utimes]) - -AC_MSG_CHECKING([for the *time fields in struct stat]) -AC_TRY_LINK([ - #include - #include - #ifdef HAVE_FCNTL_H - #include - #endif - ], [ - struct stat s; - s.st_atimespec.tv_sec = 0; - s.st_mtimespec.tv_sec = 0; - ], [ - AC_DEFINE(ATIME_SEC, st_atimespec.tv_sec, [atime seconds for this platform]) - AC_DEFINE(ATIME_NSEC, st_atimespec.tv_nsec, [atime nanoseconds for this platform]) - AC_DEFINE(MTIME_SEC, st_mtimespec.tv_sec, [mtime seconds for this platform]) - AC_DEFINE(MTIME_NSEC, st_mtimespec.tv_nsec, [mtime nanoseconds for this platform]) - AC_MSG_RESULT([st_*timespec]) - ], [ -AC_TRY_LINK([ - #include - #include - #ifdef HAVE_FCNTL_H - #include - #endif - ], [ - struct stat s; - s.st_atim.tv_sec = 0; - s.st_mtim.tv_sec = 0; - ], [ - AC_DEFINE(ATIME_SEC, st_atim.tv_sec, [atime seconds for this platform]) - AC_DEFINE(ATIME_NSEC, st_atim.tv_nsec, [atime nanoseconds for this platform]) - AC_DEFINE(MTIME_SEC, st_mtim.tv_sec, [mtime seconds for this platform]) - AC_DEFINE(MTIME_NSEC, st_mtim.tv_nsec, [mtime nanoseconds for this platform]) - AC_MSG_RESULT([st_*tim]) - ], [ -AC_TRY_LINK([ - #include - #include - #ifdef HAVE_FCNTL_H - #include - #endif - ], [ - struct stat s; - s.st_atime = 0; - s.st_mtime = 0; - ], [ - AC_DEFINE(ATIME_SEC, st_atime, [atime seconds for this platform]) - AC_DEFINE(MTIME_SEC, st_mtime, [mtime seconds for this platform]) - AC_MSG_RESULT([st_*time]) - ], [ - AC_MSG_ERROR([cannot determine]) - ] -) - ] -) - ] -) - -AC_MSG_CHECKING([if S_ISWHT can be used]) -AC_TRY_LINK([ - #include - #include - #ifdef HAVE_FCNTL_H - #include - #endif - ], [ - struct stat s; - if (S_ISWHT(s.st_mode)) {} - ], [ - AC_DEFINE(HAVE_S_ISWHT, 1, [defined when S_ISWHT exists]) - AC_MSG_RESULT([yes]) - ], [ - AC_MSG_RESULT([no]) - ] -) - -AC_MSG_CHECKING([if lchown exists]) -AC_TRY_LINK([ - #include - #include - ], [ - lchown("", -1, -1); - ], [ - AC_DEFINE(HAVE_LCHOWN, 1, [defined when LCHOWN exists]) - AC_MSG_RESULT([yes]) - ], [ - AC_MSG_RESULT([no]) - ] -) - AC_ARG_WITH(portage-user, AC_HELP_STRING([--with-portage-user=myuser],[use user 'myuser' as portage owner (default portage)]), [case "${withval}" in @@ -233,7 +131,6 @@ AC_SUBST(PORTAGE_GREP) AC_CONFIG_FILES([subst-install], [chmod +x subst-install]) AC_CONFIG_FILES([ Makefile - src/Makefile man/Makefile bin/Makefile pym/Makefile diff --git a/man/chpathtool.5 b/man/chpathtool.5 deleted file mode 100644 index ce6dbcf66..000000000 --- a/man/chpathtool.5 +++ /dev/null @@ -1,76 +0,0 @@ -.TH "CHPATHTOOL" "5" "May 2011" "Portage 2.2-prefix" "Portage" -.SH NAME -chpathtool \- changes paths inside files -.SH SYNOPSIS -.B chpathtool -.R [-q] -.I in-file -.I out-file -.I magic -.I value -.SH DESCRIPTION -.B chpathtool -is an application that changes occurences of -.I magic -into -.I value -for the given -.I in-file -and writes the modified file as -.IR outfile ". -.RB "The important difference of " chpathtool " when compared to e.g. -.BR sed "(1) -is that for the strings it replaces, it maintains the original string -.RI "length of " magic " by prepending as much '/'-characters as needed -.RI "to the " value " string to match the size of " magic " for binary files." -.P -.RB "For efficiency, " chpathtool " will operate recursively if -.IR in-file " is a directory. In that case, " out-file " is assumed -to be a directory as well. Best efforts are made to retain the -properties of the original file in the output file. Currently, -.BR chpathtool " sets permissions, ownership and modification times. -.P -.BR chpathtool " is meant for the Prefix branch of Portage to relocate -packages from one prefix offset to another. This ability allows for the -use of binary packages, even though built for a different offset prefix. -.P -.RB "When " chpathtool " has found a match inside a binary file, -it simply uses the replacement string which has been prepended with as -many '/'-characters as necessary to match the size of the to be replaced -string. This strategy makes an awkward but correct path on UNIX -systems, and has the advantage of being as long as the original path. -This is in particular important for binary files that may store -information about the original string. An example is C-code where the -optimizer replaced a strlen(static_path_var) with the actual length of -that string. The strings cannot be just shortened also because in -binaries typically offsets are used to find relevant pieces of data. -Changing the size here would break those offsets. -.P -For text files, it avoids adding many '/'-characters because the -previously mentioned string length problem is typically is not going to -be a problem. Here, -.B chpathtool -replaces -the string, and searches for the end of the string in the form of a -zero-byte ('\\0'). At this byte, the neccessary padding bytes are -inserted, such that the file internally doesn't change in size, if it -happens to have zero-bytes in it. It is to be expected that for text -files, this zero-byte is never found, and -.BR "chpathtool " will " not " -write out the padding zero-bytes, and produce a warning about this -instead. -.P -.RB "Because " chpathtool " attempts not to change the internal file -.RI "structure, it can only operate if the length of " value " is not -.RI "greater than the length of " magic ". -.SH OPTIONS -.TP -.B -q -Suppress messages about padding bytes which could not be written. -.SH EXAMPLES -.B chpathtool -/var/tmp/my/old/prefix /var/tmp/new/prefix "/my/old/prefix" "/new/prefix" -.SH "REPORTING BUGS" -Please report bugs via http://bugs.gentoo.org/ -.SH AUTHORS -Fabian Groffen diff --git a/pym/_emerge/Binpkg.py b/pym/_emerge/Binpkg.py index e6e2e21d5..6c70b19f0 100644 --- a/pym/_emerge/Binpkg.py +++ b/pym/_emerge/Binpkg.py @@ -5,7 +5,6 @@ from _emerge.EbuildPhase import EbuildPhase from _emerge.BinpkgFetcher import BinpkgFetcher from _emerge.BinpkgEnvExtractor import BinpkgEnvExtractor from _emerge.BinpkgExtractorAsync import BinpkgExtractorAsync -from _emerge.BinpkgChpathtoolAsync import BinpkgChpathtoolAsync from _emerge.CompositeTask import CompositeTask from _emerge.BinpkgVerifier import BinpkgVerifier from _emerge.EbuildMerge import EbuildMerge @@ -23,7 +22,6 @@ import io import logging import textwrap from portage.output import colorize -from portage.const import EPREFIX class Binpkg(CompositeTask): @@ -52,7 +50,6 @@ class Binpkg(CompositeTask): dir_path = os.path.join(os.path.realpath(settings["PORTAGE_TMPDIR"]), "portage", pkg.category, pkg.pf) self._image_dir = os.path.join(dir_path, "image") - self._work_dir = os.path.join(dir_path, "work") self._infloc = os.path.join(dir_path, "build-info") self._ebuild_path = os.path.join(self._infloc, pkg.pf + ".ebuild") settings["EBUILD"] = self._ebuild_path @@ -225,7 +222,7 @@ class Binpkg(CompositeTask): pkg_path = self._pkg_path dir_mode = 0o755 - for mydir in (dir_path, self._image_dir, self._work_dir, infloc): + for mydir in (dir_path, self._image_dir, infloc): portage.util.ensure_dirs(mydir, uid=portage.data.portage_uid, gid=portage.data.portage_gid, mode=dir_mode) @@ -270,25 +267,6 @@ class Binpkg(CompositeTask): finally: f.close() - # PREFIX LOCAL: deal with EPREFIX from binpkg - # Retrieve the EPREFIX this package was built with - self._build_prefix = pkg_xpak.getfile(_unicode_encode("EPREFIX", - encoding=_encodings['repo.content'])) - if not self._buil_dprefix: - self._build_prefix = '' - else: - self._build_prefix = self._build_prefix.strip() - # We want to install in "our" prefix, not the binary one - self.settings["EPREFIX"] = EPREFIX - f = io.open(_unicode_encode(os.path.join(infloc, 'EPREFIX'), - encoding=_encodings['fs'], errors='strict'), - mode='w', encoding=_encodings['content'], errors='strict') - try: - f.write(_unicode_decode(EPREFIX + "\n")) - finally: - f.close() - # END PREFIX LOCAL - env_extractor = BinpkgEnvExtractor(background=self.background, scheduler=self.scheduler, settings=self.settings) @@ -314,18 +292,9 @@ class Binpkg(CompositeTask): self.wait() return - # PREFIX LOCAL: - # if the prefix differs, we copy it to the image after - # extraction using chpathtool - if (self._build_prefix != EPREFIX): - pkgloc = self._work_dir - else: - pkgloc = self._image_dir - # END PREFIX LOCAL - extractor = BinpkgExtractorAsync(background=self.background, env=self.settings.environ(), - image_dir=pkgloc, + image_dir=self._image_dir, pkg=self.pkg, pkg_path=self._pkg_path, logfile=self.settings.get("PORTAGE_LOG_FILE"), scheduler=self.scheduler) @@ -340,17 +309,30 @@ class Binpkg(CompositeTask): self.wait() return - # PREFIX LOCAL: use chpathtool binary - if self._build_prefix != EPREFIX: - chpathtool = BinpkgChpathtoolAsync(background=self.background, - image_dir=self._image_dir, work_dir=self._work_dir, - buildprefix=self._build_prefix, eprefix=EPREFIX, - pkg=self.pkg, scheduler=self.scheduler) - self._writemsg_level(">>> Adjusting Prefix to %s\n" % EPREFIX) - self._start_task(chpathtool, self._chpathtool_exit) - else: + try: + with io.open(_unicode_encode(os.path.join(self._infloc, "EPREFIX"), + encoding=_encodings['fs'], errors='strict'), mode='r', + encoding=_encodings['repo.content'], errors='replace') as f: + self._build_prefix = f.read().rstrip('\n') + except IOError: + self._build_prefix = "" + + if self._build_prefix == self.settings["EPREFIX"]: + ensure_dirs(self.settings["ED"]) + self._current_task = None + self.returncode = os.EX_OK self.wait() - # END PREFIX LOCAL + return + + chpathtool = SpawnProcess( + args=[portage._python_interpreter, + os.path.join(self.settings["PORTAGE_BIN_PATH"], "chpathtool.py"), + self.settings["D"], self._build_prefix, self.settings["EPREFIX"]], + background=self.background, env=self.settings.environ(), + scheduler=self.scheduler, + logfile=self.settings.get('PORTAGE_LOG_FILE')) + self._writemsg_level(">>> Adjusting Prefix to %s\n" % self.settings["EPREFIX"]) + self._start_task(chpathtool, self._chpathtool_exit) def _chpathtool_exit(self, chpathtool): if self._final_exit(chpathtool) != os.EX_OK: @@ -361,6 +343,27 @@ class Binpkg(CompositeTask): self.wait() return + # We want to install in "our" prefix, not the binary one + with io.open(_unicode_encode(os.path.join(self._infloc, "EPREFIX"), + encoding=_encodings['fs'], errors='strict'), mode='w', + encoding=_encodings['repo.content'], errors='strict') as f: + f.write(self.settings["EPREFIX"] + "\n") + + # Move the files to the correct location for merge. + image_tmp_dir = os.path.join( + self.settings["PORTAGE_BUILDDIR"], "image_tmp") + build_d = os.path.join(self.settings["D"], + self._build_prefix.lstrip(os.sep)) + if not os.path.isdir(build_d): + # Assume this is a virtual package or something. + shutil.rmtree(self._image_dir) + ensure_dirs(self.settings["ED"]) + else: + os.rename(build_d, image_tmp_dir) + shutil.rmtree(self._image_dir) + ensure_dirs(os.path.dirname(self.settings["ED"].rstrip(os.sep))) + os.rename(image_tmp_dir, self.settings["ED"]) + self.wait() def _unlock_builddir(self): diff --git a/src/Makefile.am b/src/Makefile.am deleted file mode 100644 index 0d5151f6a..000000000 --- a/src/Makefile.am +++ /dev/null @@ -1,10 +0,0 @@ -SHELL = @PORTAGE_BASH@ - -SUBDIRS = - -bin_PROGRAMS = chpathtool -bindir = @PORTAGE_BASE@/bin - -chpathtool_SOURCES = chpathtool.c - -MAINTAINERCLEANFILES = Makefile.in diff --git a/src/bsd-flags/Makefile.in b/src/bsd-flags/Makefile.in deleted file mode 100644 index b126e4e79..000000000 --- a/src/bsd-flags/Makefile.in +++ /dev/null @@ -1,45 +0,0 @@ -srcdir = @srcdir@ -abs_builddir = @abs_builddir@ -abs_srcdir = @abs_srcdir@ - -INSTALL = @INSTALL@ - -DISTFILES = setup.py \ - Makefile.in \ - PKG-INFO \ - README \ - missingos.c \ - setup.cfg \ - ChangeLog - -all: builddir - setup.py build - -builddir: - test "${abs_builddir}" != "${abs_srcdir}" \ - && for x in $(DISTFILES) \ - ; do $(INSTALL) ${abs_srcdir}/$${x} ${abs_builddir}/$${x} \ - ; done - -clean-builddir: - if test "${abs_builddir}" != "${abs_srcdir}" \ - ; then for x in $(DISTFILES) \ - ; do rm -f ${abs_builddir}/$${x} \ - ; done \ - ; fi - -install: builddir - setup.py install --root $(DESTDIR)/ - -distdir: - for x in $(DISTFILES); do \ - $(INSTALL) -D ${srcdir}/$${x} $(distdir)/$${x}; \ - done - chmod 755 $(distdir)/setup.py - -distclean: clean-builddir - -clean maintainer-clean: -.PHONY: all install distdir builddir -.PHONY: distclean clean maintainer-clean clean-builddir - diff --git a/src/chpathtool.c b/src/chpathtool.c deleted file mode 100644 index 7a74567b7..000000000 --- a/src/chpathtool.c +++ /dev/null @@ -1,530 +0,0 @@ -/* Copyright Gentoo Foundation 2006-2010 - * Author: Fabian Groffen - * - * chpathtool replaces a given string (magic) into another (value), - * thereby paying attention to the original size of magic in order not - * to change offsets in the file changed. To achieve this goal, value - * may not be greater in size than magic, and for binary files, the - * difference in size between the two is compensated by prepending - * '/'-characters to value uptil it matches the size of magic. For text - * files magic is just replaced with value, with the additional logic - * that the end of a string is considered to be at the first NULL-byte - * encountered after magic. If no such NULL-byte is found, the padding - * NULL-bytes are silently dropped. Since this is done on text files, - * this is likely the case. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef HAVE_ALLOCA_H -#include -#endif -#ifdef HAVE_UTIME_H -#include -#endif -#include -#include - -/* Don't allocate too much, or you'll be paying for waiting on IO, - * size -1 to align in memory. */ -#define BUFSIZE 8095 -/* POSIX says 256 on this one, but I can hardly believe that really is - * the limit of most popular systems. XOPEN says 1024, taking that - * value, hoping it is enough */ -#define MAX_PATH 1024 - -/* structure to track and trace hardlinks */ -typedef struct _file_hlink { - dev_t st_dev; /* device inode resides on */ - ino_t st_ino; /* inode's number */ - char path[MAX_PATH]; /* the file's path + name */ - struct _file_hlink* next; /* pointer to the next in the list */ -} file_hlink; - -static char *magic; -static char *value; -static size_t magiclen; -static size_t valuelen; -static char quiet; -static file_hlink *hlinks = NULL; - -/** - * Writes padding zero-bytes after the first encountered zero-byte. - * Returns padding if no zero-byte was seen, or 0 if padding was - * applied. - */ -static size_t padonwrite(size_t padding, char *buf, size_t len, FILE *fout) { - char *z; - if (padding == 0 || (z = memchr(buf, '\0', len)) == NULL) { - /* cheap case, nothing complicated to do here */ - fwrite(buf, len, 1, fout); - } else { - /* found a zero-byte, insert padding */ - fwrite(buf, z - buf, 1, fout); - /* now pad with zeros so we don't screw up - * the positions in the file */ - buf[0] = '\0'; - while (padding > 0) { - fwrite(buf, 1, 1, fout); - padding--; - } - fwrite(z, len - (z - buf), 1, fout); - } - - return(padding); -} - -/** - * Searches buf for an occurrence of needle, doing a byte-based match, - * disregarding end of string markers (zero-bytes), as strstr does. - * Returns a pointer to the first occurrence of needle in buf, or NULL - * if not found. If a partial match is found at the end of the buffer - * (size < strlen(needle)) it is returned as well. - */ -static char *memstr(const char *buf, const char *needle, size_t len) { - const char *ret; - size_t off; - for (ret = buf; ret - buf < len; ret++) { - off = 0; - while (needle[off] != '\0' && - (ret - buf) + off < len && - needle[off] == ret[off]) - { - off++; - } - if (needle[off] == '\0' || (ret - buf) + off == len) - return((char *)ret); - } - return(NULL); -} - -/** - * Copies the given file to the output file with prefix transformations - * on the fly. - */ -static int chpath(const char *fi, const char *fo) { - FILE *fin; - FILE *fout; - size_t len; - size_t pos; - size_t padding; - char *tmp; - char buf[BUFSIZE + 1]; - char firstblock; - char *lvalue = value; - size_t lvaluelen = valuelen; - - /* make sure there is a trailing zero-byte, such that strstr and - * strchr won't go out of bounds causing segfaults. */ - buf[BUFSIZE] = '\0'; - - fin = fopen(fi, "r"); - if (fin == NULL) { - fprintf(stderr, "unable to open %s: %s\n", fi, strerror(errno)); - return(-1); - } - fout = fopen(fo, "w"); - if (fout == NULL) { - fprintf(stderr, "unable to open %s: %s\n", fo, strerror(errno)); - return(-1); - } - - pos = 0; - padding = 0; - firstblock = 1; - while ((len = fread(buf + pos, 1, BUFSIZE - pos, fin)) != 0 || pos > 0) { - if (firstblock == 1) { - firstblock = 0; - /* examine the bytes we read to judge if they are binary or - * text; in case of the latter we can avoid writing ugly - * paths with zilions of slashes */ - for (pos = 0; pos < len; pos++) { - /* this is a very stupid assumption, but I don't know - * anything better: if we find a byte that's out of - * ASCII character scope, we assume this is binary */ - if ((buf[pos] < ' ' || buf[pos] > '~') && - strchr("\t\r\n", buf[pos]) == NULL) - { - /* Make sure we don't mess up code, GCC for instance - * hardcodes the result of strlen("string") in - * object code. This means we can nicely replace - * this string with a shorter NULL-terminated one, - * but that won't change the hardcoded output of the - * original strlen. Hence, pad the value with - * slashes until it is of same size as magic. */ - lvalue = alloca(sizeof(char) * (magiclen + 1)); - lvaluelen = magiclen; - snprintf(lvalue, lvaluelen + 1, "%*s", - (int)magiclen, value); - tmp = lvalue; - while (*tmp == ' ') - *tmp++ = '/'; - break; - } - } - pos = 0; - } - len += pos; - if ((tmp = memstr(buf, magic, len)) != NULL) { - /* if binary : */ - if (tmp == buf) { - if (len < magiclen) { - /* must be last piece */ - padding = padonwrite(padding, buf, len, fout); - break; - } - /* do some magic, overwrite it basically */ - fwrite(lvalue, lvaluelen, 1, fout); - /* store what we need to correct */ - padding += magiclen - lvaluelen; - /* move away the magic */ - pos = len - magiclen; - memmove(buf, buf + magiclen, pos); - continue; - } else { - /* move this bunch to the front */ - pos = len - (tmp - buf); - } - } else { - /* magic is not in here, since memchr also returns a match - * if incomplete but at the end of the string, here we can - * always read a new block. */ - if (len != BUFSIZE) { - /* last piece */ - padding = padonwrite(padding, buf, len, fout); - break; - } else { - pos = 0; - tmp = buf + len; - } - } - padding = padonwrite(padding, buf, len - pos, fout); - if (pos > 0) - memmove(buf, tmp, pos); - } - fflush(fout); - fclose(fout); - fclose(fin); - - if (padding != 0 && quiet == 0) { - fprintf(stdout, "warning: couldn't find a location to write " - "%zd padding bytes in %s\n", padding, fo); - } - - return(0); -} - -int dirwalk(char *src, char *srcp, char *trg, char *trgp) { - DIR *d; - struct dirent *de; - struct stat s; - struct timeval times[2]; -#ifndef HAVE_UTIMES - struct utimbuf ub; -#endif - char *st; - char *tt; - - if (lstat(trg, &s) != 0) { - /* initially create directory read/writable by owner, set - * permissions like src when we're done processing this - * directory. */ - if (mkdir(trg, S_IRWXU) != 0) { - fprintf(stderr, "failed to create directory %s: %s\n", - trg, strerror(errno)); - return(-1); - } - } else { - fprintf(stderr, "directory already exists: %s\n", trg); - return(-1); - } - - if ((d = opendir(src)) == NULL) { - fprintf(stderr, "cannot read directory %s: %s\n", src, strerror(errno)); - return(-1); - } - /* store the end of the string pointer */ - st = srcp; - tt = trgp; - while ((de = readdir(d)) != NULL) { - if (strcmp(de->d_name, "..") == 0 || strcmp(de->d_name, ".") == 0) - continue; - - *st = '/'; - strcpy(st + 1, de->d_name); - *tt = '/'; - strcpy(tt + 1, de->d_name); - st += 1 + strlen(de->d_name); - tt += 1 + strlen(de->d_name); - - if (lstat(src, &s) != 0) { - fprintf(stderr, "cannot stat %s: %s\n", src, strerror(errno)); - closedir(d); - return(-1); - } - if ( - S_ISBLK(s.st_mode) || - S_ISCHR(s.st_mode) || - S_ISFIFO(s.st_mode) || -#ifdef HAVE_S_ISWHT - S_ISWHT(s.st_mode) || -#endif - S_ISSOCK(s.st_mode) - ) - { - fprintf(stderr, "missing implementation for copying " - "object %s\n", src); - closedir(d); - return(-1); - } else if ( - S_ISDIR(s.st_mode) - ) - { - /* recurse */ - if (dirwalk(src, st, trg, tt) != 0) - return(-1); - } else if ( - S_ISREG(s.st_mode) - ) - { - /* handle hard links, match each of them to a list of known - * files (with st_nlink > 1), such that we only process each - * file once, and are able to restore the hard link. */ - if (s.st_nlink > 1) { - if (hlinks == NULL) { - hlinks = malloc(sizeof(file_hlink)); - hlinks->st_dev = s.st_dev; - hlinks->st_ino = s.st_ino; - strcpy(hlinks->path, src); - hlinks->next = NULL; - } else { - /* look for this file */ - file_hlink *hl = NULL; - do { - hl = (hl == NULL ? hlinks : hl->next); - if (hl->st_dev == s.st_dev && hl->st_ino == s.st_ino) { - /* this is the same file, make a hard link */ - if (link(hl->path, trg) != 0) { - fprintf(stderr, "failed to create hard link " - "%s: %s\n", trg, strerror(errno)); - return(-1); - } - hl = NULL; - break; - } - } while (hl->next != NULL); - /* we didn't know this one yet, add it */ - if (hl != NULL) { - hl = hl->next = malloc(sizeof(file_hlink)); - hl->st_dev = s.st_dev; - hl->st_ino = s.st_ino; - strcpy(hl->path, src); - hl->next = NULL; - } else { - /* don't "copy" the file, we already made a hard - * link to it, just restore modified path */ - st = srcp; - tt = trgp; - *st = *tt = '\0'; - continue; - } - } - } - - /* copy */ - if (chpath(src, trg) != 0) { - closedir(d); - return(-1); - } - /* fix permissions */ - if (chmod(trg, s.st_mode) != 0) { - fprintf(stderr, "failed to set permissions of %s: %s\n", - trg, strerror(errno)); - return(-1); - } - if (chown(trg, s.st_uid, s.st_gid) != 0) { - fprintf(stderr, "failed to set ownership of %s: %s\n", - trg, strerror(errno)); - return(-1); - } - times[0].tv_sec = s.ATIME_SEC; -#ifdef ATIME_NSEC - times[0].tv_usec = (s.ATIME_NSEC) / 1000; -#else - times[0].tv_usec = 0; -#endif - times[1].tv_sec = s.MTIME_SEC; -#ifdef MTIME_NSEC - times[1].tv_usec = (s.MTIME_NSEC) / 1000; -#else - times[1].tv_usec = 0; -#endif -#ifdef HAVE_UTIMES - if (utimes(trg, times) != 0) { - fprintf(stderr, "failed to set utimes of %s: %s\n", - trg, strerror(errno)); - return(-1); - } -#else - ub.actime = s.ATIME_SEC; - ub.modtime = s.MTIME_SEC; - if (utime(trg, &ub) != 0) { - fprintf(stderr, "failed to set utime of %s: %s\n", - trg, strerror(errno)); - return(-1); - } -#endif - } else if ( - S_ISLNK(s.st_mode) - ) - { - char buf[MAX_PATH]; - char rep[MAX_PATH]; - char *pb = buf; - char *pr = rep; - char *p = NULL; - int len = readlink(src, buf, MAX_PATH - 1); - buf[len] = '\0'; - /* replace occurences of magic by value in the string if - * absolute */ - if (buf[0] == '/') while ((p = strstr(pb, magic)) != NULL) { - memcpy(pr, pb, p - pb); - pr += p - pb; - memcpy(pr, value, valuelen); - pr += valuelen; - pb += magiclen; - } - len = (&buf[0] + len) - pb; - memcpy(pr, pb, len); - pr[len] = '\0'; - - if (symlink(rep, trg) != 0) { - fprintf(stderr, "failed to create symlink %s -> %s: %s\n", - trg, rep, strerror(errno)); - return(-1); - } - -#ifdef HAVE_LCHOWN - if (lchown(trg, s.st_uid, s.st_gid) != 0) { - fprintf(stderr, "failed to set ownership of %s: %s\n", - trg, strerror(errno)); - return(-1); - } -#endif - } - - /* restore modified path */ - st = srcp; - tt = trgp; - *st = *tt = '\0'; - } - closedir(d); - - /* fix permissions/ownership etc. */ - if (lstat(src, &s) != 0) { - fprintf(stderr, "cannot stat %s: %s\n", src, strerror(errno)); - return(-1); - } - if (chmod(trg, s.st_mode) != 0) { - fprintf(stderr, "failed to set permissions of %s: %s\n", - trg, strerror(errno)); - return(-1); - } - if (chown(trg, s.st_uid, s.st_gid) != 0) { - fprintf(stderr, "failed to set ownership of %s: %s\n", - trg, strerror(errno)); - return(-1); - } - times[0].tv_sec = s.ATIME_SEC; -#ifdef ATIME_NSEC - times[0].tv_usec = (s.ATIME_NSEC) / 1000; -#else - times[0].tv_usec = 0; -#endif - times[1].tv_sec = s.MTIME_SEC; -#ifdef MTIME_NSEC - times[1].tv_usec = (s.MTIME_NSEC) / 1000; -#else - times[1].tv_usec = 0; -#endif -#ifdef HAVE_UTIMES - if (utimes(trg, times) != 0) { - fprintf(stderr, "failed to set utimes of %s: %s\n", - trg, strerror(errno)); - return(-1); - } -#else - ub.actime = s.ATIME_SEC; - ub.modtime = s.MTIME_SEC; - if (utime(trg, &ub) != 0) { - fprintf(stderr, "failed to set utime of %s: %s\n", - trg, strerror(errno)); - return(-1); - } -#endif - - return(0); -} - -int main(int argc, char **argv) { - struct stat file; - int o = 0; - - quiet = 0; - if (argc >= 2 && strcmp(argv[1], "-q") == 0) { - argc--; - o++; - quiet = 1; - } - - if (argc != 5) { - fprintf(stderr, "usage: [-q] in-file out-file magic value\n"); - fprintf(stderr, " if in-file is a directory, out-file is " - "treated as one too\n"); - fprintf(stderr, " -q suppress messages about being unable to " - "write padding bytes\n"); - return(-1); - } - - magic = argv[o + 3]; - value = argv[o + 4]; - magiclen = strlen(magic); - valuelen = strlen(value); - - if (magiclen < valuelen) { - fprintf(stderr, "value length (%zd) is bigger than " - "the magic length (%zd)\n", valuelen, magiclen); - return(-1); - } - if (magiclen > BUFSIZE) { - fprintf(stderr, "magic length (%zd) is bigger than " - "BUFSIZE (%d), unable to process\n", magiclen, BUFSIZE); - return(-1); - } - - if (lstat(argv[o + 1], &file) != 0) { - fprintf(stderr, "unable to stat %s: %s\n", - argv[o + 1], strerror(errno)); - return(-1); - } - if (S_ISDIR(file.st_mode)) { - char *src = alloca(sizeof(char) * MAX_PATH); - char *trg = alloca(sizeof(char) * MAX_PATH); - strcpy(src, argv[o + 1]); - strcpy(trg, argv[o + 2]); - /* walk this directory and process recursively */ - return(dirwalk(src, src + strlen(argv[o + 1]), - trg, trg + strlen(argv[o + 2]))); - } else { - /* process as normal file */ - return(chpath(argv[o + 1], argv[o + 2])); - } -} -