mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-09-04 20:19:47 +08:00
isofs: fix Y2038 and Y2156 issues in Rock Ridge TF entry
This change implements the Rock Ridge TF entry LONG_FORM bit, which uses the ISO 9660 17-byte date format (up to year 9999, with 10ms precision) instead of the 7-byte date format (up to year 2155, with 1s precision). Previously the LONG_FORM bit was ignored; and isofs would entirely misinterpret the date as the wrong format, resulting in garbage timestamps on the filesystem. The Y2038 issue in iso_date() is fixed by returning a struct timespec64 instead of an int. parse_rock_ridge_inode_internal() is fixed so it does proper bounds checks of the TF entry timestamps. Signed-off-by: Jonas 'Sortie' Termansen <sortie@maxsi.org> Signed-off-by: Jan Kara <jack@suse.cz> Link: https://patch.msgid.link/20250411145022.2292255-1-sortie@maxsi.org
This commit is contained in:
parent
0405d4b63d
commit
5ea45f54c8
@ -1275,6 +1275,7 @@ static int isofs_read_inode(struct inode *inode, int relocated)
|
|||||||
unsigned long offset;
|
unsigned long offset;
|
||||||
struct iso_inode_info *ei = ISOFS_I(inode);
|
struct iso_inode_info *ei = ISOFS_I(inode);
|
||||||
int ret = -EIO;
|
int ret = -EIO;
|
||||||
|
struct timespec64 ts;
|
||||||
|
|
||||||
block = ei->i_iget5_block;
|
block = ei->i_iget5_block;
|
||||||
bh = sb_bread(inode->i_sb, block);
|
bh = sb_bread(inode->i_sb, block);
|
||||||
@ -1387,8 +1388,10 @@ static int isofs_read_inode(struct inode *inode, int relocated)
|
|||||||
inode->i_ino, de->flags[-high_sierra]);
|
inode->i_ino, de->flags[-high_sierra]);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
inode_set_mtime_to_ts(inode,
|
ts = iso_date(de->date, high_sierra ? ISO_DATE_HIGH_SIERRA : 0);
|
||||||
inode_set_atime_to_ts(inode, inode_set_ctime(inode, iso_date(de->date, high_sierra), 0)));
|
inode_set_ctime_to_ts(inode, ts);
|
||||||
|
inode_set_atime_to_ts(inode, ts);
|
||||||
|
inode_set_mtime_to_ts(inode, ts);
|
||||||
|
|
||||||
ei->i_first_extent = (isonum_733(de->extent) +
|
ei->i_first_extent = (isonum_733(de->extent) +
|
||||||
isonum_711(de->ext_attr_length));
|
isonum_711(de->ext_attr_length));
|
||||||
|
@ -106,7 +106,9 @@ static inline unsigned int isonum_733(u8 *p)
|
|||||||
/* Ignore bigendian datum due to broken mastering programs */
|
/* Ignore bigendian datum due to broken mastering programs */
|
||||||
return get_unaligned_le32(p);
|
return get_unaligned_le32(p);
|
||||||
}
|
}
|
||||||
extern int iso_date(u8 *, int);
|
#define ISO_DATE_HIGH_SIERRA (1 << 0)
|
||||||
|
#define ISO_DATE_LONG_FORM (1 << 1)
|
||||||
|
struct timespec64 iso_date(u8 *p, int flags);
|
||||||
|
|
||||||
struct inode; /* To make gcc happy */
|
struct inode; /* To make gcc happy */
|
||||||
|
|
||||||
|
@ -412,7 +412,12 @@ repeat:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SIG('T', 'F'):
|
case SIG('T', 'F'): {
|
||||||
|
int flags, size, slen;
|
||||||
|
|
||||||
|
flags = rr->u.TF.flags & TF_LONG_FORM ? ISO_DATE_LONG_FORM : 0;
|
||||||
|
size = rr->u.TF.flags & TF_LONG_FORM ? 17 : 7;
|
||||||
|
slen = rr->len - 5;
|
||||||
/*
|
/*
|
||||||
* Some RRIP writers incorrectly place ctime in the
|
* Some RRIP writers incorrectly place ctime in the
|
||||||
* TF_CREATE field. Try to handle this correctly for
|
* TF_CREATE field. Try to handle this correctly for
|
||||||
@ -420,27 +425,28 @@ repeat:
|
|||||||
*/
|
*/
|
||||||
/* Rock ridge never appears on a High Sierra disk */
|
/* Rock ridge never appears on a High Sierra disk */
|
||||||
cnt = 0;
|
cnt = 0;
|
||||||
if (rr->u.TF.flags & TF_CREATE) {
|
if ((rr->u.TF.flags & TF_CREATE) && size <= slen) {
|
||||||
inode_set_ctime(inode,
|
inode_set_ctime_to_ts(inode,
|
||||||
iso_date(rr->u.TF.times[cnt++].time, 0),
|
iso_date(rr->u.TF.data + size * cnt++, flags));
|
||||||
0);
|
slen -= size;
|
||||||
}
|
}
|
||||||
if (rr->u.TF.flags & TF_MODIFY) {
|
if ((rr->u.TF.flags & TF_MODIFY) && size <= slen) {
|
||||||
inode_set_mtime(inode,
|
inode_set_mtime_to_ts(inode,
|
||||||
iso_date(rr->u.TF.times[cnt++].time, 0),
|
iso_date(rr->u.TF.data + size * cnt++, flags));
|
||||||
0);
|
slen -= size;
|
||||||
}
|
}
|
||||||
if (rr->u.TF.flags & TF_ACCESS) {
|
if ((rr->u.TF.flags & TF_ACCESS) && size <= slen) {
|
||||||
inode_set_atime(inode,
|
inode_set_atime_to_ts(inode,
|
||||||
iso_date(rr->u.TF.times[cnt++].time, 0),
|
iso_date(rr->u.TF.data + size * cnt++, flags));
|
||||||
0);
|
slen -= size;
|
||||||
}
|
}
|
||||||
if (rr->u.TF.flags & TF_ATTRIBUTES) {
|
if ((rr->u.TF.flags & TF_ATTRIBUTES) && size <= slen) {
|
||||||
inode_set_ctime(inode,
|
inode_set_ctime_to_ts(inode,
|
||||||
iso_date(rr->u.TF.times[cnt++].time, 0),
|
iso_date(rr->u.TF.data + size * cnt++, flags));
|
||||||
0);
|
slen -= size;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case SIG('S', 'L'):
|
case SIG('S', 'L'):
|
||||||
{
|
{
|
||||||
int slen;
|
int slen;
|
||||||
|
@ -65,13 +65,9 @@ struct RR_PL_s {
|
|||||||
__u8 location[8];
|
__u8 location[8];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct stamp {
|
|
||||||
__u8 time[7]; /* actually 6 unsigned, 1 signed */
|
|
||||||
} __attribute__ ((packed));
|
|
||||||
|
|
||||||
struct RR_TF_s {
|
struct RR_TF_s {
|
||||||
__u8 flags;
|
__u8 flags;
|
||||||
struct stamp times[]; /* Variable number of these beasts */
|
__u8 data[];
|
||||||
} __attribute__ ((packed));
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
/* Linux-specific extension for transparent decompression */
|
/* Linux-specific extension for transparent decompression */
|
||||||
|
@ -16,24 +16,39 @@
|
|||||||
* to GMT. Thus we should always be correct.
|
* to GMT. Thus we should always be correct.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int iso_date(u8 *p, int flag)
|
struct timespec64 iso_date(u8 *p, int flags)
|
||||||
{
|
{
|
||||||
int year, month, day, hour, minute, second, tz;
|
int year, month, day, hour, minute, second, tz;
|
||||||
int crtime;
|
struct timespec64 ts;
|
||||||
|
|
||||||
|
if (flags & ISO_DATE_LONG_FORM) {
|
||||||
|
year = (p[0] - '0') * 1000 +
|
||||||
|
(p[1] - '0') * 100 +
|
||||||
|
(p[2] - '0') * 10 +
|
||||||
|
(p[3] - '0') - 1900;
|
||||||
|
month = ((p[4] - '0') * 10 + (p[5] - '0'));
|
||||||
|
day = ((p[6] - '0') * 10 + (p[7] - '0'));
|
||||||
|
hour = ((p[8] - '0') * 10 + (p[9] - '0'));
|
||||||
|
minute = ((p[10] - '0') * 10 + (p[11] - '0'));
|
||||||
|
second = ((p[12] - '0') * 10 + (p[13] - '0'));
|
||||||
|
ts.tv_nsec = ((p[14] - '0') * 10 + (p[15] - '0')) * 10000000;
|
||||||
|
tz = p[16];
|
||||||
|
} else {
|
||||||
year = p[0];
|
year = p[0];
|
||||||
month = p[1];
|
month = p[1];
|
||||||
day = p[2];
|
day = p[2];
|
||||||
hour = p[3];
|
hour = p[3];
|
||||||
minute = p[4];
|
minute = p[4];
|
||||||
second = p[5];
|
second = p[5];
|
||||||
if (flag == 0) tz = p[6]; /* High sierra has no time zone */
|
ts.tv_nsec = 0;
|
||||||
else tz = 0;
|
/* High sierra has no time zone */
|
||||||
|
tz = flags & ISO_DATE_HIGH_SIERRA ? 0 : p[6];
|
||||||
|
}
|
||||||
|
|
||||||
if (year < 0) {
|
if (year < 0) {
|
||||||
crtime = 0;
|
ts.tv_sec = 0;
|
||||||
} else {
|
} else {
|
||||||
crtime = mktime64(year+1900, month, day, hour, minute, second);
|
ts.tv_sec = mktime64(year+1900, month, day, hour, minute, second);
|
||||||
|
|
||||||
/* sign extend */
|
/* sign extend */
|
||||||
if (tz & 0x80)
|
if (tz & 0x80)
|
||||||
@ -65,7 +80,7 @@ int iso_date(u8 *p, int flag)
|
|||||||
* for pointing out the sign error.
|
* for pointing out the sign error.
|
||||||
*/
|
*/
|
||||||
if (-52 <= tz && tz <= 52)
|
if (-52 <= tz && tz <= 52)
|
||||||
crtime -= tz * 15 * 60;
|
ts.tv_sec -= tz * 15 * 60;
|
||||||
}
|
}
|
||||||
return crtime;
|
return ts;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user