mirror of
https://github.com/MidnightCommander/mc
synced 2025-01-18 17:29:28 +03:00
tar: use separate structure to store various file info.
Signed-off-by: Andrew Borodin <aborodin@vmail.ru>
This commit is contained in:
parent
d4d0fe6019
commit
c35dfc3363
@ -274,6 +274,24 @@ typedef enum
|
||||
HEADER_FAILURE /* ill-formed header, or bad checksum */
|
||||
} read_header;
|
||||
|
||||
struct tar_stat_info
|
||||
{
|
||||
char *orig_file_name; /* name of file read from the archive header */
|
||||
char *file_name; /* name of file for the current archive entry after being normalized */
|
||||
char *link_name; /* name of link for the current archive entry */
|
||||
#if 0
|
||||
char *uname; /* user name of owner */
|
||||
char *gname; /* group name of owner */
|
||||
#endif
|
||||
struct stat stat; /* regular filesystem stat */
|
||||
|
||||
/* stat() doesn't always have access, data modification, and status
|
||||
change times in a convenient form, so store them separately. */
|
||||
struct timespec atime;
|
||||
struct timespec mtime;
|
||||
struct timespec ctime;
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
struct vfs_s_super base; /* base class */
|
||||
@ -308,6 +326,10 @@ static off_t current_tar_position = 0;
|
||||
|
||||
static union block block_buf;
|
||||
|
||||
static struct tar_stat_info current_stat_info;
|
||||
|
||||
static struct timespec start_time;
|
||||
|
||||
/* --------------------------------------------------------------------------------------------- */
|
||||
/*** file scope functions ************************************************************************/
|
||||
/* --------------------------------------------------------------------------------------------- */
|
||||
@ -324,6 +346,48 @@ tar_base64_init (void)
|
||||
|
||||
/* --------------------------------------------------------------------------------------------- */
|
||||
|
||||
static void
|
||||
tar_stat_destroy (struct tar_stat_info *st)
|
||||
{
|
||||
g_free (st->orig_file_name);
|
||||
g_free (st->file_name);
|
||||
g_free (st->link_name);
|
||||
#if 0
|
||||
g_free (st->uname);
|
||||
g_free (st->gname);
|
||||
#endif
|
||||
memset (st, 0, sizeof (*st));
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------- */
|
||||
|
||||
static void
|
||||
tar_assign_string (char **string, char *value)
|
||||
{
|
||||
g_free (*string);
|
||||
*string = value;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------- */
|
||||
|
||||
static void
|
||||
tar_assign_string_dup (char **string, const char *value)
|
||||
{
|
||||
g_free (*string);
|
||||
*string = g_strdup (value);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------- */
|
||||
|
||||
static void
|
||||
tar_assign_string_dup_n (char **string, const char *value, size_t n)
|
||||
{
|
||||
g_free (*string);
|
||||
*string = g_strndup (value, n);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Represent @n using a signed integer I such that (uintmax_t) I == @n.
|
||||
With a good optimizing compiler, this is equivalent to (intmax_t) i
|
||||
@ -672,11 +736,11 @@ tar_checksum (const union block *header)
|
||||
/* --------------------------------------------------------------------------------------------- */
|
||||
|
||||
static void
|
||||
tar_decode_header (union block *header, tar_super_t * arch, struct stat *st)
|
||||
tar_decode_header (union block *header, tar_super_t * arch)
|
||||
{
|
||||
gboolean hbits = FALSE;
|
||||
|
||||
st->st_mode = MODE_FROM_HEADER (header->header.mode, &hbits);
|
||||
current_stat_info.stat.st_mode = MODE_FROM_HEADER (header->header.mode, &hbits);
|
||||
|
||||
/*
|
||||
* Try to determine the archive format.
|
||||
@ -720,31 +784,31 @@ tar_decode_header (union block *header, tar_super_t * arch, struct stat *st)
|
||||
/* --------------------------------------------------------------------------------------------- */
|
||||
|
||||
static void
|
||||
tar_fill_stat (struct vfs_s_super *archive, struct stat *st, union block *header)
|
||||
tar_fill_stat (struct vfs_s_super *archive, union block *header)
|
||||
{
|
||||
tar_super_t *arch = TAR_SUPER (archive);
|
||||
|
||||
/* Adjust st->st_mode because there are tar-files with
|
||||
/* Adjust current_stat_info.stat.st_mode because there are tar-files with
|
||||
* typeflag==SYMTYPE and S_ISLNK(mod)==0. I don't
|
||||
* know about the other modes but I think I cause no new
|
||||
* problem when I adjust them, too. -- Norbert.
|
||||
*/
|
||||
if (header->header.typeflag == DIRTYPE || header->header.typeflag == GNUTYPE_DUMPDIR)
|
||||
st->st_mode |= S_IFDIR;
|
||||
current_stat_info.stat.st_mode |= S_IFDIR;
|
||||
else if (header->header.typeflag == SYMTYPE)
|
||||
st->st_mode |= S_IFLNK;
|
||||
current_stat_info.stat.st_mode |= S_IFLNK;
|
||||
else if (header->header.typeflag == CHRTYPE)
|
||||
st->st_mode |= S_IFCHR;
|
||||
current_stat_info.stat.st_mode |= S_IFCHR;
|
||||
else if (header->header.typeflag == BLKTYPE)
|
||||
st->st_mode |= S_IFBLK;
|
||||
current_stat_info.stat.st_mode |= S_IFBLK;
|
||||
else if (header->header.typeflag == FIFOTYPE)
|
||||
st->st_mode |= S_IFIFO;
|
||||
current_stat_info.stat.st_mode |= S_IFIFO;
|
||||
else
|
||||
st->st_mode |= S_IFREG;
|
||||
current_stat_info.stat.st_mode |= S_IFREG;
|
||||
|
||||
st->st_dev = 0;
|
||||
current_stat_info.stat.st_dev = 0;
|
||||
#ifdef HAVE_STRUCT_STAT_ST_RDEV
|
||||
st->st_rdev = 0;
|
||||
current_stat_info.stat.st_rdev = 0;
|
||||
#endif
|
||||
|
||||
switch (arch->type)
|
||||
@ -754,10 +818,10 @@ tar_fill_stat (struct vfs_s_super *archive, struct stat *st, union block *header
|
||||
case TAR_GNU:
|
||||
case TAR_OLDGNU:
|
||||
/* *INDENT-OFF* */
|
||||
st->st_uid = *header->header.uname != '\0'
|
||||
current_stat_info.stat.st_uid = *header->header.uname != '\0'
|
||||
? (uid_t) vfs_finduid (header->header.uname)
|
||||
: UID_FROM_HEADER (header->header.uid);
|
||||
st->st_gid = *header->header.gname != '\0'
|
||||
current_stat_info.stat.st_gid = *header->header.gname != '\0'
|
||||
? (gid_t) vfs_findgid (header->header.gname)
|
||||
: GID_FROM_HEADER (header->header.gid);
|
||||
/* *INDENT-ON* */
|
||||
@ -767,7 +831,7 @@ tar_fill_stat (struct vfs_s_super *archive, struct stat *st, union block *header
|
||||
case BLKTYPE:
|
||||
case CHRTYPE:
|
||||
#ifdef HAVE_STRUCT_STAT_ST_RDEV
|
||||
st->st_rdev =
|
||||
current_stat_info.stat.st_rdev =
|
||||
makedev (MAJOR_FROM_HEADER (header->header.devmajor),
|
||||
MINOR_FROM_HEADER (header->header.devminor));
|
||||
#endif
|
||||
@ -778,37 +842,38 @@ tar_fill_stat (struct vfs_s_super *archive, struct stat *st, union block *header
|
||||
break;
|
||||
|
||||
default:
|
||||
st->st_uid = UID_FROM_HEADER (header->header.uid);
|
||||
st->st_gid = GID_FROM_HEADER (header->header.gid);
|
||||
current_stat_info.stat.st_uid = UID_FROM_HEADER (header->header.uid);
|
||||
current_stat_info.stat.st_gid = GID_FROM_HEADER (header->header.gid);
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef HAVE_STRUCT_STAT_ST_MTIM
|
||||
st->st_atim.tv_nsec = st->st_mtim.tv_nsec = st->st_ctim.tv_nsec = 0;
|
||||
current_stat_info.atime.tv_nsec = 0;
|
||||
current_stat_info.mtime.tv_nsec = 0;
|
||||
current_stat_info.ctime.tv_nsec = 0;
|
||||
#endif
|
||||
st->st_mtime = TIME_FROM_HEADER (header->header.mtime);
|
||||
st->st_atime = 0;
|
||||
st->st_ctime = 0;
|
||||
current_stat_info.mtime.tv_sec = TIME_FROM_HEADER (header->header.mtime);
|
||||
current_stat_info.atime.tv_sec = 0;
|
||||
current_stat_info.ctime.tv_sec = 0;
|
||||
if (arch->type == TAR_GNU || arch->type == TAR_OLDGNU)
|
||||
{
|
||||
st->st_atime = TIME_FROM_HEADER (header->oldgnu_header.atime);
|
||||
st->st_ctime = TIME_FROM_HEADER (header->oldgnu_header.ctime);
|
||||
current_stat_info.atime.tv_sec = TIME_FROM_HEADER (header->oldgnu_header.atime);
|
||||
current_stat_info.ctime.tv_sec = TIME_FROM_HEADER (header->oldgnu_header.ctime);
|
||||
}
|
||||
|
||||
#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
|
||||
st->st_blksize = 8 * 1024; /* FIXME */
|
||||
current_stat_info.stat.st_blksize = 8 * 1024; /* FIXME */
|
||||
#endif
|
||||
vfs_adjust_stat (st);
|
||||
vfs_adjust_stat (¤t_stat_info.stat);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------- */
|
||||
|
||||
static read_header
|
||||
tar_read_header (struct vfs_class *me, struct vfs_s_super *archive, size_t * h_size)
|
||||
tar_read_header (struct vfs_class *me, struct vfs_s_super *archive)
|
||||
{
|
||||
tar_super_t *arch = TAR_SUPER (archive);
|
||||
union block *header;
|
||||
struct stat st;
|
||||
static char *next_long_name = NULL, *next_long_link = NULL;
|
||||
read_header status;
|
||||
|
||||
@ -823,14 +888,12 @@ tar_read_header (struct vfs_class *me, struct vfs_s_super *archive, size_t * h_s
|
||||
return status;
|
||||
|
||||
if (header->header.typeflag == LNKTYPE || header->header.typeflag == DIRTYPE)
|
||||
*h_size = 0; /* Links 0 size on tape */
|
||||
current_stat_info.stat.st_size = 0; /* Links 0 size on tape */
|
||||
else
|
||||
*h_size = OFF_FROM_HEADER (header->header.size);
|
||||
current_stat_info.stat.st_size = OFF_FROM_HEADER (header->header.size);
|
||||
|
||||
memset (&st, 0, sizeof (st));
|
||||
st.st_size = *h_size;
|
||||
tar_decode_header (header, arch, &st);
|
||||
tar_fill_stat (archive, &st, header);
|
||||
tar_decode_header (header, arch);
|
||||
tar_fill_stat (archive, header);
|
||||
|
||||
/* Skip over pax extended header and global extended header records. */
|
||||
if (header->header.typeflag == XHDTYPE || header->header.typeflag == XGLTYPE)
|
||||
@ -851,7 +914,7 @@ tar_read_header (struct vfs_class *me, struct vfs_s_super *archive, size_t * h_s
|
||||
if (arch->type == TAR_UNKNOWN)
|
||||
arch->type = TAR_GNU;
|
||||
|
||||
if (*h_size > MC_MAXPATHLEN)
|
||||
if (st->st_size > MC_MAXPATHLEN)
|
||||
{
|
||||
message (D_ERROR, MSG_ERROR, _("Inconsistent tar archive"));
|
||||
return HEADER_FAILURE;
|
||||
@ -860,9 +923,9 @@ tar_read_header (struct vfs_class *me, struct vfs_s_super *archive, size_t * h_s
|
||||
longp = header->header.typeflag == GNUTYPE_LONGNAME ? &next_long_name : &next_long_link;
|
||||
|
||||
g_free (*longp);
|
||||
bp = *longp = g_malloc (*h_size + 1);
|
||||
bp = *longp = g_malloc (st->st_size + 1);
|
||||
|
||||
for (size = *h_size; size > 0; size -= written)
|
||||
for (size = st->st_size; size > 0; size -= written)
|
||||
{
|
||||
data = tar_find_next_block (arch)->buffer;
|
||||
if (data == NULL)
|
||||
@ -903,6 +966,7 @@ tar_read_header (struct vfs_class *me, struct vfs_s_super *archive, size_t * h_s
|
||||
recent_long_link =
|
||||
next_long_link != NULL ? next_long_link : g_strndup (header->header.linkname,
|
||||
sizeof (header->header.linkname));
|
||||
tar_assign_string (¤t_stat_info.link_name, recent_long_link);
|
||||
|
||||
recent_long_name = NULL;
|
||||
switch (arch->type)
|
||||
@ -948,7 +1012,9 @@ tar_read_header (struct vfs_class *me, struct vfs_s_super *archive, size_t * h_s
|
||||
recent_long_name = g_strndup (header->header.name, sizeof (header->header.name));
|
||||
}
|
||||
|
||||
tar_assign_string_dup (¤t_stat_info.orig_file_name, recent_long_name);
|
||||
canonicalize_pathname (recent_long_name);
|
||||
tar_assign_string (¤t_stat_info.file_name, recent_long_name);
|
||||
|
||||
data_position = current_tar_position;
|
||||
|
||||
@ -991,29 +1057,25 @@ tar_read_header (struct vfs_class *me, struct vfs_s_super *archive, size_t * h_s
|
||||
{
|
||||
entry = vfs_s_new_entry (me, p, inode);
|
||||
vfs_s_insert_entry (me, parent, entry);
|
||||
g_free (recent_long_link);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
if (S_ISDIR (st.st_mode))
|
||||
if (S_ISDIR (st->st_mode))
|
||||
{
|
||||
entry = VFS_SUBCLASS (me)->find_entry (me, parent, p, LINK_NO_FOLLOW, FL_NONE);
|
||||
if (entry != NULL)
|
||||
goto done;
|
||||
}
|
||||
|
||||
inode = vfs_s_new_inode (me, archive, &st);
|
||||
inode = vfs_s_new_inode (me, archive, st);
|
||||
inode->data_offset = data_position;
|
||||
|
||||
if (*recent_long_link != '\0')
|
||||
inode->linkname = recent_long_link;
|
||||
else if (recent_long_link != next_long_link)
|
||||
g_free (recent_long_link);
|
||||
if (*current_stat_info.linkname != '\0')
|
||||
inode->linkname = g_strdup (current_stat_info.link_name);
|
||||
|
||||
entry = vfs_s_new_entry (me, p, inode);
|
||||
vfs_s_insert_entry (me, parent, entry);
|
||||
g_free (recent_long_name);
|
||||
|
||||
done:
|
||||
next_long_link = next_long_name = NULL;
|
||||
@ -1036,12 +1098,19 @@ static struct vfs_s_super *
|
||||
tar_new_archive (struct vfs_class *me)
|
||||
{
|
||||
tar_super_t *arch;
|
||||
gint64 usec;
|
||||
|
||||
arch = g_new0 (tar_super_t, 1);
|
||||
arch->base.me = me;
|
||||
arch->fd = -1;
|
||||
arch->type = TAR_UNKNOWN;
|
||||
|
||||
/* time in microseconds */
|
||||
usec = g_get_real_time ();
|
||||
/* time in seconds and nanoseconds */
|
||||
start_time.tv_sec = usec / G_USEC_PER_SEC;
|
||||
start_time.tv_nsec = (usec % G_USEC_PER_SEC) * 1000;
|
||||
|
||||
return VFS_SUPER (arch);
|
||||
}
|
||||
|
||||
@ -1059,6 +1128,8 @@ tar_free_archive (struct vfs_class *me, struct vfs_s_super *archive)
|
||||
mc_close (arch->fd);
|
||||
arch->fd = -1;
|
||||
}
|
||||
|
||||
tar_stat_destroy (¤t_stat_info);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------- */
|
||||
@ -1147,11 +1218,11 @@ tar_open_archive (struct vfs_s_super *archive, const vfs_path_t * vpath,
|
||||
|
||||
while (TRUE)
|
||||
{
|
||||
size_t h_size = 0;
|
||||
read_header prev_status;
|
||||
|
||||
prev_status = status;
|
||||
status = tar_read_header (vpath_element->class, archive, &h_size);
|
||||
tar_stat_destroy (¤t_stat_info);
|
||||
status = tar_read_header (vpath_element->class, archive);
|
||||
|
||||
switch (status)
|
||||
{
|
||||
@ -1161,7 +1232,8 @@ tar_open_archive (struct vfs_s_super *archive, const vfs_path_t * vpath,
|
||||
return -1;
|
||||
|
||||
case HEADER_SUCCESS:
|
||||
tar_skip_n_records (archive, (h_size + BLOCKSIZE - 1) / BLOCKSIZE);
|
||||
tar_skip_n_records (archive,
|
||||
(current_stat_info.stat.st_size + BLOCKSIZE - 1) / BLOCKSIZE);
|
||||
continue;
|
||||
|
||||
/*
|
||||
|
Loading…
Reference in New Issue
Block a user