예제 #1
0
void PictureBank::loadArchive(const std::string &filename)
{
  std::cout << "reading image archive: " << filename << std::endl;
  struct archive *a;
  struct archive_entry *entry;
  int rc;

  a = archive_read_new();
  archive_read_support_compression_all(a);
  archive_read_support_format_all(a);
  rc = archive_read_open_filename(a, filename.c_str(), 16384); // block size
  
  if (rc != ARCHIVE_OK) 
  {
    THROW("Cannot open archive " << filename);
  }

  SDL_Surface *surface;
  SDL_RWops *rw;

  const Uint32 bufferSize = 10000000;  // allocated buffer
  std::auto_ptr< Uint8 > buffer( new Uint8[ bufferSize ] );
   
  if( buffer.get() == 0 ) 
    THROW("Memory error, cannot allocate buffer size " << bufferSize);

  std::string entryname;
  while( archive_read_next_header(a, &entry) == ARCHIVE_OK )
  {
    // for all entries in archive
    entryname = archive_entry_pathname(entry);
    if ((archive_entry_stat(entry)->st_mode & S_IFREG) == 0)
    {
      // not a regular file (maybe a directory). skip it.
      continue;
    }

    if (archive_entry_stat(entry)->st_size >= bufferSize) 
    {
      THROW("Cannot load archive: file is too big " << entryname << " in archive " << filename);
    }
      
    int size = archive_read_data(a, buffer.get(), bufferSize);  // read data into buffer
    rw = SDL_RWFromMem(buffer.get(), size);
    surface = IMG_Load_RW(rw, 0);
    SDL_SetAlpha( surface, 0, 0 );

    setPicture(entryname, *surface);
    SDL_FreeRW(rw);
  }

  rc = archive_read_finish(a);
  if (rc != ARCHIVE_OK)
  {
    THROW("Error while reading archive " << filename);
  }
}
예제 #2
0
/* Return a pointer to the next file in the archive `a' */
static struct pkg_file *
freebsd_get_next_entry(struct archive *a)
{
	uint64_t length;
	char *str;
	struct archive_entry *entry;
	const struct stat *sb;

	assert(a != NULL);

	/* Read the next entry to a buffer. */
	if (archive_read_next_header(a, &entry) != ARCHIVE_OK) {
		return NULL;
	}

	/* Allocate enough space for the file and copy it to the string */
	length = archive_entry_size(entry);
	str = malloc(length+1);
	if (!str) {
		return NULL;
	}
	archive_read_data_into_buffer(a, str, length);
	str[length] = '\0';

	/* Get the needed struct stat from the archive */
	sb = archive_entry_stat(entry);

	/* Create the pkg_file and return it */
	return pkg_file_new_from_buffer(archive_entry_pathname(entry),
		length, str, sb);
}
예제 #3
0
size_t pkg_archive_get_size(const char *path){
	struct archive *a;
	struct archive_entry *entry;
	size_t size = 0;
	struct stat *f_stat;
	int r;
	
	a = pkg_archive_open(path);
	
	if(a == NULL)
		return -1;
	
	for(;;){
		r = archive_read_next_header(a, &entry);
		if(r == ARCHIVE_EOF)
			break;
		if(r != ARCHIVE_OK){
			pkg_archive_close(a);
			return -1;
		}
		
		f_stat = (struct stat *)archive_entry_stat(entry);
		
		size += f_stat->st_size;
	}
	
	return size;
}
/*
 * Extract all of the archive members of the specified archive
 * object.  If dest is not NULL, extract archive members to that
 * directory.  If dest is not NULL and does not exist as a directory,
 * create it first.  Return ARCHIVE_OK on success, ARCHIVE_* otherwise.
 */
int unpack_members_and_finish(struct archive *a, char *dest,
                              filterfunc filter, void* userptr) {
    int restore = 0;
    char prevcwd[PATH_MAX];
    struct archive_entry *e = NULL;

    if (getcwd(prevcwd, PATH_MAX) == NULL) {
        logMessage(ERROR, "unable to getcwd() (%s:%d): %m", __func__,
                                                            __LINE__);
        return ARCHIVE_FATAL;
    } else {
        restore = 1;
    }

    if (dest != NULL) {
        if (unpack_mkpath(dest) != ARCHIVE_OK) {
            return ARCHIVE_FATAL;
        } else if (chdir(dest) == -1) {
            logMessage(ERROR, "unable to chdir %s (%s:%d): %m",
                       dest, __func__, __LINE__);
            return ARCHIVE_FATAL;
        }
    }

    while (archive_read_next_header(a, &e) == ARCHIVE_OK) {
        const char *pathname = archive_entry_pathname(e);
        const struct stat *fstat = archive_entry_stat(e);

        if (filter && filter(pathname, fstat, userptr))
            continue;

        if (archive_read_extract(a, e, 0) != ARCHIVE_OK) {
            logMessage(ERROR, "error unpacking %s (%s:%d): %s",
                       pathname, __func__, __LINE__,
                       archive_error_string(a));
            return ARCHIVE_FATAL;
        }
    }

    if (restore && chdir(prevcwd) == -1) {
        logMessage(ERROR, "unable to chdir %s (%s:%d): %m",
                   dest, __func__, __LINE__);
        return ARCHIVE_FATAL;
    }

    if (archive_read_finish(a) != ARCHIVE_OK) {
        logMessage(ERROR, "error closing archive (%s:%d): %s",
                   __func__, __LINE__, archive_error_string(a));
        return ARCHIVE_FATAL;
    }

    return ARCHIVE_OK;
}
예제 #5
0
파일: write.c 프로젝트: marccodes/lfl
static int
append_archive(struct bsdtar *bsdtar, struct archive *a, struct archive *ina)
{
	struct archive_entry *in_entry;
	int e;

	while (0 == archive_read_next_header(ina, &in_entry)) {
		if (!new_enough(bsdtar, archive_entry_pathname(in_entry),
			archive_entry_stat(in_entry)))
			continue;
		if (excluded(bsdtar, archive_entry_pathname(in_entry)))
			continue;
		if (bsdtar->option_interactive &&
		    !yes("copy '%s'", archive_entry_pathname(in_entry)))
			continue;
		if (bsdtar->verbose)
			safe_fprintf(stderr, "a %s",
			    archive_entry_pathname(in_entry));
		siginfo_setinfo(bsdtar, "copying",
		    archive_entry_pathname(in_entry),
		    archive_entry_size(in_entry));
		siginfo_printinfo(bsdtar, 0);

		e = archive_write_header(a, in_entry);
		if (e != ARCHIVE_OK) {
			if (!bsdtar->verbose)
				bsdtar_warnc(bsdtar, 0, "%s: %s",
				    archive_entry_pathname(in_entry),
				    archive_error_string(a));
			else
				fprintf(stderr, ": %s", archive_error_string(a));
		}
		if (e == ARCHIVE_FATAL)
			exit(1);

		if (e >= ARCHIVE_WARN) {
			if (archive_entry_size(in_entry) == 0)
				archive_read_data_skip(ina);
			else if (copy_file_data(bsdtar, a, ina))
				exit(1);
		}

		if (bsdtar->verbose)
			fprintf(stderr, "\n");
	}

	/* Note: If we got here, we saw no write errors, so return success. */
	return (0);
}
예제 #6
0
파일: write.c 프로젝트: kamilWLca/brix
static int
append_archive(struct bsdtar *bsdtar, struct archive *a, struct archive *ina)
{
	struct archive_entry *in_entry;
	int e;

	while (ARCHIVE_OK == (e = archive_read_next_header(ina, &in_entry))) {
		if (!new_enough(bsdtar, archive_entry_pathname(in_entry),
			archive_entry_stat(in_entry)))
			continue;
		if (lafe_excluded(bsdtar->matching, archive_entry_pathname(in_entry)))
			continue;
		if (bsdtar->option_interactive &&
		    !yes("copy '%s'", archive_entry_pathname(in_entry)))
			continue;
		if (bsdtar->verbose)
			safe_fprintf(stderr, "a %s",
			    archive_entry_pathname(in_entry));
		if (need_report())
			report_write(bsdtar, a, in_entry, 0);

		e = archive_write_header(a, in_entry);
		if (e != ARCHIVE_OK) {
			if (!bsdtar->verbose)
				lafe_warnc(0, "%s: %s",
				    archive_entry_pathname(in_entry),
				    archive_error_string(a));
			else
				fprintf(stderr, ": %s", archive_error_string(a));
		}
		if (e == ARCHIVE_FATAL)
			exit(1);

		if (e >= ARCHIVE_WARN) {
			if (archive_entry_size(in_entry) == 0)
				archive_read_data_skip(ina);
			else if (copy_file_data(bsdtar, a, ina, in_entry))
				exit(1);
		}

		if (bsdtar->verbose)
			fprintf(stderr, "\n");
	}

	return (e == ARCHIVE_EOF ? ARCHIVE_OK : e);
}
예제 #7
0
static int
do_extract(struct archive *a, struct archive_entry *ae, const char *location,
    int nfiles, struct pkg *pkg, struct pkg *local)
{
	int	retcode = EPKG_OK;
	int	ret = 0, cur_file = 0;
	char	path[MAXPATHLEN], pathname[MAXPATHLEN], rpath[MAXPATHLEN];
	struct stat st;
	const struct stat *aest;
	bool renamed = false;
	const struct pkg_file *rf;
	struct pkg_config_file *rcf;
	struct sbuf *newconf;
	bool automerge = pkg_object_bool(pkg_config_get("AUTOMERGE"));
	unsigned long set, clear;

#ifndef HAVE_ARC4RANDOM
	srand(time(NULL));
#endif

	if (nfiles == 0)
		return (EPKG_OK);

	pkg_emit_extract_begin(pkg);
	pkg_emit_progress_start(NULL);

	newconf = sbuf_new_auto();

	do {
		ret = ARCHIVE_OK;
		sbuf_clear(newconf);
		rf = NULL;
		rcf = NULL;
		pkg_absolutepath(archive_entry_pathname(ae), path, sizeof(path));
		snprintf(pathname, sizeof(pathname), "%s%s%s",
		    location ? location : "", *path == '/' ? "" : "/",
		    path
		);
		strlcpy(rpath, pathname, sizeof(rpath));

		aest = archive_entry_stat(ae);
		archive_entry_fflags(ae, &set, &clear);
		if (lstat(rpath, &st) != -1) {
			/*
			 * We have an existing file on the path, so handle it
			 */
			if (!S_ISDIR(aest->st_mode)) {
				pkg_debug(2, "Old version found, renaming");
				pkg_add_file_random_suffix(rpath, sizeof(rpath), 12);
				renamed = true;
			}

			if (!S_ISDIR(st.st_mode) && S_ISDIR(aest->st_mode)) {
				if (S_ISLNK(st.st_mode)) {
					if (stat(rpath, &st) == -1) {
						pkg_emit_error("Dead symlink %s", rpath);
					} else {
						pkg_debug(2, "Directory is a symlink, use it");
						pkg_emit_progress_tick(cur_file++, nfiles);
						continue;
					}
				}
			}
		}

		archive_entry_set_pathname(ae, rpath);

		/* load in memory the content of config files */
		if (pkg_is_config_file(pkg, path, &rf, &rcf)) {
			pkg_debug(1, "Populating config_file %s", pathname);
			size_t len = archive_entry_size(ae);
			rcf->content = malloc(len);
			archive_read_data(a, rcf->content, len);
			if (renamed && (!automerge || local == NULL))
				strlcat(pathname, ".pkgnew", sizeof(pathname));
		}

		/*
		 * check if the file is already provided by previous package
		 */
		if (!automerge)
			attempt_to_merge(renamed, rcf, local, pathname, path, newconf);

		if (sbuf_len(newconf) == 0 && (rcf == NULL || rcf->content == NULL)) {
			pkg_debug(1, "Extracting: %s", archive_entry_pathname(ae));
			int install_as_user = (getenv("INSTALL_AS_USER") != NULL);
			int extract_flags = EXTRACT_ARCHIVE_FLAGS;
			if (install_as_user) {
				/* when installing as user don't try to set file ownership */
				extract_flags &= ~ARCHIVE_EXTRACT_OWNER;
			}
			ret = archive_read_extract(a, ae, extract_flags);
		} else {
			if (sbuf_len(newconf) == 0) {
				sbuf_cat(newconf, rcf->content);
				sbuf_finish(newconf);
			}
			pkg_debug(2, "Writing conf in %s", pathname);
			unlink(rpath);
			FILE *f = fopen(rpath, "w+");
			fprintf(f, "%s", sbuf_data(newconf));
			fclose(f);
		}

		if (ret != ARCHIVE_OK) {
			/*
			 * show error except when the failure is during
			 * extracting a directory and that the directory already
			 * exists.
			 * this allow to install packages linux_base from
			 * package for example
			 */
			if (archive_entry_filetype(ae) != AE_IFDIR ||
			    !is_dir(pathname)) {
				pkg_emit_error("archive_read_extract(): %s",
				    archive_error_string(a));
				retcode = EPKG_FATAL;
				goto cleanup;
			}
		}
		/* Reapply modes to the directories to work around a problem on FreeBSD 9 */
		if (archive_entry_filetype(ae) == AE_IFDIR)
			chmod(pathname, aest->st_mode);

		pkg_emit_progress_tick(cur_file++, nfiles);

		/* Rename old file */
		if (renamed) {

			pkg_debug(1, "Renaming %s -> %s", rpath, pathname);
#ifdef HAVE_CHFLAGS
			bool old = false;
			if (set & NOCHANGESFLAGS)
				chflags(rpath, 0);

			if (lstat(pathname, &st) != -1) {
				old = true;
				if (st.st_flags & NOCHANGESFLAGS)
					chflags(pathname, 0);
			}
#endif

			if (rename(rpath, pathname) == -1) {
#ifdef HAVE_CHFLAGS
				/* restore flags */
				if (old)
					chflags(pathname, st.st_flags);
#endif
				pkg_emit_error("cannot rename %s to %s: %s", rpath, pathname,
					strerror(errno));
				retcode = EPKG_FATAL;
				goto cleanup;
			}
#ifdef HAVE_CHFLAGS
			/* Restore flags */
			chflags(pathname, set);
#endif
		}

		if (string_end_with(pathname, ".pkgnew"))
			pkg_emit_notice("New configuration file: %s", pathname);

		renamed = false;
	} while ((ret = archive_read_next_header(a, &ae)) == ARCHIVE_OK);

	if (ret != ARCHIVE_EOF) {
		pkg_emit_error("archive_read_next_header(): %s",
		    archive_error_string(a));
		retcode = EPKG_FATAL;
	}

cleanup:

	pkg_emit_progress_tick(nfiles, nfiles);
	pkg_emit_extract_finished(pkg);

	if (renamed && retcode == EPKG_FATAL) {
#ifdef HAVE_CHFLAGS
		if (set & NOCHANGESFLAGS)
			chflags(rpath, set & ~NOCHANGESFLAGS);
#endif
		unlink(rpath);
	}

	return (retcode);
}
/* ArchiveWriter::addEntry {{{
 *
*/
ZEND_METHOD(ArchiveWriter, addEntry)
{
	zval *this = getThis();
	zval *entry_obj;
	archive_file_t *arch;
	archive_entry_t *entry, *entry_copy;
	char *pathname;
	int pathname_len;
	const struct stat *stat_sb;
	zend_error_handling error_handling;

	zend_replace_error_handling(EH_THROW, ce_ArchiveException, &error_handling TSRMLS_CC);

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &entry_obj) == FAILURE) {
		zend_restore_error_handling(&error_handling TSRMLS_CC);
		return;
	}

	if (!_archive_get_fd(this, &arch TSRMLS_CC)) {
		zend_restore_error_handling(&error_handling TSRMLS_CC);
		return;
	}

	if (!instanceof_function(Z_OBJCE_P(entry_obj), ce_ArchiveEntry TSRMLS_CC)) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "An instance of ArchiveEntry is required");
		zend_restore_error_handling(&error_handling TSRMLS_CC);
		return;
	}

	if (!_archive_get_entry_struct(entry_obj, &entry TSRMLS_CC)) {
		zend_restore_error_handling(&error_handling TSRMLS_CC);
		return;
	}

	pathname = entry->filename;
	pathname_len = strlen(pathname);

	_archive_normalize_path(&pathname, &pathname_len);

	if (pathname_len == 0 || pathname[0] == '\0') {
		/* user is probably trying to add "./", "/", ".." or ".", ignoring it silently */
		zend_restore_error_handling(&error_handling TSRMLS_CC);
		RETURN_TRUE;
	}

	/* copy entry.. */
	entry_copy = emalloc(sizeof(archive_entry_t));
	memcpy(entry_copy, entry, sizeof(archive_entry_t));
	entry_copy->entry = archive_entry_new();
	entry_copy->filename = estrdup(entry->filename);

	entry_copy->data = NULL;
	entry_copy->data_len = 0;

	archive_entry_copy_pathname(entry_copy->entry, pathname);
	stat_sb = archive_entry_stat(entry->entry);
	archive_entry_copy_stat(entry_copy->entry, stat_sb);

	/* ..and add it to the hash */
	zend_hash_update(arch->entries, pathname, pathname_len + 1, &entry_copy, sizeof(archive_entry_t), NULL);
	zend_restore_error_handling(&error_handling TSRMLS_CC);
	RETURN_TRUE;
}
예제 #9
0
/*
 * Handle 'x' and 't' modes.
 */
static void
read_archive(struct bsdtar *bsdtar, char mode)
{
	FILE			 *out;
	struct archive		 *a;
	struct archive_entry	 *entry;
	const struct stat	 *st;
	int			  r;

	while (*bsdtar->argv) {
		include(bsdtar, *bsdtar->argv);
		bsdtar->argv++;
	}

	if (bsdtar->names_from_file != NULL)
		include_from_file(bsdtar, bsdtar->names_from_file);

	a = archive_read_new();
	if (bsdtar->compress_program != NULL)
		archive_read_support_compression_program(a, bsdtar->compress_program);
	else
		archive_read_support_compression_all(a);
	archive_read_support_format_all(a);
	if (archive_read_open_file(a, bsdtar->filename,
	    bsdtar->bytes_per_block != 0 ? bsdtar->bytes_per_block :
	    DEFAULT_BYTES_PER_BLOCK))
		bsdtar_errc(bsdtar, 1, 0, "Error opening archive: %s",
		    archive_error_string(a));

	do_chdir(bsdtar);
	for (;;) {
		/* Support --fast-read option */
		if (bsdtar->option_fast_read &&
		    unmatched_inclusions(bsdtar) == 0)
			break;

		r = archive_read_next_header(a, &entry);
		if (r == ARCHIVE_EOF)
			break;
		if (r < ARCHIVE_OK)
			bsdtar_warnc(bsdtar, 0, "%s", archive_error_string(a));
		if (r <= ARCHIVE_WARN)
			bsdtar->return_value = 1;
		if (r == ARCHIVE_RETRY) {
			/* Retryable error: try again */
			bsdtar_warnc(bsdtar, 0, "Retrying...");
			continue;
		}
		if (r == ARCHIVE_FATAL)
			break;

		/*
		 * Exclude entries that are too old.
		 */
		st = archive_entry_stat(entry);
		if (bsdtar->newer_ctime_sec > 0) {
			if (st->st_ctime < bsdtar->newer_ctime_sec)
				continue; /* Too old, skip it. */
			if (st->st_ctime == bsdtar->newer_ctime_sec
			    && ARCHIVE_STAT_CTIME_NANOS(st)
			    <= bsdtar->newer_ctime_nsec)
				continue; /* Too old, skip it. */
		}
		if (bsdtar->newer_mtime_sec > 0) {
			if (st->st_mtime < bsdtar->newer_mtime_sec)
				continue; /* Too old, skip it. */
			if (st->st_mtime == bsdtar->newer_mtime_sec
			    && ARCHIVE_STAT_MTIME_NANOS(st)
			    <= bsdtar->newer_mtime_nsec)
				continue; /* Too old, skip it. */
		}

		/*
		 * Note that pattern exclusions are checked before
		 * pathname rewrites are handled.  This gives more
		 * control over exclusions, since rewrites always lose
		 * information.  (For example, consider a rewrite
		 * s/foo[0-9]/foo/.  If we check exclusions after the
		 * rewrite, there would be no way to exclude foo1/bar
		 * while allowing foo2/bar.)
		 */
		if (excluded(bsdtar, archive_entry_pathname(entry)))
			continue; /* Excluded by a pattern test. */

		/*
		 * Modify the pathname as requested by the user.  We
		 * do this for -t as well to give users a way to
		 * preview the effects of their rewrites.  We also do
		 * this before extraction security checks (including
		 * leading '/' removal).  Note that some rewrite
		 * failures prevent extraction.
		 */
		if (edit_pathname(bsdtar, entry))
			continue; /* Excluded by a rewrite failure. */

		if (mode == 't') {
			/* Perversely, gtar uses -O to mean "send to stderr"
			 * when used with -t. */
			out = bsdtar->option_stdout ? stderr : stdout;

			if (bsdtar->verbose < 2)
				safe_fprintf(out, "%s",
				    archive_entry_pathname(entry));
			else
				list_item_verbose(bsdtar, out, entry);
			fflush(out);
			r = archive_read_data_skip(a);
			if (r == ARCHIVE_WARN) {
				fprintf(out, "\n");
				bsdtar_warnc(bsdtar, 0, "%s",
				    archive_error_string(a));
			}
			if (r == ARCHIVE_RETRY) {
				fprintf(out, "\n");
				bsdtar_warnc(bsdtar, 0, "%s",
				    archive_error_string(a));
			}
			if (r == ARCHIVE_FATAL) {
				fprintf(out, "\n");
				bsdtar_warnc(bsdtar, 0, "%s",
				    archive_error_string(a));
				bsdtar->return_value = 1;
				break;
			}
			fprintf(out, "\n");
		} else {
			if (bsdtar->option_interactive &&
			    !yes("extract '%s'", archive_entry_pathname(entry)))
				continue;

			/*
			 * Format here is from SUSv2, including the
			 * deferred '\n'.
			 */
			if (bsdtar->verbose) {
				safe_fprintf(stderr, "x %s",
				    archive_entry_pathname(entry));
				fflush(stderr);
			}
			if (bsdtar->option_stdout)
				r = archive_read_data_into_fd(a, 1);
			else
				r = archive_read_extract(a, entry,
				    bsdtar->extract_flags);
			if (r != ARCHIVE_OK) {
				if (!bsdtar->verbose)
					safe_fprintf(stderr, "%s",
					    archive_entry_pathname(entry));
				safe_fprintf(stderr, ": %s",
				    archive_error_string(a));
				if (!bsdtar->verbose)
					fprintf(stderr, "\n");
				bsdtar->return_value = 1;
			}
			if (bsdtar->verbose)
				fprintf(stderr, "\n");
			if (r == ARCHIVE_FATAL)
				break;
		}
	}

	if (bsdtar->verbose > 2)
		fprintf(stdout, "Archive Format: %s,  Compression: %s\n",
		    archive_format_name(a), archive_compression_name(a));

	archive_read_finish(a);
}
예제 #10
0
/*
 * Display information about the current file.
 *
 * The format here roughly duplicates the output of 'ls -l'.
 * This is based on SUSv2, where 'tar tv' is documented as
 * listing additional information in an "unspecified format,"
 * and 'pax -l' is documented as using the same format as 'ls -l'.
 */
static void
list_item_verbose(struct bsdtar *bsdtar, FILE *out, struct archive_entry *entry)
{
	const struct stat	*st;
	char			 tmp[100];
	size_t			 w;
	const char		*p;
	const char		*fmt;
	time_t			 tim;
	static time_t		 now;

	st = archive_entry_stat(entry);

	/*
	 * We avoid collecting the entire list in memory at once by
	 * listing things as we see them.  However, that also means we can't
	 * just pre-compute the field widths.  Instead, we start with guesses
	 * and just widen them as necessary.  These numbers are completely
	 * arbitrary.
	 */
	if (!bsdtar->u_width) {
		bsdtar->u_width = 6;
		bsdtar->gs_width = 13;
	}
	if (!now)
		time(&now);
	fprintf(out, "%s %d ",
	    archive_entry_strmode(entry),
	    (int)(st->st_nlink));

	/* Use uname if it's present, else uid. */
	p = archive_entry_uname(entry);
	if ((p == NULL) || (*p == '\0')) {
		sprintf(tmp, "%lu ", (unsigned long)st->st_uid);
		p = tmp;
	}
	w = strlen(p);
	if (w > bsdtar->u_width)
		bsdtar->u_width = w;
	fprintf(out, "%-*s ", (int)bsdtar->u_width, p);

	/* Use gname if it's present, else gid. */
	p = archive_entry_gname(entry);
	if (p != NULL && p[0] != '\0') {
		fprintf(out, "%s", p);
		w = strlen(p);
	} else {
		sprintf(tmp, "%lu", (unsigned long)st->st_gid);
		w = strlen(tmp);
		fprintf(out, "%s", tmp);
	}

	/*
	 * Print device number or file size, right-aligned so as to make
	 * total width of group and devnum/filesize fields be gs_width.
	 * If gs_width is too small, grow it.
	 */
	if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) {
		sprintf(tmp, "%lu,%lu",
		    (unsigned long)major(st->st_rdev),
		    (unsigned long)minor(st->st_rdev)); /* ls(1) also casts here. */
	} else {
		/*
		 * Note the use of platform-dependent macros to format
		 * the filesize here.  We need the format string and the
		 * corresponding type for the cast.
		 */
		sprintf(tmp, BSDTAR_FILESIZE_PRINTF,
		    (BSDTAR_FILESIZE_TYPE)st->st_size);
	}
	if (w + strlen(tmp) >= bsdtar->gs_width)
		bsdtar->gs_width = w+strlen(tmp)+1;
	fprintf(out, "%*s", (int)(bsdtar->gs_width - w), tmp);

	/* Format the time using 'ls -l' conventions. */
	tim = (time_t)st->st_mtime;
	if (abs(tim - now) > (365/2)*86400)
		fmt = bsdtar->day_first ? "%e %b  %Y" : "%b %e  %Y";
	else
		fmt = bsdtar->day_first ? "%e %b %R" : "%b %e %R";
	strftime(tmp, sizeof(tmp), fmt, localtime(&tim));
	fprintf(out, " %s ", tmp);
	safe_fprintf(out, "%s", archive_entry_pathname(entry));

	/* Extra information for links. */
	if (archive_entry_hardlink(entry)) /* Hard link */
		safe_fprintf(out, " link to %s",
		    archive_entry_hardlink(entry));
	else if (S_ISLNK(st->st_mode)) /* Symbolic link */
		safe_fprintf(out, " -> %s", archive_entry_symlink(entry));
}
예제 #11
0
/*
 * Handle 'x' and 't' modes.
 */
static void
read_archive(struct bsdtar *bsdtar, char mode)
{
	struct progress_data	progress_data;
	FILE			 *out;
	struct archive		 *a;
	struct archive_entry	 *entry;
	const struct stat	 *st;
	int			  r;

	while (*bsdtar->argv) {
		lafe_include(&bsdtar->matching, *bsdtar->argv);
		bsdtar->argv++;
	}

	if (bsdtar->names_from_file != NULL)
		lafe_include_from_file(&bsdtar->matching,
		    bsdtar->names_from_file, bsdtar->option_null);

	a = archive_read_new();
	if (bsdtar->compress_program != NULL)
		archive_read_support_compression_program(a, bsdtar->compress_program);
	else
		archive_read_support_compression_all(a);
	archive_read_support_format_all(a);
	if (ARCHIVE_OK != archive_read_set_options(a, bsdtar->option_options))
		lafe_errc(1, 0, "%s", archive_error_string(a));
	if (archive_read_open_file(a, bsdtar->filename,
	    bsdtar->bytes_per_block != 0 ? bsdtar->bytes_per_block :
	    DEFAULT_BYTES_PER_BLOCK))
		lafe_errc(1, 0, "Error opening archive: %s",
		    archive_error_string(a));

	do_chdir(bsdtar);

	if (mode == 'x') {
		/* Set an extract callback so that we can handle SIGINFO. */
		progress_data.bsdtar = bsdtar;
		progress_data.archive = a;
		archive_read_extract_set_progress_callback(a, progress_func,
		    &progress_data);
	}

	if (mode == 'x' && bsdtar->option_chroot) {
#if HAVE_CHROOT
		if (chroot(".") != 0)
			lafe_errc(1, errno, "Can't chroot to \".\"");
#else
		lafe_errc(1, 0,
		    "chroot isn't supported on this platform");
#endif
	}

	for (;;) {
		/* Support --fast-read option */
		if (bsdtar->option_fast_read &&
		    lafe_unmatched_inclusions(bsdtar->matching) == 0)
			break;

		r = archive_read_next_header(a, &entry);
		progress_data.entry = entry;
		if (r == ARCHIVE_EOF)
			break;
		if (r < ARCHIVE_OK)
			lafe_warnc(0, "%s", archive_error_string(a));
		if (r <= ARCHIVE_WARN)
			bsdtar->return_value = 1;
		if (r == ARCHIVE_RETRY) {
			/* Retryable error: try again */
			lafe_warnc(0, "Retrying...");
			continue;
		}
		if (r == ARCHIVE_FATAL)
			break;

		if (bsdtar->uid >= 0) {
			archive_entry_set_uid(entry, bsdtar->uid);
			archive_entry_set_uname(entry, NULL);
		}
		if (bsdtar->gid >= 0) {
			archive_entry_set_gid(entry, bsdtar->gid);
			archive_entry_set_gname(entry, NULL);
		}
		if (bsdtar->uname)
			archive_entry_set_uname(entry, bsdtar->uname);
		if (bsdtar->gname)
			archive_entry_set_gname(entry, bsdtar->gname);

		/*
		 * Exclude entries that are too old.
		 */
		st = archive_entry_stat(entry);
		if (bsdtar->newer_ctime_sec > 0) {
			if (st->st_ctime < bsdtar->newer_ctime_sec)
				continue; /* Too old, skip it. */
			if (st->st_ctime == bsdtar->newer_ctime_sec
			    && ARCHIVE_STAT_CTIME_NANOS(st)
			    <= bsdtar->newer_ctime_nsec)
				continue; /* Too old, skip it. */
		}
		if (bsdtar->newer_mtime_sec > 0) {
			if (st->st_mtime < bsdtar->newer_mtime_sec)
				continue; /* Too old, skip it. */
			if (st->st_mtime == bsdtar->newer_mtime_sec
			    && ARCHIVE_STAT_MTIME_NANOS(st)
			    <= bsdtar->newer_mtime_nsec)
				continue; /* Too old, skip it. */
		}

		/*
		 * Note that pattern exclusions are checked before
		 * pathname rewrites are handled.  This gives more
		 * control over exclusions, since rewrites always lose
		 * information.  (For example, consider a rewrite
		 * s/foo[0-9]/foo/.  If we check exclusions after the
		 * rewrite, there would be no way to exclude foo1/bar
		 * while allowing foo2/bar.)
		 */
		if (lafe_excluded(bsdtar->matching, archive_entry_pathname(entry)))
			continue; /* Excluded by a pattern test. */

		if (mode == 't') {
			/* Perversely, gtar uses -O to mean "send to stderr"
			 * when used with -t. */
			out = bsdtar->option_stdout ? stderr : stdout;

			/*
			 * TODO: Provide some reasonable way to
			 * preview rewrites.  gtar always displays
			 * the unedited path in -t output, which means
			 * you cannot easily preview rewrites.
			 */
			if (bsdtar->verbose < 2)
				safe_fprintf(out, "%s",
				    archive_entry_pathname(entry));
			else
				list_item_verbose(bsdtar, out, entry);
			fflush(out);
			r = archive_read_data_skip(a);
			if (r == ARCHIVE_WARN) {
				fprintf(out, "\n");
				lafe_warnc(0, "%s",
				    archive_error_string(a));
			}
			if (r == ARCHIVE_RETRY) {
				fprintf(out, "\n");
				lafe_warnc(0, "%s",
				    archive_error_string(a));
			}
			if (r == ARCHIVE_FATAL) {
				fprintf(out, "\n");
				lafe_warnc(0, "%s",
				    archive_error_string(a));
				bsdtar->return_value = 1;
				break;
			}
			fprintf(out, "\n");
		} else {
			/* Note: some rewrite failures prevent extraction. */
			if (edit_pathname(bsdtar, entry))
				continue; /* Excluded by a rewrite failure. */

			if (bsdtar->option_interactive &&
			    !yes("extract '%s'", archive_entry_pathname(entry)))
				continue;

			/*
			 * Format here is from SUSv2, including the
			 * deferred '\n'.
			 */
			if (bsdtar->verbose) {
				safe_fprintf(stderr, "x %s",
				    archive_entry_pathname(entry));
				fflush(stderr);
			}

			// TODO siginfo_printinfo(bsdtar, 0);

			if (bsdtar->option_stdout)
				r = archive_read_data_into_fd(a, 1);
			else
				r = archive_read_extract(a, entry,
				    bsdtar->extract_flags);
			if (r != ARCHIVE_OK) {
				if (!bsdtar->verbose)
					safe_fprintf(stderr, "%s",
					    archive_entry_pathname(entry));
				safe_fprintf(stderr, ": %s",
				    archive_error_string(a));
				if (!bsdtar->verbose)
					fprintf(stderr, "\n");
				bsdtar->return_value = 1;
			}
			if (bsdtar->verbose)
				fprintf(stderr, "\n");
			if (r == ARCHIVE_FATAL)
				break;
		}
	}


	r = archive_read_close(a);
	if (r != ARCHIVE_OK)
		lafe_warnc(0, "%s", archive_error_string(a));
	if (r <= ARCHIVE_WARN)
		bsdtar->return_value = 1;

	if (bsdtar->verbose > 2)
		fprintf(stdout, "Archive Format: %s,  Compression: %s\n",
		    archive_format_name(a), archive_compression_name(a));

	archive_read_finish(a);
}
예제 #12
0
int cake_insert(cake_config_t *config, cake_package_t *package, char *filename, int flags)
{
    struct archive *archive;
    struct archive_entry *entry;
    struct stat st, vst;
    char *pathname;
    char *orig_cwd = get_current_dir_name();
    pkginfo_t *pkg = NULL;

    // Open archive
    archive = archive_read_new();
    archive_read_support_compression_all(archive);
    archive_read_support_format_tar(archive);
    archive_read_open_filename(archive, filename, 64);
    chdir(config->install_root);

    // Check for existing files
    while (archive_read_next_header(archive, &entry) == ARCHIVE_OK)
    {
        pathname = archive_entry_pathname(entry);
        vst = *archive_entry_stat(entry);

        // Parse PKGINFO
        if (!strcmp(pathname, ".PKGINFO"))
        {
            int size = archive_entry_size(entry);
            char buffer[size];
            archive_read_data(archive, buffer, size);

            pkg = pkginfo_read_buffer(buffer);
        } else {
            archive_read_data_skip(archive);
        }
    }

    archive_read_finish(archive);

    // Check if we found pkginfo
    char *pkgname = pkginfo_get(pkg, "pkgname");
    if (pkg == NULL || pkgname == NULL)
    {
        fputs("Invalid package, PKGINFO not found or invalid.\n", stderr);
        return -1;
    }

    // Get package specifier
    cake_package_t *pkgdesc;
    if (package != NULL)
    {
        pkgdesc = package;

        if (pkgdesc->name == NULL)
            pkgdesc->name = pkgname;

        if (pkgdesc->branch == NULL)
            pkgdesc->branch = "__LOCAL__";
    } else {
        pkgdesc = malloc(sizeof(cake_package_t));

        pkgdesc->name = pkgname;
        pkgdesc->branch = "__LOCAL__";
        pkgdesc->server = cake_config_get_server(config, "__LOCAL__");
    }

    // Remove existing package
    if (cakedb_has(config, pkgdesc))
        cake_remove(config, pkgdesc, 0);

    // Reopen archive
    chdir(orig_cwd);
    archive = archive_read_new();
    archive_read_support_compression_all(archive);
    archive_read_support_format_tar(archive);
    archive_read_open_filename(archive, filename, 64);
    chdir(config->install_root);

    // Check for existing files
    while (archive_read_next_header(archive, &entry) == ARCHIVE_OK)
    {
        pathname = archive_entry_pathname(entry);
        vst = *archive_entry_stat(entry);

        // Check file existence
        if (!(flags & CAKE_INSERT_FORCE) && !(vst.st_mode & S_IFDIR) && stat(pathname, &st) == 0)
        {
            fprintf(stderr, "Error: file %s%s exists on disk.\n", config->install_root, pathname);
            return -1;
        }
    }

    archive_read_finish(archive);

    // Holds all the installed filenames
    FILE *files = cakedb_open(config, pkgdesc, "files", "w");

    // Check if file could be opened
    if (files == NULL)
    {
        fputs("Unable to create database entry, permission denied?\n", stderr);
        return -1;
    }

    // Source origin for the package
    FILE *sourceloc = cakedb_open(config, pkgdesc, "origin", "w");
    fprintf(sourceloc, "%s\n%s\n%s\n", pkgdesc->name, pkgdesc->branch,
            pkgdesc->server == NULL ? "__LOCAL__" : pkgdesc->server->name);
    fclose(sourceloc);

    // Reopen archive
    chdir(orig_cwd);
    archive = archive_read_new();
    archive_read_support_compression_all(archive);
    archive_read_support_format_tar(archive);
    archive_read_open_filename(archive, filename, 64);
    chdir(config->install_root);

    // Extract files
    while (archive_read_next_header(archive, &entry) == ARCHIVE_OK)
    {
        pathname = strdup(archive_entry_pathname(entry));
        vst = *archive_entry_stat(entry);

        // Write PKGINFO
        if (!strcmp(pathname, ".PKGINFO"))
        {
            FILE *pkginfo = cakedb_open(config, pkgdesc, "pkginfo", "w");
            archive_read_data_into_fd(archive, fileno(pkginfo));
            continue;
        }

        // Ignore root dotfiles
        if (pathname[0] == '.')
        {
            archive_read_data_skip(archive);
            continue;
        }

        // Move /etc to /usr/etc
        if (!strncmp(pathname, "etc/", 4))
        {
            if (flags & CAKE_INSERT_INSTALL_CONFIGS)
            {
                // Verbose output
                if (flags & CAKE_INSERT_VERBOSE && pathname[strlen(pathname)-1] != '/')
                    printf("Installing configuration %s\n", pathname);
            } else {
                char *newpath = pathname;
                newpath = malloc(strlen(pathname)+5);
                sprintf(newpath, "usr/%s", pathname);

                archive_entry_set_pathname(entry, newpath);
            }
        } else {
            // Verbose output
            if (flags & CAKE_INSERT_VERBOSE_VERBOSE)
                printf("Extracting %s\n", pathname);
        }

        archive_read_extract(archive, entry,
                ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_TIME |
                ARCHIVE_EXTRACT_ACL | ARCHIVE_EXTRACT_XATTR |
                ARCHIVE_EXTRACT_UNLINK);

        fputs(pathname, files);
        fputc('\n', files);
    }

    // Close archive
    fclose(files);
    archive_read_finish(archive);

    return 0;
}
예제 #13
0
/*
 * explode source RPM into the current directory
 * use filters to skip packages and files we do not need
 */
int explodeRPM(const char *source,
        filterfunc filter,
        dependencyfunc provides,
        dependencyfunc deps,
        void* userptr)
{
    char buffer[BUFFERSIZE+1]; /* make space for trailing \0 */
    FD_t fdi;
    Header h;
    char * rpmio_flags = NULL;
    rpmRC rc;
    FD_t gzdi;
    struct archive *cpio;
    struct archive_entry *cpio_entry;
    struct cpio_mydata cpio_mydata;

    rpmts ts;
    rpmVSFlags vsflags;
    const char *compr;

    if (strcmp(source, "-") == 0)
        fdi = fdDup(STDIN_FILENO);
    else
        fdi = Fopen(source, "r.ufdio");

    if (Ferror(fdi)) {
        const char *srcname = (strcmp(source, "-") == 0) ? "<stdin>" : source;
        logMessage(ERROR, "%s: %s\n", srcname, Fstrerror(fdi));
        return EXIT_FAILURE;
    }
    rpmReadConfigFiles(NULL, NULL);

    /* Initialize RPM transaction */
    ts = rpmtsCreate();
    vsflags = 0;

    /* Do not check digests, signatures or headers */
    vsflags |= _RPMVSF_NODIGESTS;
    vsflags |= _RPMVSF_NOSIGNATURES;
    vsflags |= RPMVSF_NOHDRCHK;
    (void) rpmtsSetVSFlags(ts, vsflags);

    rc = rpmReadPackageFile(ts, fdi, "rpm2dir", &h);

    ts = rpmtsFree(ts);

    switch (rc) {
        case RPMRC_OK:
        case RPMRC_NOKEY:
        case RPMRC_NOTTRUSTED:
            break;
        case RPMRC_NOTFOUND:
            logMessage(ERROR, "%s is not an RPM package", source);
            return EXIT_FAILURE;
            break;
        case RPMRC_FAIL:
        default:
            logMessage(ERROR, "error reading header from %s package\n", source);
            return EXIT_FAILURE;
            break;
    }

    /* Retrieve all dependencies and run them through deps function */
    while (deps) {
        struct rpmtd_s td;
        const char *depname;

        if (!headerGet(h, RPMTAG_REQUIRENAME, &td, HEADERGET_MINMEM))
            break;

        /* iterator */
        while ((depname = rpmtdNextString(&td))) {
            if (deps(depname, userptr)) {
                Fclose(fdi);
                return EXIT_BADDEPS;
            }
        }
        rpmtdFreeData(&td);
        break;
    }

    /* Retrieve all provides and run them through provides function */
    while (provides) {
        struct rpmtd_s td;
        const char *depname;
        int found = 0;

        if (!headerGet(h, RPMTAG_PROVIDES, &td, HEADERGET_MINMEM))
            break;

        /* iterator */
        while ((depname = rpmtdNextString(&td))) {
            if (!provides(depname, userptr)) {
                found++;
            }
        }
        rpmtdFreeData(&td);
        if (found<=0)
            return EXIT_BADDEPS;
        break;
    }

    /* Retrieve type of payload compression. */
    compr = headerGetString(h, RPMTAG_PAYLOADCOMPRESSOR);
    if (compr && strcmp(compr, "gzip")) {
        checked_asprintf(&rpmio_flags, "r.%sdio", compr);
    }
    else {
        checked_asprintf(&rpmio_flags, "r.gzdio");
    }

    /* Open uncompressed cpio stream */
    gzdi = Fdopen(fdi, rpmio_flags);
    free(rpmio_flags);

    if (gzdi == NULL) {
        logMessage(ERROR, "cannot re-open payload: %s\n", Fstrerror(gzdi));
        return EXIT_FAILURE;
    }

    /* initialize cpio decompressor */
    cpio = archive_read_new();
    if (cpio==NULL) {
        Fclose(gzdi);
        return -1;
    }

    cpio_mydata.gzdi = gzdi;
    cpio_mydata.buffer = buffer;
    archive_read_support_compression_all(cpio);
    archive_read_support_format_all(cpio);
    rc = archive_read_open(cpio, &cpio_mydata, NULL, rpm_myread, rpm_myclose);

    /* check the status of archive_open */
    if (rc != ARCHIVE_OK){
        Fclose(gzdi);
        return -1;
    }

    /* read all files in cpio archive */
    while ((rc = archive_read_next_header(cpio, &cpio_entry)) == ARCHIVE_OK){
        const struct stat *fstat;
        int64_t fsize;
        const char* filename;
        int needskip = 1; /* do we need to read the data to get to the next header? */
        int offset = 0;
        int towrite = 0;

        filename = archive_entry_pathname(cpio_entry);
        fstat = archive_entry_stat(cpio_entry);
        fsize = archive_entry_size(cpio_entry);

        /* Strip leading slashes */
        while (filename[offset] == '/')
            offset+=1;

        /* Strip leading ./ */
        while (filename[offset] == '.' && filename[offset+1] == '/')
            offset+=2;

        /* Other file type - we do not care except special cases */
        if (!S_ISREG(fstat->st_mode))
            towrite = 1;
        else
            towrite = 2;

        if (filter && filter(filename+offset, fstat, userptr)) {
            /* filter this file */
            towrite = 0;
        }

        /* Create directories */
        char* dirname = strdup(filename+offset);

        /* If the dup fails, let's hope the dirs already exist */
        if (dirname){
            char* dirptr = dirname;
            while (dirptr && *dirptr) {
                dirptr = strchr(dirptr, '/');
                if (dirptr) {
                    *dirptr = 0;
                    mkdir(dirname, 0700);
                    *dirptr = '/';
                    dirptr++;
                }
            }
            free(dirname);
        }

        /* Regular file */
        if (towrite>=2) {
            FILE *fdout = fopen(filename+offset, "w");

            if (fdout==NULL){
                rc = 33;
                break;
            }
            
            rc = archive_read_data_into_fd(cpio, fileno(fdout));
            if (rc!=ARCHIVE_OK) {
                /* XXX We didn't get the file.. well.. */
                needskip = 0;
            } else {
                needskip = 0;
                fclose(fdout);
            }
        }

        /* symlink, we assume that the path contained in symlink
         * is shorter than BUFFERSIZE */
        while (towrite && S_ISLNK(fstat->st_mode)) {
            char symlinkbuffer[BUFFERSIZE-1];

            needskip = 0;
            if ((rc = archive_read_data(cpio, symlinkbuffer, fsize))!=ARCHIVE_OK) {
                /* XXX We didn't get the file.. well.. */
                break;
            }

            if (symlink(buffer, filename+offset)) {
                logMessage(ERROR, "Failed to create symlink %s -> %s", filename+offset, buffer);
            }

            break;
        }

        if(needskip)
            archive_read_data_skip(cpio);
    }

    archive_read_finish(cpio);

    return rc != ARCHIVE_OK;
}
예제 #14
0
파일: write.c 프로젝트: zeha/tarsnap-deb
static int
append_archive(struct bsdtar *bsdtar, struct archive *a, struct archive *ina,
    void * cookie)
{
	struct archive_entry *in_entry;
	int e;

	while (0 == archive_read_next_header(ina, &in_entry)) {
		if (truncate_archive(bsdtar))
			break;
		if (checkpoint_archive(bsdtar, 0))
			exit(1);
		if (cookie == NULL)
			disk_pause(bsdtar);
		if (network_select(0))
			exit(1);

		if (!new_enough(bsdtar, archive_entry_pathname(in_entry),
			archive_entry_stat(in_entry)))
			continue;
		if (excluded(bsdtar, archive_entry_pathname(in_entry)))
			continue;
		if (bsdtar->option_interactive &&
		    !yes("copy '%s'", archive_entry_pathname(in_entry)))
			continue;
		if (bsdtar->verbose)
			safe_fprintf(stderr, "a %s",
			    archive_entry_pathname(in_entry));
		siginfo_setinfo(bsdtar, "copying",
		    archive_entry_pathname(in_entry),
		    archive_entry_size(in_entry));
		siginfo_printinfo(bsdtar, 0);

		if (MODE_HEADER(bsdtar, a))
			goto err_fatal;
		e = archive_write_header(a, in_entry);
		if (e != ARCHIVE_OK) {
			if (!bsdtar->verbose)
				bsdtar_warnc(bsdtar, 0, "%s: %s",
				    archive_entry_pathname(in_entry),
				    archive_error_string(a));
			else
				fprintf(stderr, ": %s", archive_error_string(a));
		}
		if (e == ARCHIVE_FATAL)
			exit(1);
		if (e < ARCHIVE_WARN)
			goto done;

		if (MODE_DATA(bsdtar, a))
			goto err_fatal;

		if (archive_entry_size(in_entry) == 0)
			archive_read_data_skip(ina);
		else if (cookie == NULL) {
			if (copy_file_data(bsdtar, a, ina))
				exit(1);
		} else {
			switch (archive_multitape_copy(ina, cookie, a,
			    bsdtar->write_cookie)) {
			case -1:
				goto err_fatal;
			case -2:
				goto err_read;
			}
		}

done:
		if (MODE_DONE(bsdtar, a))
			goto err_fatal;
		if (bsdtar->verbose)
			fprintf(stderr, "\n");
		continue;

err_read:
		bsdtar->return_value = 1;
		if (MODE_DONE(bsdtar, a))
			goto err_fatal;
		if (bsdtar->verbose)
			fprintf(stderr, "\n");
		break;

err_fatal:
		bsdtar_warnc(bsdtar, archive_errno(a), "%s",
		    archive_error_string(a));
		exit(1);
	}

	/* Note: If we got here, we saw no write errors, so return success. */
	return (0);
}
예제 #15
0
파일: cpio.c 프로젝트: marccodes/lfl
/*
 * Display information about the current file.
 *
 * The format here roughly duplicates the output of 'ls -l'.
 * This is based on SUSv2, where 'tar tv' is documented as
 * listing additional information in an "unspecified format,"
 * and 'pax -l' is documented as using the same format as 'ls -l'.
 */
static void
list_item_verbose(struct cpio *cpio, struct archive_entry *entry)
{
	char			 size[32];
	char			 date[32];
	char			 uids[16], gids[16];
	const char 		*uname, *gname;
	FILE			*out = stdout;
	const struct stat	*st;
	const char		*fmt;
	time_t			 tim;
	static time_t		 now;

	st = archive_entry_stat(entry);

	if (!now)
		time(&now);

	if (cpio->option_numeric_uid_gid) {
		/* Format numeric uid/gid for display. */
		snprintf(uids, sizeof(uids), "%d",
		    (int)archive_entry_uid(entry));
		uname = uids;
		snprintf(gids, sizeof(gids), "%d",
		    (int)archive_entry_gid(entry));
		gname = gids;
	} else {
		/* Use uname if it's present, else lookup name from uid. */
		uname = archive_entry_uname(entry);
		if (uname == NULL)
			uname = lookup_uname(cpio, archive_entry_uid(entry));
		/* Use gname if it's present, else lookup name from gid. */
		gname = archive_entry_gname(entry);
		if (gname == NULL)
			gname = lookup_gname(cpio, archive_entry_gid(entry));
	}

	/* Print device number or file size. */
	if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) {
		snprintf(size, sizeof(size), "%lu,%lu",
		    (unsigned long)major(st->st_rdev),
		    (unsigned long)minor(st->st_rdev)); /* ls(1) also casts here. */
	} else {
		snprintf(size, sizeof(size), CPIO_FILESIZE_PRINTF,
		    (CPIO_FILESIZE_TYPE)st->st_size);
	}

	/* Format the time using 'ls -l' conventions. */
	tim = (time_t)st->st_mtime;
#if defined(_WIN32) && !defined(__CYGWIN__)
	/* Windows' strftime function does not support %e format. */
	if (abs(tim - now) > (365/2)*86400)
		fmt = cpio->day_first ? "%d %b  %Y" : "%b %d  %Y";
	else
		fmt = cpio->day_first ? "%d %b %H:%M" : "%b %d %H:%M";
#else
	if (abs(tim - now) > (365/2)*86400)
		fmt = cpio->day_first ? "%e %b  %Y" : "%b %e  %Y";
	else
		fmt = cpio->day_first ? "%e %b %H:%M" : "%b %e %H:%M";
#endif
	strftime(date, sizeof(date), fmt, localtime(&tim));

	fprintf(out, "%s%3d %-8s %-8s %8s %12s %s",
	    archive_entry_strmode(entry),
	    archive_entry_nlink(entry),
	    uname, gname, size, date,
	    archive_entry_pathname(entry));

	/* Extra information for links. */
	if (archive_entry_hardlink(entry)) /* Hard link */
		fprintf(out, " link to %s", archive_entry_hardlink(entry));
	else if (archive_entry_symlink(entry)) /* Symbolic link */
		fprintf(out, " -> %s", archive_entry_symlink(entry));
	fprintf(out, "\n");
}