Recognize hardlinks when reading cpio files.

This doesn't yet address the issue of selective restore
of hardlinked files.  With cpio format, it's possible to correctly
restore any linked file; the API doesn't yet fully support this.
(There's no way for the library to inform a client whether or not
there's a file body associated with this entry.  The assumption
right now is that "hardlink" entries have no file body.)
This commit is contained in:
Tim Kientzle 2004-03-07 00:57:43 +00:00
parent 3eba15c12e
commit cf5704e17b

View File

@ -32,8 +32,10 @@ __FBSDID("$FreeBSD$");
#ifdef DMALLOC
#include <dmalloc.h>
#endif
#include <err.h>
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@ -55,19 +57,43 @@ struct cpio_header {
char c_filesize[11];
};
struct links_entry {
struct links_entry *next;
struct links_entry *previous;
int links;
dev_t dev;
ino_t ino;
char *name;
};
#define CPIO_MAGIC 0x13141516
struct cpio {
int magic;
struct links_entry *links_head;
};
static int64_t atol8(const char *, unsigned);
static int archive_read_format_cpio_bid(struct archive *);
static int archive_read_format_cpio_cleanup(struct archive *);
static int archive_read_format_cpio_read_header(struct archive *,
struct archive_entry *);
static void record_hardlink(struct cpio *cpio, struct archive_entry *entry,
const struct stat *st);
int
archive_read_support_format_cpio(struct archive *a)
{
struct cpio *cpio;
cpio = malloc(sizeof(*cpio));
memset(cpio, 0, sizeof(*cpio));
cpio->magic = CPIO_MAGIC;
return (__archive_read_register_format(a,
NULL,
cpio,
archive_read_format_cpio_bid,
archive_read_format_cpio_read_header,
NULL));
archive_read_format_cpio_cleanup));
}
@ -100,6 +126,7 @@ archive_read_format_cpio_read_header(struct archive *a,
struct archive_entry *entry)
{
struct stat st;
struct cpio *cpio;
size_t bytes;
const struct cpio_header *header;
const void *h;
@ -107,6 +134,9 @@ archive_read_format_cpio_read_header(struct archive *a,
a->archive_format = ARCHIVE_FORMAT_CPIO;
a->archive_format_name = "POSIX octet-oriented cpio";
cpio = *(a->pformat_data);
if (cpio->magic != CPIO_MAGIC)
errx(1, "CPIO data lost? This can't happen.\n");
/* Read fixed-size portion of header. */
bytes = (a->compression_read_ahead)(a, &h, sizeof(struct cpio_header));
@ -114,7 +144,7 @@ archive_read_format_cpio_read_header(struct archive *a,
return (ARCHIVE_FATAL);
(a->compression_read_consume)(a, sizeof(struct cpio_header));
/* Parse out octal fields into struct stat */
/* Parse out octal fields into struct stat. */
memset(&st, 0, sizeof(st));
header = h;
@ -130,7 +160,7 @@ archive_read_format_cpio_read_header(struct archive *a,
/*
* Note: entry_bytes_remaining is at least 64 bits and
* therefore gauranteed to be big enough for a 33-bite file
* therefore gauranteed to be big enough for a 33-bit file
* size. struct stat.st_size may only be 32 bits, so
* assigning there first could lose information.
*/
@ -169,11 +199,36 @@ archive_read_format_cpio_read_header(struct archive *a,
return (ARCHIVE_EOF);
}
/* Detect and record hardlinks to previously-extracted entries. */
record_hardlink(cpio, entry, &st);
return (ARCHIVE_OK);
}
static int
archive_read_format_cpio_cleanup(struct archive *a)
{
struct cpio *cpio;
cpio = *(a->pformat_data);
/* Free inode->name map */
while (cpio->links_head != NULL) {
struct links_entry *lp = cpio->links_head->next;
if (cpio->links_head->name)
free(cpio->links_head->name);
free(cpio->links_head);
cpio->links_head = lp;
}
free(cpio);
*(a->pformat_data) = NULL;
return (ARCHIVE_OK);
}
/* Note that this implementation does not (and should not!) obey
/*
* Note that this implementation does not (and should not!) obey
* locale settings; you cannot simply substitute strtol here, since
* it does obey locale.
*/
@ -199,3 +254,43 @@ atol8(const char *p, unsigned char_cnt)
}
return (l);
}
static void
record_hardlink(struct cpio *cpio, struct archive_entry *entry,
const struct stat *st)
{
struct links_entry *le;
/*
* First look in the list of multiply-linked files. If we've
* already dumped it, convert this entry to a hard link entry.
*/
for (le = cpio->links_head; le; le = le->next) {
if (le->dev == st->st_dev && le->ino == st->st_ino) {
archive_entry_set_hardlink(entry, le->name);
if (--le->links <= 0) {
if (le->previous != NULL)
le->previous->next = le->next;
if (le->next != NULL)
le->next->previous = le->previous;
if (cpio->links_head == le)
cpio->links_head = le->next;
free(le);
}
return;
}
}
le = malloc(sizeof(struct links_entry));
if (cpio->links_head != NULL)
cpio->links_head->previous = le;
le->next = cpio->links_head;
le->previous = NULL;
cpio->links_head = le;
le->dev = st->st_dev;
le->ino = st->st_ino;
le->links = st->st_nlink - 1;
le->name = strdup(archive_entry_pathname(entry));
}