aboutsummaryrefslogtreecommitdiffstats
path: root/src/dstacker_alloc.c
diff options
context:
space:
mode:
authorcc4b05f61e2d8f77114750386c9f9a60 <cc4b05f61e2d8f7@7114750386c9f9a60>2023-05-11 14:38:47 +0000
committercc4b05f61e2d8f77114750386c9f9a60 <cc4b05f61e2d8f7@7114750386c9f9a60>2023-05-11 14:38:47 +0000
commitf5450bfd35a6410528d124f534c2b1a958cafe51 (patch)
treea808b12d6ad5343fabdec7b8918df6b4d844e03f /src/dstacker_alloc.c
parent5ad2bb7a6ac7e97c031908d2439808a00fff6214 (diff)
downloaddmsdosnow-dmsdos-0.9.2.2.tar.gz
dmsdos-0.9.2.2 addeddmsdos-0.9.2.2
Diffstat (limited to 'src/dstacker_alloc.c')
-rw-r--r--src/dstacker_alloc.c477
1 files changed, 477 insertions, 0 deletions
diff --git a/src/dstacker_alloc.c b/src/dstacker_alloc.c
new file mode 100644
index 0000000..a0b3762
--- /dev/null
+++ b/src/dstacker_alloc.c
@@ -0,0 +1,477 @@
+/*
+dstacker_alloc.c
+
+DMSDOS CVF-FAT module: stacker allocation routines.
+
+******************************************************************************
+DMSDOS (compressed MSDOS filesystem support) for Linux
+written 1995-1998 by Frank Gockel and Pavel Pisa
+
+ (C) Copyright 1995-1998 by Frank Gockel
+ (C) Copyright 1996-1998 by Pavel Pisa
+
+Some code of dmsdos has been copied from the msdos filesystem
+so there are the following additional copyrights:
+
+ (C) Copyright 1992,1993 by Werner Almesberger (msdos filesystem)
+ (C) Copyright 1994,1995 by Jacques Gelinas (mmap code)
+ (C) Copyright 1992-1995 by Linus Torvalds
+
+DMSDOS was inspired by the THS filesystem (a simple doublespace
+DS-0-2 compressed read-only filesystem) written 1994 by Thomas Scheuermann.
+
+The DMSDOS code is distributed under the Gnu General Public Licence.
+See file COPYING for details.
+******************************************************************************
+
+*/
+
+#ifdef __KERNEL__
+#include <linux/sched.h>
+#include <linux/ctype.h>
+#include <linux/major.h>
+#include <linux/blkdev.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/locks.h>
+#include <asm/segment.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/msdos_fs.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/shm.h>
+#include <linux/mman.h>
+#include <asm/system.h>
+#endif
+
+#include "dmsdos.h"
+
+#ifdef __DMSDOS_LIB__
+/* some interface hacks */
+#include"lib_interface.h"
+#include<malloc.h>
+#include<errno.h>
+#endif
+
+#ifdef DMSDOS_CONFIG_STAC
+
+/* initializes Stac_cwalk structure, which can be used for sequential
+ access to all sectors of cluster and when needed informs about
+ all data areas in every sector. flg parameter speedups initialization
+ when some informations are not necessarry
+ flg = 0 .. only sector numbers
+ flg = 1 .. sector numbers and data without ending suballocation
+ flg = 2 .. sector numbers and exact data areas
+ flg = 3 .. same as 2 but more checking
+*/
+int stac_cwalk_init(Stac_cwalk *cw,struct super_block*sb,
+ int clusternr,int flg)
+{
+ __u8 *pp;
+ unsigned u,v;
+ int i;
+ int last_sect;
+ Mdfat_entry mde;
+ struct buffer_head *bh;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+ cw->finfo=NULL;
+ cw->fbh=NULL;
+ cw->sect=0;
+ cw->sb=sb;
+ cw->clusternr=clusternr;
+ /* -------------------------------------------- */
+ dbl_mdfat_value(sb,clusternr,NULL,&mde);
+ cw->start_sect=mde.sector_minus_1+1;
+ cw->sect_cnt=cw->start_len=mde.size_lo_minus_1+1;
+ cw->flags=mde.flags;
+ if(!cw->start_sect) {cw->fcnt=0;return 0;};
+ /* -------------------------------------------- */
+ cw->fcnt=1;
+ cw->flen=cw->sect_cnt=cw->start_len;
+ cw->bytes_in_clust=cw->sect_cnt*SECTOR_SIZE;
+ cw->offset=0;
+ cw->bytes=SECTOR_SIZE;
+ cw->bytes_in_last=0;
+ last_sect=cw->start_sect+cw->sect_cnt-1;
+ /* -------------------------------------------- */
+ u=(cw->flags&0xe0)>>5;
+ if(cw->start_len==dblsb->s_sectperclust) u|=0x8;
+ switch(u)
+ { case 0x0: /* ffff=000x, len<ClustSize, compressed, regular file/dir */
+ case 0x2: /* ffff=010x, deleted 0 */
+ cw->compressed=1;
+ break;
+
+ case 0x1: /* ffff=001x, not compressed but not full size, regular file/dir */
+ case 0x3: /* ffff=011x, deleted 1 */
+ case 0x8: /* ffff=000x, len=ClustSize, not compressed, regular file/dir */
+ case 0xa: /* ffff=010x, deleted 8 */
+ case 0xc: /* ffff=100x, len=ClustSize, directory, never compressed */
+ case 0xe: /* ffff=110x, deleted c */
+ cw->compressed=0;
+ break;
+
+ case 0x4: /* ffff=100x, len<ClustSize, fragmented, regular file/dir */
+ case 0x6: /* ffff=110x, deleted 4 */
+ bh=raw_bread(sb,cw->start_sect);
+ if(bh==NULL)return -1;
+ pp=bh->b_data;
+ if(pp[0]!=0xed) /* check fragment signature */
+ { printk(KERN_ERR "DMSDOS: stac_cwalk_init: fragment signature not found cluster=%d\n",
+ clusternr);
+ raw_brelse(sb,bh);
+ return -1;
+ }
+ cw->fcnt=pp[1]+1; /* number of pieces */
+ if((cw->fcnt>dblsb->s_sectperclust)||(cw->fcnt*4>SECTOR_SIZE))
+ { printk(KERN_ERR "DMSDOS: stac_cwalk_init: too much fragmented cluster=%d!\n",
+ clusternr);
+ raw_brelse(sb,bh);
+ return -1;
+ };
+ cw->compressed=!(pp[2]&0x80);
+ v=cw->sect_cnt;
+ cw->sect_cnt+=(pp[2]&0x3F)+1;
+ for(i=1;i<cw->fcnt;++i)
+ { pp+=4;
+ u=(pp[2]&0xf)+((pp[3]>>2)&0x30);
+ v+=u+1;
+ };
+ last_sect=pp[0]+(pp[1]<<8)+((pp[3]&0x3f)<<16)+u; /* for suballocation tests */
+ if(v!=cw->sect_cnt)
+ { printk(KERN_ERR "DMSDOS: stac_cwalk_init: sector count mismash fragmented cluster=%d!\n",
+ clusternr);
+ raw_brelse(sb,bh);
+ return -1;
+ }
+ cw->fbh=bh;
+ cw->finfo=bh->b_data+4;
+ cw->offset=4*cw->fcnt;
+ cw->bytes=SECTOR_SIZE-cw->offset;
+ cw->bytes_in_clust=(cw->sect_cnt-1)*SECTOR_SIZE+cw->bytes;
+ break;
+
+ case 0x5: /* ffff=101x, len<ClustSize, suballocated, regular file/dir(?) */
+ case 0x7: /* ffff=111x, deleted 5 */
+ case 0xd: /* ffff=101x, len=ClustSize, suballocated, regular file/dir(?) */
+ case 0xf: /* ffff=111x, deleted d */
+ if(flg==0) {cw->bytes_in_clust=0;cw->bytes=0;return 1;};
+ bh=raw_bread(sb,cw->start_sect);
+ if(bh==NULL)return -1;
+ pp=&(bh->b_data[SECTOR_SIZE-2]);
+ if(CHS(pp)!=0x1234)
+ { printk(KERN_ERR "DMSDOS: stac_cwalk_init: suballocation signature not found cluster=%d\n",
+ clusternr);
+ raw_brelse(sb,bh);
+ return -1;
+ }
+ if(cw->start_len==1)
+ { /* short suballocation */
+ pp = &(bh->b_data[SECTOR_SIZE-6]);
+ cw->offset= CHS(pp) & (SECTOR_SIZE - 1); /* begin of area */
+ cw->compressed= !(CHS(pp) & 0x8000);
+ if(CHS(pp)&0x4000)
+ { printk(KERN_ERR "DMSDOS: stac_cwalk_init: suballocation not present, cluster %d\n",
+ clusternr);
+ raw_brelse(sb,bh); return -1;
+ };
+ pp = &(bh->b_data[SECTOR_SIZE-8]);
+ cw->bytes=CHS(pp) & (SECTOR_SIZE - 1); /* end of area */
+ /* some more paranoic checking of allocated space */
+ if (cw->bytes&&(cw->bytes<=SECTOR_SIZE-8)&&(cw->bytes>cw->offset))
+ cw->bytes-=cw->offset;
+ else
+ { printk(KERN_ERR "DMSDOS: stac_cwalk_init: count = %d < 0 in short subalocated\n", cw->bytes);
+ printk(KERN_ERR "DMSDOS: cluster %d read error\n",clusternr);
+ raw_brelse(sb,bh); return -1;
+ }
+ cw->bytes_in_clust=cw->bytes;
+ last_sect=0;
+ }
+ else
+ { /* long suballocation */
+ pp = &(bh->b_data[SECTOR_SIZE-8]);
+ cw->offset = CHS(pp) & (SECTOR_SIZE - 1);
+ cw->compressed = !(CHS(pp) & 0x8000);
+ if(CHS(pp)&0x4000)
+ { printk(KERN_ERR "DMSDOS: stac_cwalk_init: suballocation not present, cluster %d\n",
+ clusternr);
+ raw_brelse(sb,bh); return -1;
+ };
+ cw->bytes=SECTOR_SIZE-cw->offset-8;
+ cw->bytes_in_clust+=cw->bytes-SECTOR_SIZE;
+ if (cw->bytes < 0) {
+ printk(KERN_ERR "DMSDOS: stac_cwalk_init: count = %d < 0 in long subalocated\n", cw->bytes);
+ printk(KERN_ERR "DMSDOS: cluster %d read error\n",clusternr);
+ raw_brelse(sb,bh); return -1;
+ }
+ };
+ raw_brelse(sb,bh);
+ break;
+
+ case 0x9: /* ffff=001x, contradiction ??? */
+ case 0xb: /* ffff=011x, deleted 9 */
+ default:
+ printk(KERN_ERR "DMSDOS: stac_cwalk_init: unknown flags 0x%2x cluster %d\n",
+ mde.flags,clusternr);
+ return -1;
+ };
+
+ /* if flg>=2 we are checking subalocated end of cluster */
+ /* because of we did not know if stacker can subalocate clusters
+ which are not in order we must check nonlinear
+ suballocation */
+ /* text above is question but now I know that stacker is able to do everything
+ nonlinear suballocation regulary exist */
+ if(last_sect&&(dblsb->s_cvf_version>=STAC4)&&(flg>=2))
+ { /* check for subalocated end of cluster */
+ /* 1) check start of next cluster for linear subalocation */
+ if(clusternr<dblsb->s_max_cluster)
+ { dbl_mdfat_value(sb,clusternr+1,NULL,&mde);
+ i=(mde.sector_minus_1+1)==last_sect;
+ if(i&&((mde.flags&0xA0)!=0xA0))
+ { printk(KERN_ERR "DMSDOS: stac_cwalk_init: wrong cluster types for subalocation, cluster %d\n",
+ clusternr);
+ return -1;
+ };
+ } else i=0;
+ /* 2) check for nonlinear subalocation */
+ if((u=dbl_bitfat_value(sb,last_sect,NULL))==0) if(flg>=3)goto error1;
+ if((u>1)||(i))
+ { if((u<=1)&&(flg>=3))
+ { printk(KERN_ERR "DMSDOS: stac_cwalk_init: suballocation error 1, cluster %d\n",
+ clusternr);
+ return -1;
+ };
+ if(!i)LOG_ALLOC("DMSDOS: stac_cwalk_init: nonlinear suballocation, cluster %d\n",
+ clusternr);
+ /* now we know that our cluster is subalocated, we must find
+ number of bytes in last sector of cluster */
+ bh=raw_bread(sb,last_sect);
+ if(bh==NULL)return -1;
+ pp=&(bh->b_data[SECTOR_SIZE-2]);
+ if(CHS(pp)!=0x1234)
+ { printk(KERN_ERR "DMSDOS: stac_cwalk_init: suballocation error 2, cluster %d\n",
+ clusternr);
+ raw_brelse(sb,bh);
+ return -1;
+ };
+ pp = &(bh->b_data[SECTOR_SIZE-6]); /* begin of short suball. clust */
+ u = CHS(pp) & (SECTOR_SIZE - 1);
+ pp = &(bh->b_data[SECTOR_SIZE-8]); /* begin of long suball. clust */
+ v = CHS(pp) & (SECTOR_SIZE - 1);
+ raw_brelse(sb,bh);
+ /* u contains number of bytes of our cluster in last_sect */
+ if(v<u)
+ {
+ pp = &(bh->b_data[SECTOR_SIZE-6]);u = CHS(pp);
+ pp = &(bh->b_data[SECTOR_SIZE-8]);v = CHS(pp);
+ printk(KERN_ERR "DMSDOS: stac_cwalk_init: suballocation error 3, cluster %d, zerro offset 0x%X 0x%X\n",
+ clusternr,u,v);
+ return -1;
+ };
+ cw->bytes_in_last=u;
+ if(cw->sect_cnt>1)
+ cw->bytes_in_clust-=SECTOR_SIZE-u;
+ else if((i=cw->bytes+cw->offset-cw->bytes_in_last)>0)
+ { if((cw->bytes-=i)<=0)
+ { printk(KERN_ERR "DMSDOS: stac_cwalk_init: suballocation error 4, cluster %d\n",
+ clusternr);
+ return -1;
+ };
+ cw->bytes_in_clust-=i;
+ };
+ cw->bytes_in_last|=0x4000;
+ };
+ };
+
+ if ((i=cw->bytes_in_clust-dblsb->s_sectperclust*SECTOR_SIZE)>0)
+ { cw->bytes_in_clust-=i;
+ if(!cw->bytes_in_last) cw->bytes_in_last=SECTOR_SIZE-i;
+ else if((cw->bytes_in_last-=i)<0x4000)
+ { error1:
+ printk(KERN_ERR "DMSDOS: stac_cwalk_init: bad bytes_in_cluster %d\n",
+ clusternr);
+ return -1;
+ };
+ };
+
+ return cw->bytes_in_clust;
+};
+
+/* returns in order all sectors of cluster */
+/* in Stac_cwalk updates fields sect, offset and bytes */
+int stac_cwalk_sector(Stac_cwalk *cw)
+{ if(!cw->sect)
+ { if(!cw->fcnt) return 0;
+ cw->fcnt--;
+ cw->flen--;
+ cw->sect=cw->start_sect;
+ }
+ else
+ { if(!cw->flen)
+ { if(!cw->fcnt) return 0;
+ cw->fcnt--;
+ if(cw->finfo==NULL)
+ { printk(KERN_ERR "DMSDOS: stac_cwalk_sector: finfo==NULL, cluster %d\n",
+ cw->clusternr);
+ return 0;
+ };
+ cw->sect=cw->finfo[0]+(cw->finfo[1]<<8)+((cw->finfo[3]&0x3f)<<16);
+ cw->flen=(cw->finfo[2]&0xf)+((cw->finfo[3]>>2)&0x30);
+ cw->finfo+=4;
+ }
+ else
+ { cw->sect++;
+ cw->flen--;
+ };
+ cw->offset=0;
+ if(!cw->flen&&!cw->fcnt&&cw->bytes_in_last)
+ cw->bytes=cw->bytes_in_last&(SECTOR_SIZE-1);
+ else cw->bytes=SECTOR_SIZE;
+ };
+ return cw->sect;
+};
+
+void stac_cwalk_done(Stac_cwalk *cw)
+{
+ if(cw->fbh!=NULL) raw_brelse(cw->sb,cw->fbh);
+};
+
+
+void stac_special_free(struct super_block*sb, int clusternr)
+{
+ int val;
+ int sect;
+ Mdfat_entry newmde,dummy_mde;
+ Stac_cwalk cw;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ val=stac_cwalk_init(&cw,sb,clusternr,0);
+ if(val<=0)
+ { if(val<0)
+ printk(KERN_ERR "DMSDOS: stac_special_free: alloc error in cluster %d\n", clusternr);
+ else
+ LOG_CLUST("DMSDOS: stac_special_free: already free cluster %d\n", clusternr);
+ return;
+ };
+ newmde.sector_minus_1=-1;
+ newmde.size_lo_minus_1=0;
+ newmde.size_hi_minus_1=0;
+ newmde.flags=0;
+
+ dbl_mdfat_value(sb,clusternr,&newmde,&dummy_mde);
+ if((cw.flags&0xA0)==0xA0)
+ { /* mark part of suballocated sector as free */
+ struct buffer_head *bh;
+ bh=raw_bread(sb,cw.start_sect);
+ if(bh!=NULL)
+ { if(cw.start_len==1)
+ bh->b_data[SECTOR_SIZE-6+1] |= 0x40;
+ else
+ bh->b_data[SECTOR_SIZE-8+1] |= 0x40;
+ raw_mark_buffer_dirty(sb,bh,1);
+ raw_brelse(sb,bh);
+ }
+ }
+ /* free sectors from BITFAT */
+ while((sect=stac_cwalk_sector(&cw))>0)
+ {
+ val=dbl_bitfat_value(sb,sect,NULL);
+ if(val>0)
+ { --val;
+ dbl_bitfat_value(sb,sect,&val);
+ dblsb->s_full=0;
+ /* adapt s_free_sectors, -1 unknown */
+ /*if(val==0&&dblsb->s_free_sectors>=0) dblsb->s_free_sectors++;*/
+ /* Hi Pavel,
+ I have commented this out since free sector count is now
+ maintained in dbl_bitfat_value.
+ */
+ } else LOG_CLUST("DMSDOS: stac_special_free: sector not alocated\n");
+ }
+
+ stac_cwalk_done(&cw);
+
+}
+
+/* replaces an existing cluster for stacker;
+ this unusual function must be called before rewriting any file cluster;
+ *** size must be known (encoded in mde) ***
+ it does nothing if called too often;
+ returns first sector nr
+*/
+
+int stac_replace_existing_cluster(struct super_block*sb, int cluster,
+ int near_sector,
+ Mdfat_entry*mde)
+{ Mdfat_entry old_mde,new_mde,dummy;
+ int i;
+ int newval;
+ int sector;
+ int old_sector;
+ int old_size;
+ int new_size;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ lock_mdfat_alloc(dblsb);
+
+ LOG_ALLOC("DMSDOS: stac_replace_existing_cluster cluster=%d near_sector=%d\n",
+ cluster,near_sector);
+ dbl_mdfat_value(sb,cluster,NULL,&old_mde);
+ old_size=old_mde.size_lo_minus_1+1;
+ old_sector=old_mde.sector_minus_1+1;
+ new_size=mde->size_lo_minus_1+1;
+ if(old_mde.flags&2)
+ {
+ /* stacker routines always replace mdfat entry */
+ newval=0;
+ LOG_ALLOC("DMSDOS: stac_replace_existing_cluster: freeing old sectors...\n");
+ stac_special_free(sb,cluster);
+ LOG_ALLOC("DMSDOS: stac_replace_existing_cluster: freeing finished\n");
+ }
+ LOG_ALLOC("DMSDOS: stac_replace_existing_cluster: call find_free_bitfat...\n");
+ sector=find_free_bitfat(sb,near_sector,new_size);
+ LOG_ALLOC("DMSDOS: stac_replace_existing_cluster: find_free_bitfat returned %d\n",
+ sector);
+ if(sector<=0)
+ { if(old_mde.flags&2)
+ { /* Stacker routines don't have an undo list for now.
+ We cannot restore the state before. Sorry data are lost now. */
+ new_mde.sector_minus_1=0;
+ new_mde.size_lo_minus_1=0;
+ new_mde.size_hi_minus_1=0;
+ new_mde.flags=mde->flags=0;
+ LOG_ALLOC("DMSDOS: stac_replace_existing_cluster: deleting mdfat entry...\n");
+ dbl_mdfat_value(sb,cluster,&new_mde,&dummy);
+ }
+ unlock_mdfat_alloc(dblsb);
+ return -ENOSPC; /* disk full */
+ }
+ /* check whether really free (bug supposed in find_free_bitfat) */
+ for(i=0;i<new_size;++i)
+ { if(dbl_bitfat_value(sb,sector+i,NULL))
+ { printk(KERN_EMERG "DMSDOS: find_free_bitfat returned sector %d size %d but they are not all free!\n",
+ sector,new_size);
+ unlock_mdfat_alloc(dblsb);
+ panic("DMSDOS: stac_replace_existing_cluster: This is a bug - reboot and check filesystem\n");
+ return -EIO;
+ }
+ }
+ newval=1;
+ LOG_ALLOC("DMSDOS: stac_replace_existing_cluster: allocating in bitfat...\n");
+ for(i=0;i<new_size;++i)dbl_bitfat_value(sb,sector+i,&newval);
+
+ new_mde.sector_minus_1=sector-1;
+ new_mde.size_lo_minus_1=mde->size_lo_minus_1;
+ new_mde.size_hi_minus_1=mde->size_hi_minus_1;
+ new_mde.flags=mde->flags|2;
+ LOG_ALLOC("DMSDOS: stac_replace_existing_cluster: writing mdfat...\n");
+ dbl_mdfat_value(sb,cluster,&new_mde,&dummy);
+ unlock_mdfat_alloc(dblsb);
+ return sector; /* okay */
+}
+
+#endif /* DMSDOS_CONFIG_STAC */