diff mbox series

isofs: handle CDs with bad root inode but good Joliet root directory

Message ID 20240208022134.451490-1-alexhenrie24@gmail.com (mailing list archive)
State New
Headers show
Series isofs: handle CDs with bad root inode but good Joliet root directory | expand

Commit Message

Alex Henrie Feb. 8, 2024, 2:21 a.m. UTC
I have a CD copy of the original Tom Clancy's Ghost Recon game from
2001. The disc mounts without error on Windows, but on Linux mounting
fails with the message "isofs_fill_super: get root inode failed". The
error originates in isofs_read_inode, which returns -EIO because de_len
is 0. The superblock on this disc appears to be intentionally corrupt as
a form of copy protection.

When the root inode is unusable, instead of giving up immediately, try
to continue with the Joliet file table. This fixes the Ghost Recon CD
and probably other copy-protected CDs too.

Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>
---
 fs/isofs/inode.c | 21 ++++++++++++++++++---
 1 file changed, 18 insertions(+), 3 deletions(-)

Comments

Jan Kara Feb. 8, 2024, 10:59 a.m. UTC | #1
On Wed 07-02-24 19:21:32, Alex Henrie wrote:
> I have a CD copy of the original Tom Clancy's Ghost Recon game from
> 2001. The disc mounts without error on Windows, but on Linux mounting
> fails with the message "isofs_fill_super: get root inode failed". The
> error originates in isofs_read_inode, which returns -EIO because de_len
> is 0. The superblock on this disc appears to be intentionally corrupt as
> a form of copy protection.
> 
> When the root inode is unusable, instead of giving up immediately, try
> to continue with the Joliet file table. This fixes the Ghost Recon CD
> and probably other copy-protected CDs too.
> 
> Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>

Thanks! I've added the patch to my tree. Just made two minor tweaks on
commit:

> @@ -908,8 +908,22 @@ static int isofs_fill_super(struct super_block *s, void *data, int silent)
>  	 * we then decide whether to use the Joliet descriptor.
>  	 */
>  	inode = isofs_iget(s, sbi->s_firstdatazone, 0);
> -	if (IS_ERR(inode))
> -		goto out_no_root;
> +
> +	/*
> +	 * Fix for broken CDs with a corrupt root inode but a correct Joliet
> +	 * root directory.
> +	 */
> +	if (IS_ERR(inode)) {
> +		if (joliet_level) {

Here I've added "&& sbi->s_firstdatazone != first_data_zone" to make sure
joliet extension has a different inode. Not sure if such media would be
valid but even if it was not, we should not crash the kernel (which would
happen in that case because we don't expect inode to be NULL).

> +			printk(KERN_NOTICE
> +				"ISOFS: root inode is unusable. "
> +				"Disabling Rock Ridge and switching to Joliet.");
> +			sbi->s_rock = 0;
> +			inode = NULL;
> +		} else {
> +			goto out_no_root;
> +		}
> +	}
>  
>  	/*
>  	 * Fix for broken CDs with Rock Ridge and empty ISO root directory but
> @@ -939,7 +953,8 @@ static int isofs_fill_super(struct super_block *s, void *data, int silent)
>  			sbi->s_firstdatazone = first_data_zone;
>  			printk(KERN_DEBUG
>  				"ISOFS: changing to secondary root\n");
> -			iput(inode);
> +			if (inode != NULL)
> +				iput(inode);

This isn't needed. iput() handles NULL inode just fine.

								Honza
Alex Henrie Feb. 9, 2024, 6:04 a.m. UTC | #2
On Thu, Feb 8, 2024 at 3:59 AM Jan Kara <jack@suse.cz> wrote:
>
> On Wed 07-02-24 19:21:32, Alex Henrie wrote:
> > I have a CD copy of the original Tom Clancy's Ghost Recon game from
> > 2001. The disc mounts without error on Windows, but on Linux mounting
> > fails with the message "isofs_fill_super: get root inode failed". The
> > error originates in isofs_read_inode, which returns -EIO because de_len
> > is 0. The superblock on this disc appears to be intentionally corrupt as
> > a form of copy protection.
> >
> > When the root inode is unusable, instead of giving up immediately, try
> > to continue with the Joliet file table. This fixes the Ghost Recon CD
> > and probably other copy-protected CDs too.
> >
> > Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>
>
> Thanks! I've added the patch to my tree. Just made two minor tweaks on
> commit:
>
> > @@ -908,8 +908,22 @@ static int isofs_fill_super(struct super_block *s, void *data, int silent)
> >        * we then decide whether to use the Joliet descriptor.
> >        */
> >       inode = isofs_iget(s, sbi->s_firstdatazone, 0);
> > -     if (IS_ERR(inode))
> > -             goto out_no_root;
> > +
> > +     /*
> > +      * Fix for broken CDs with a corrupt root inode but a correct Joliet
> > +      * root directory.
> > +      */
> > +     if (IS_ERR(inode)) {
> > +             if (joliet_level) {
>
> Here I've added "&& sbi->s_firstdatazone != first_data_zone" to make sure
> joliet extension has a different inode. Not sure if such media would be
> valid but even if it was not, we should not crash the kernel (which would
> happen in that case because we don't expect inode to be NULL).
>
> > +                     printk(KERN_NOTICE
> > +                             "ISOFS: root inode is unusable. "
> > +                             "Disabling Rock Ridge and switching to Joliet.");
> > +                     sbi->s_rock = 0;
> > +                     inode = NULL;
> > +             } else {
> > +                     goto out_no_root;
> > +             }
> > +     }
> >
> >       /*
> >        * Fix for broken CDs with Rock Ridge and empty ISO root directory but
> > @@ -939,7 +953,8 @@ static int isofs_fill_super(struct super_block *s, void *data, int silent)
> >                       sbi->s_firstdatazone = first_data_zone;
> >                       printk(KERN_DEBUG
> >                               "ISOFS: changing to secondary root\n");
> > -                     iput(inode);
> > +                     if (inode != NULL)
> > +                             iput(inode);
>
> This isn't needed. iput() handles NULL inode just fine.

I tried out your two changes and the patch still works great. Thank
you for noticing and fixing those details, and thanks for the quick
review!

-Alex
diff mbox series

Patch

diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c
index 3e4d53e26f94..86a767fa1e16 100644
--- a/fs/isofs/inode.c
+++ b/fs/isofs/inode.c
@@ -908,8 +908,22 @@  static int isofs_fill_super(struct super_block *s, void *data, int silent)
 	 * we then decide whether to use the Joliet descriptor.
 	 */
 	inode = isofs_iget(s, sbi->s_firstdatazone, 0);
-	if (IS_ERR(inode))
-		goto out_no_root;
+
+	/*
+	 * Fix for broken CDs with a corrupt root inode but a correct Joliet
+	 * root directory.
+	 */
+	if (IS_ERR(inode)) {
+		if (joliet_level) {
+			printk(KERN_NOTICE
+				"ISOFS: root inode is unusable. "
+				"Disabling Rock Ridge and switching to Joliet.");
+			sbi->s_rock = 0;
+			inode = NULL;
+		} else {
+			goto out_no_root;
+		}
+	}
 
 	/*
 	 * Fix for broken CDs with Rock Ridge and empty ISO root directory but
@@ -939,7 +953,8 @@  static int isofs_fill_super(struct super_block *s, void *data, int silent)
 			sbi->s_firstdatazone = first_data_zone;
 			printk(KERN_DEBUG
 				"ISOFS: changing to secondary root\n");
-			iput(inode);
+			if (inode != NULL)
+				iput(inode);
 			inode = isofs_iget(s, sbi->s_firstdatazone, 0);
 			if (IS_ERR(inode))
 				goto out_no_root;