On Fri 11-04-25 16:50:21, Jonas 'Sortie' Termansen wrote: > 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@xxxxxxxxx> Thanks for the patch! It looks good. I'll merge it through my tree. Honza > --- > I tested this change by making two .isos: > > * A normal .iso with Rock Ridge TF without LONG_FORM made with xorriso > * A long-form .iso with Rock Ridge TF LONG_FORM made with a patched xorriso > > Each containing these test files: > > -rw-rw-r-- 1 root root 0 Apr 11 16:20 Y2025 > -rw-rw-r-- 1 root root 0 Jun 1 2038 Y2038 > -rw-rw-r-- 1 root root 0 Jun 1 2155 Y2155 > -rw-rw-r-- 1 root root 0 Jun 1 2156 Y2156 > > The normal Rock Ridge .iso was made with xorriso, and a long form .iso was > made with a custom xorriso patched to output Rock Ridge TF LONG_FORM. > > On Linux master mounting an iso without LONG_FORM: > > -r--r--r-- 1 root root 0 Apr 11 16:20 Y2025 > -r--r--r-- 1 root root 0 Apr 25 1902 Y2038 > -r--r--r-- 1 root root 0 Apr 24 2019 Y2155 > -r--r--r-- 1 root root 0 Nov 24 2019 Y2156 > > (Y2025 is correct; Y2038, Y2155, and Y2156 are garbage) > > On Linux master mounting an iso with LONG_FORM: > > -r--r--r-- 1 root root 0 Jan 25 1954 Y2025 > -r--r--r-- 1 root root 0 Jan 26 1954 Y2038 > -r--r--r-- 1 root root 0 Feb 28 1954 Y2155 > -r--r--r-- 1 root root 0 Feb 28 1954 Y2156 > > (All timestamps are garbage) > > With this patch, mounting an iso without LONG_FORM: > > -r--r--r-- 1 root root 0 Apr 11 16:20 Y2025 > -r--r--r-- 1 root root 0 Jun 1 2038 Y2038 > -r--r--r-- 1 root root 0 Jun 1 2155 Y2155 > -r--r--r-- 1 root root 0 Jan 1 2156 Y2156 > > (Y2025, Y2038, and Y2155 are correct; > Y2156 was correctly truncated by xorriso) > > With this patch, mounting an iso with LONG_FORM: > > -r--r--r-- 1 root root 0 Apr 11 16:20 Y2025 > -r--r--r-- 1 root root 0 Jun 1 2038 Y2038 > -r--r--r-- 1 root root 0 Jun 1 2155 Y2155 > -r--r--r-- 1 root root 0 Jun 1 2156 Y2156 > > (All timestamps are correct) > --- > fs/isofs/inode.c | 7 +++++-- > fs/isofs/isofs.h | 4 +++- > fs/isofs/rock.c | 40 ++++++++++++++++++++++----------------- > fs/isofs/rock.h | 6 +----- > fs/isofs/util.c | 49 +++++++++++++++++++++++++++++++----------------- > 5 files changed, 64 insertions(+), 42 deletions(-) > > diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c > index 47038e660812..d5da9817df9b 100644 > --- a/fs/isofs/inode.c > +++ b/fs/isofs/inode.c > @@ -1275,6 +1275,7 @@ static int isofs_read_inode(struct inode *inode, int relocated) > unsigned long offset; > struct iso_inode_info *ei = ISOFS_I(inode); > int ret = -EIO; > + struct timespec64 ts; > > block = ei->i_iget5_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]); > } > #endif > - inode_set_mtime_to_ts(inode, > - inode_set_atime_to_ts(inode, inode_set_ctime(inode, iso_date(de->date, high_sierra), 0))); > + ts = iso_date(de->date, high_sierra ? ISO_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) + > isonum_711(de->ext_attr_length)); > diff --git a/fs/isofs/isofs.h b/fs/isofs/isofs.h > index 2d55207c9a99..506555837533 100644 > --- a/fs/isofs/isofs.h > +++ b/fs/isofs/isofs.h > @@ -106,7 +106,9 @@ static inline unsigned int isonum_733(u8 *p) > /* Ignore bigendian datum due to broken mastering programs */ > 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 */ > > diff --git a/fs/isofs/rock.c b/fs/isofs/rock.c > index dbf911126e61..576498245b9d 100644 > --- a/fs/isofs/rock.c > +++ b/fs/isofs/rock.c > @@ -412,7 +412,12 @@ parse_rock_ridge_inode_internal(struct iso_directory_record *de, > } > } > 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 > * TF_CREATE field. Try to handle this correctly for > @@ -420,27 +425,28 @@ parse_rock_ridge_inode_internal(struct iso_directory_record *de, > */ > /* Rock ridge never appears on a High Sierra disk */ > cnt = 0; > - if (rr->u.TF.flags & TF_CREATE) { > - inode_set_ctime(inode, > - iso_date(rr->u.TF.times[cnt++].time, 0), > - 0); > + if ((rr->u.TF.flags & TF_CREATE) && size <= slen) { > + inode_set_ctime_to_ts(inode, > + iso_date(rr->u.TF.data + size * cnt++, flags)); > + slen -= size; > } > - if (rr->u.TF.flags & TF_MODIFY) { > - inode_set_mtime(inode, > - iso_date(rr->u.TF.times[cnt++].time, 0), > - 0); > + if ((rr->u.TF.flags & TF_MODIFY) && size <= slen) { > + inode_set_mtime_to_ts(inode, > + iso_date(rr->u.TF.data + size * cnt++, flags)); > + slen -= size; > } > - if (rr->u.TF.flags & TF_ACCESS) { > - inode_set_atime(inode, > - iso_date(rr->u.TF.times[cnt++].time, 0), > - 0); > + if ((rr->u.TF.flags & TF_ACCESS) && size <= slen) { > + inode_set_atime_to_ts(inode, > + iso_date(rr->u.TF.data + size * cnt++, flags)); > + slen -= size; > } > - if (rr->u.TF.flags & TF_ATTRIBUTES) { > - inode_set_ctime(inode, > - iso_date(rr->u.TF.times[cnt++].time, 0), > - 0); > + if ((rr->u.TF.flags & TF_ATTRIBUTES) && size <= slen) { > + inode_set_ctime_to_ts(inode, > + iso_date(rr->u.TF.data + size * cnt++, flags)); > + slen -= size; > } > break; > + } > case SIG('S', 'L'): > { > int slen; > diff --git a/fs/isofs/rock.h b/fs/isofs/rock.h > index 7755e587f778..c0856fa9bb6a 100644 > --- a/fs/isofs/rock.h > +++ b/fs/isofs/rock.h > @@ -65,13 +65,9 @@ struct RR_PL_s { > __u8 location[8]; > }; > > -struct stamp { > - __u8 time[7]; /* actually 6 unsigned, 1 signed */ > -} __attribute__ ((packed)); > - > struct RR_TF_s { > __u8 flags; > - struct stamp times[]; /* Variable number of these beasts */ > + __u8 data[]; > } __attribute__ ((packed)); > > /* Linux-specific extension for transparent decompression */ > diff --git a/fs/isofs/util.c b/fs/isofs/util.c > index e88dba721661..42f479da0b28 100644 > --- a/fs/isofs/util.c > +++ b/fs/isofs/util.c > @@ -16,29 +16,44 @@ > * 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 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]; > + month = p[1]; > + day = p[2]; > + hour = p[3]; > + minute = p[4]; > + second = p[5]; > + ts.tv_nsec = 0; > + /* High sierra has no time zone */ > + tz = flags & ISO_DATE_HIGH_SIERRA ? 0 : p[6]; > + } > > - year = p[0]; > - month = p[1]; > - day = p[2]; > - hour = p[3]; > - minute = p[4]; > - second = p[5]; > - if (flag == 0) tz = p[6]; /* High sierra has no time zone */ > - else tz = 0; > - > if (year < 0) { > - crtime = 0; > + ts.tv_sec = 0; > } else { > - crtime = mktime64(year+1900, month, day, hour, minute, second); > + ts.tv_sec = mktime64(year+1900, month, day, hour, minute, second); > > /* sign extend */ > if (tz & 0x80) > tz |= (-1 << 8); > - > + > /* > * The timezone offset is unreliable on some disks, > * so we make a sanity check. In no case is it ever > @@ -65,7 +80,7 @@ int iso_date(u8 *p, int flag) > * for pointing out the sign error. > */ > if (-52 <= tz && tz <= 52) > - crtime -= tz * 15 * 60; > + ts.tv_sec -= tz * 15 * 60; > } > - return crtime; > -} > + return ts; > +} > -- > 2.47.2 > -- Jan Kara <jack@xxxxxxxx> SUSE Labs, CR