Fixes to SYSV file system support (inode.c)

Michal Jaegermann (michal@ellpspace.math.ualberta.ca)
Sat, 11 Dec 1999 17:52:50 -0700 (MST)


Recently I had to get a data from an old Xenix disk and in the process
it turned out that a support for SYSV FILESYSTEM, although cleaned up
to a great extent from 2.1 kernel times, still has troubles. It
probably works mostly ok on Intel with this exception that a reported
length of file names will likely be 0 which precludes reading of
anything. On 64-bit Alpha one is getting not only tons of "unaligned
access" due to some "optimizations" but also, from time to time,
incorrect values. The later fills with garbage, in every file, 1024
byte blocs starting at offsets 1024 (0x400) and 9216 (0x2400). Bigger
directories are also not something to write home about. :-)
I would not even try to write on such file system but in all likelihood
such operation would be totally destructive.

Included below is a patch which allowed me to recover correctly all
data from a file system in question (some 150 MB worth). It is against
linux-2.2.14pre12 but it should be all right (maybe with some fuzz)
for all 2.2 kernels and likely for 2.3 as well.

Despite of that there is still something which does not look right.
The original code for 'coh_read3byte()' reads:

return (unsigned long)(*(unsigned char *)p) << 16
| (unsigned long)(*(unsigned short *)(p+1));

Putting aside all other issues what is returned obviously depends
on BIG or LITTLE ENDIAN representation for short and is not the same.
A similar issue with 'coh_write3byte()'. It is not clear to me what
was **supposed** to be there so I did not try to second guess
BIG ENDIAN fixes. Alpha happens to be LITTLE ENDIAN. Comments?
My replacements are not worse from what was there before but
this does not say very much.

In the course of this project I also ported to 2.2 kernels Peter Swain
patches (http://www.iinet.net.au/~swine/sco_fs) to extend SYSV support
to its SCO variant including "divvy" subpartitions. I had to do that
or data would be lost. :-) Support for "divvies" is somewhat hacky;
one has to find proper offsets (I just searched through a disk looking
for something which looked like an SCO superblock) and use search
results to mount "divvies" via loop. Once proper parametes were found
everything works, with a fixed SYSV support, as expected. I am not
sure what to do with that. Any interest?

Michal
michal@harddata.com

Patch follows:

--- linux-2.2.14pre12/fs/sysv/inode.c.sysv Sun Mar 21 08:11:37 1999
+++ linux-2.2.14pre12/fs/sysv/inode.c Sat Dec 11 17:02:17 1999
@@ -41,14 +41,14 @@
" sz %lu blks %lu cnt %u\n",
inode->i_ino, inode->i_mode, inode->i_nlink, inode->i_uid,
inode->i_gid, inode->i_size, inode->i_blocks, inode->i_count);
- printk(" db <0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx"
- " 0x%lx 0x%lx>\n",
+ printk(" db <0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x"
+ " 0x%x 0x%x>\n",
inode->u.sysv_i.i_data[0], inode->u.sysv_i.i_data[1],
inode->u.sysv_i.i_data[2], inode->u.sysv_i.i_data[3],
inode->u.sysv_i.i_data[4], inode->u.sysv_i.i_data[5],
inode->u.sysv_i.i_data[6], inode->u.sysv_i.i_data[7],
inode->u.sysv_i.i_data[8], inode->u.sysv_i.i_data[9]);
- printk(" ib <0x%lx 0x%lx 0x%lx>\n",
+ printk(" ib <0x%x 0x%x 0x%x>\n",
inode->u.sysv_i.i_data[10],
inode->u.sysv_i.i_data[11],
inode->u.sysv_i.i_data[12]);
@@ -166,6 +166,7 @@
sb->sv_firstdatazone = sbd1->s_isize;
sb->sv_nzones = sbd1->s_fsize;
sb->sv_ndatazones = sb->sv_nzones - sb->sv_firstdatazone;
+ sb->sv_namelen = SYSV_NAMELEN;
return sb;
}

@@ -234,6 +235,7 @@
sb->sv_firstdatazone = sbd->s_isize;
sb->sv_nzones = sbd->s_fsize;
sb->sv_ndatazones = sb->sv_nzones - sb->sv_firstdatazone;
+ sb->sv_namelen = SYSV_NAMELEN;
return sb;
}

@@ -289,6 +291,7 @@
sb->sv_firstdatazone = sbd->s_isize;
sb->sv_nzones = sbd->s_fsize;
sb->sv_ndatazones = sb->sv_nzones - sb->sv_firstdatazone;
+ sb->sv_namelen = SYSV_NAMELEN;
return sb;
}

@@ -335,6 +338,7 @@
sb->sv_firstdatazone = sbd->s_isize;
sb->sv_nzones = from_coh_ulong(sbd->s_fsize);
sb->sv_ndatazones = sb->sv_nzones - sb->sv_firstdatazone;
+ sb->sv_namelen = SYSV_NAMELEN;
return sb;
}

@@ -796,44 +800,56 @@

#ifdef __BIG_ENDIAN

-static inline unsigned long read3byte (unsigned char * p)
+static inline u32 read3byte (unsigned char * p)
{
- return (p[2] | (p[1]<<8) | (p[0]<<16));
+ u32 ret = *p++;
+ ret = (ret << 8) | (*p++);
+ return (ret << 8 | *p);
}

-static inline void write3byte (unsigned char *p , unsigned long val)
+static inline void write3byte (unsigned char *p , u32 val)
{
- p[2]=val&0xFF;
- p[1]=(val>>8)&0xFF;
- p[0]=(val>>16)&0xFF;
+ p[2]=val;
+ p[1]=(val>>8);
+ p[0]=(val>>16);
}

#else

-static inline unsigned long read3byte (unsigned char * p)
+static inline u32 read3byte (unsigned char * p)
{
- return (unsigned long)(*(unsigned short *)p)
- | (unsigned long)(*(unsigned char *)(p+2)) << 16;
-}
-
-static inline void write3byte (unsigned char * p, unsigned long val)
-{
- *(unsigned short *)p = (unsigned short) val;
- *(unsigned char *)(p+2) = val >> 16;
+ u32 ret = *(p + 2);
+ ret <<= 8;
+ ret |= *(p + 1);
+ ret <<= 8;
+ ret |= *p;
+ return (ret);
+}
+
+static inline void write3byte (unsigned char * p, u32 val)
+{
+ *p++ = val;
+ val >>= 8;
+ *p++ = val;
+ val >>= 8;
+ *p = val;
}

#endif

-static inline unsigned long coh_read3byte (unsigned char * p)
+static inline u32 coh_read3byte (unsigned char * p)
{
- return (unsigned long)(*(unsigned char *)p) << 16
- | (unsigned long)(*(unsigned short *)(p+1));
+ u32 ret = *p++;
+ ret <<= 16;
+ ret += p[0] + (p[1] << 8);
+ return ret;
}

-static inline void coh_write3byte (unsigned char * p, unsigned long val)
+static inline void coh_write3byte (unsigned char * p, u32 val)
{
- *(unsigned char *)p = val >> 16;
- *(unsigned short *)(p+1) = (unsigned short) val;
+ p[0] = val >> 16;
+ p[1] = val;
+ p[2] = (val >> 8);
}

void sysv_read_inode(struct inode * inode)

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/