|
|
1.1 ! root 1: /* ! 2: dblspace_fileops.c-2.1.80 ! 3: ! 4: DMSDOS CVF-FAT module: file operation routines (for kernel>=2.1.80). ! 5: ! 6: ****************************************************************************** ! 7: DMSDOS (compressed MSDOS filesystem support) for Linux ! 8: written 1995-1998 by Frank Gockel and Pavel Pisa ! 9: ! 10: (C) Copyright 1995-1998 by Frank Gockel ! 11: (C) Copyright 1996-1998 by Pavel Pisa ! 12: ! 13: Some code of dmsdos has been copied from the msdos filesystem ! 14: so there are the following additional copyrights: ! 15: ! 16: (C) Copyright 1992,1993 by Werner Almesberger (msdos filesystem) ! 17: (C) Copyright 1994,1995 by Jacques Gelinas (mmap code) ! 18: (C) Copyright 1992-1995 by Linus Torvalds ! 19: ! 20: DMSDOS was inspired by the THS filesystem (a simple doublespace ! 21: DS-0-2 compressed read-only filesystem) written 1994 by Thomas Scheuermann. ! 22: ! 23: The DMSDOS code is distributed under the Gnu General Public Licence. ! 24: See file COPYING for details. ! 25: ****************************************************************************** ! 26: ! 27: */ ! 28: ! 29: #include <linux/sched.h> ! 30: #include <linux/ctype.h> ! 31: #include <linux/major.h> ! 32: #include <linux/blkdev.h> ! 33: #include <linux/fs.h> ! 34: #include <linux/stat.h> ! 35: #include <linux/locks.h> ! 36: #include <asm/segment.h> ! 37: #include <linux/mm.h> ! 38: #include <linux/malloc.h> ! 39: #include <linux/string.h> ! 40: #include <linux/msdos_fs.h> ! 41: #include <linux/errno.h> ! 42: #include <linux/kernel.h> ! 43: #include <linux/shm.h> ! 44: #include <linux/mman.h> ! 45: #include <asm/system.h> ! 46: #include "dmsdos.h" ! 47: ! 48: extern unsigned long dmsdos_speedup; ! 49: ! 50: #define MIN(x,y) ( ( (x)<(y) ) ? (x) : (y) ) ! 51: ! 52: void do_cluster_reada(struct super_block*sb,int clusternr) ! 53: { /* read one cluster ahead without waiting for the data */ ! 54: int nextclust; ! 55: ! 56: nextclust=dbl_fat_nextcluster(sb,clusternr,NULL); ! 57: if(nextclust>0) ! 58: { /* no need to read-ahead if it is in cache */ ! 59: /* for a simple search for existence we needn't lock the cache */ ! 60: if(find_in_ccache(sb,nextclust,NULL,NULL)==NULL) ! 61: dmsdos_read_cluster(sb,NULL,nextclust); ! 62: } ! 63: } ! 64: ! 65: int dblspace_file_read( ! 66: struct file *filp, ! 67: char *buf, ! 68: size_t count, ! 69: loff_t *ppos) ! 70: { ! 71: int clusternr; ! 72: /*unsigned char*clusterd;*/ ! 73: int offset; ! 74: int bytes_read; ! 75: int membytes; ! 76: int membytes_bits; ! 77: int ret; ! 78: char * b; ! 79: int toread; ! 80: Cluster_head*ch; ! 81: struct inode *inode; ! 82: struct super_block*sb; ! 83: Dblsb*dblsb; ! 84: ! 85: LOG_FS("DMSDOS: file_read start...\n"); ! 86: ! 87: inode = filp->f_dentry->d_inode; ! 88: LOG_FS("DMSDOS: file_read: got inode\n"); ! 89: sb=inode->i_sb; ! 90: LOG_FS("DMSDOS: file_read: got sb\n"); ! 91: dblsb=MSDOS_SB(sb)->private_data; ! 92: LOG_FS("DMSDOS: file_read: got dblsb\n"); ! 93: ! 94: if (!inode) { ! 95: printk(KERN_ERR "DMSDOS: file_read: inode = NULL, rejected.\n"); ! 96: return -EINVAL; ! 97: } ! 98: /* S_ISLNK allows for UMSDOS. Should never happen for normal MSDOS */ ! 99: if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) { ! 100: printk(KERN_ERR "DMSDOS: file_read: mode = %07o, rejected.\n",inode->i_mode); ! 101: return -EINVAL; ! 102: } ! 103: ! 104: LOG_FS("DMSDOS: file_read init complete...\n"); ! 105: ! 106: if(count<=0)return 0; ! 107: ! 108: if(*ppos>=inode->i_size)return 0; ! 109: ! 110: if(*ppos+count>inode->i_size)count=inode->i_size-*ppos; ! 111: ! 112: ret=verify_area(VERIFY_WRITE, buf, count); ! 113: if(ret<0)return ret; ! 114: ret=0; ! 115: ! 116: membytes=SECTOR_SIZE*dblsb->s_sectperclust; ! 117: membytes_bits=SECTOR_BITS+dblsb->s_spc_bits; ! 118: ! 119: /* calculate clusternr for cluster to read */ ! 120: offset=*ppos&(membytes-1); ! 121: LOG_CLUST("DMSDOS: file_read: get_cluster...\n"); ! 122: clusternr=get_cluster(inode,*ppos>>membytes_bits); ! 123: if(clusternr<=0) ! 124: { printk(KERN_ERR "DMSDOS: file_readx: FAT mismatches file size for ino=%ld\n", ! 125: inode->i_ino); ! 126: return 0; ! 127: } ! 128: ! 129: bytes_read=0; ! 130: b=buf; ! 131: ! 132: do ! 133: { LOG_CLUST("DMSDOS: file_read: calling ch_read...\n"); ! 134: ch=ch_read(sb,clusternr,0); ! 135: LOG_CLUST("DMSDOS: file_read: after ch_read\n"); ! 136: ret=(ch==NULL)?-EIO:0; ! 137: if(ret>=0) ! 138: { if(count>READA_THRESHOLD&&(dmsdos_speedup&SP_USE_READ_AHEAD)!=0) ! 139: do_cluster_reada(inode->i_sb,clusternr); ! 140: toread=(membytes-offset>count) ? count : membytes-offset; ! 141: /*printk("DMSDOS file_readx: memcpy_tofs(0x%08x,0x%08x,0x%08x)\n", ! 142: b,clusterd+offset,toread);*/ ! 143: memcpy_tofs(b,ch->c_data+offset,toread); ! 144: bytes_read+=toread; ! 145: *ppos+=toread; ! 146: count-=toread; ! 147: ch_free(ch); ! 148: if(count>0) ! 149: { b+=toread; ! 150: offset=0; ! 151: LOG_CLUST("DMSDOS: file_read: get_cluster...\n"); ! 152: clusternr=get_cluster(inode,*ppos>>membytes_bits); ! 153: if(*ppos&(membytes-1)) ! 154: panic("DMSDOS: read_file bug: f_pos not cluster-aligned"); ! 155: if(clusternr<=0) ! 156: { ret=-EIO; ! 157: printk(KERN_ERR "DMSDOS: file_readx: FAT mismatches file size for ino=%ld\n", ! 158: inode->i_ino); ! 159: } ! 160: } ! 161: } ! 162: } ! 163: while(count>0&&ret>=0); ! 164: ! 165: return (bytes_read==0&&ret<0)?ret:bytes_read; ! 166: } ! 167: ! 168: int dblspace_file_write( ! 169: struct file *filp, ! 170: const char *buf, ! 171: size_t count, ! 172: loff_t *ppos) ! 173: { ! 174: int cluster; ! 175: int ret=0; ! 176: unsigned int offset; ! 177: const unsigned char *b; ! 178: int canwrite; ! 179: int written; ! 180: int clustersize; ! 181: int clustersize_bits; ! 182: int uc; ! 183: Cluster_head*ch; ! 184: struct inode *inode = filp->f_dentry->d_inode; ! 185: struct super_block*sb=inode->i_sb; ! 186: Dblsb*dblsb=MSDOS_SB(sb)->private_data; ! 187: ! 188: if (!inode) { ! 189: printk(KERN_ERR "dmsdos_file_write: inode = NULL\n"); ! 190: return -EINVAL; ! 191: } ! 192: /* S_ISLNK allows for UMSDOS. Should never happen for normal MSDOS */ ! 193: if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) { ! 194: printk(KERN_ERR "dmsdos_file_write: mode = %07o\n",inode->i_mode); ! 195: return -EINVAL; ! 196: } ! 197: /* ! 198: * ok, append may not work when many processes are writing at the same time ! 199: * but so what. That way leads to madness anyway. ! 200: */ ! 201: if(sb->s_flags&MS_RDONLY) ! 202: { printk(KERN_ERR "DMSDOS: file_write: READ-ONLY filesystem\n"); ! 203: return -EROFS; ! 204: } ! 205: ! 206: if(dblsb->s_comp==READ_ONLY)return -EPERM; ! 207: ! 208: if (filp->f_flags & O_APPEND) *ppos = inode->i_size; ! 209: if (count <= 0) return 0; ! 210: ! 211: ret=verify_area(VERIFY_READ, buf, count); ! 212: if(ret<0)return ret; ! 213: ret=0; ! 214: ! 215: clustersize=dblsb->s_sectperclust*SECTOR_SIZE; ! 216: clustersize_bits=dblsb->s_spc_bits+SECTOR_BITS; ! 217: ! 218: uc=0; ! 219: /* no longer working in 2.3.99... ! 220: if(dmsdos_speedup&SP_NO_EMD_COMPR) ! 221: uc=(MSDOS_I(inode)->i_binary>1)?1:0; / uncompressed forced / ! 222: */ ! 223: ! 224: offset=*ppos&(clustersize-1); ! 225: do ! 226: { cluster=get_cluster(inode,*ppos>>clustersize_bits); ! 227: if(cluster>0)break; ! 228: if(dblsb->s_full==2) ! 229: { printk(KERN_ERR "DMSDOS: write_file: CVF full (full flag set)\n"); ! 230: return -ENOSPC; ! 231: } ! 232: if(dblsb->s_free_sectors<MIN_FREE_SECTORS) ! 233: { printk(KERN_ERR "DMSDOS: write_file: CVF full (free sector count too low)\n"); ! 234: return -ENOSPC; ! 235: } ! 236: if(fat_add_cluster(inode)<0) ! 237: { printk(KERN_ERR "DMSDOS: write_file: fat_add_cluster failed\n"); ! 238: return -ENOSPC; ! 239: } ! 240: } ! 241: while(cluster<=0); ! 242: ! 243: LOG_CLUST("DMSDOS: file_write: beginning with cluster %d\n", ! 244: cluster); ! 245: ! 246: b=buf; ! 247: written=0; ! 248: ! 249: while(count>0) ! 250: { ! 251: if(offset>0||count<clustersize) ! 252: { /* cluster must be read because it will only partially overwritten */ ! 253: LOG_CLUST("DMSDOS: write_file: reading cluster %d...\n",cluster); ! 254: ch=ch_read(sb,cluster,C_KEEP_LOCK); ! 255: if(ch==NULL) ! 256: { printk(KERN_ERR "DMSDOS: write_file: read_cluster failed!\n"); ! 257: ret=-EIO; ! 258: break; ! 259: } ! 260: /*lock_ch(ch); we call with KEEP_LOCK above */ ! 261: } ! 262: else ! 263: { /* cluster will be fully overwritten, don't read it */ ! 264: ch=ch_read(sb,cluster,C_KEEP_LOCK|C_NO_READ); ! 265: if(ch==NULL) ! 266: { printk(KERN_ERR "DMSDOS: write_file: ch_noread failed!\n"); ! 267: ret=-EIO; ! 268: break; ! 269: } ! 270: /*lock_ch(ch); we call with KEEP_LOCK above */ ! 271: ch->c_length= (count+offset<clustersize) ? ! 272: count+offset : clustersize; ! 273: } ! 274: canwrite=MIN(clustersize-offset,count); ! 275: memcpy_fromfs(&(ch->c_data[offset]),b,canwrite); ! 276: ! 277: /* did cluster grow ? */ ! 278: if(canwrite+offset>ch->c_length) ! 279: { /*printk(KERN_ERR "DMSDOS: write_file: write beyond logical cluster end, appending.\n"); ! 280: */ ! 281: ch->c_length=canwrite+offset; ! 282: } ! 283: if(ch->c_length>clustersize) ! 284: { printk(KERN_ERR "DMSDOS: write_file: length>clustersize ??? bug !!!\n"); ! 285: ch->c_length=clustersize; ! 286: } ! 287: ! 288: /*unlock_ch(ch); no not here*/ ! 289: ! 290: LOG_CLUST("DMSDOS: write_file: writing cluster %d...\n",cluster); ! 291: ! 292: /* ch_dirty_locked unlocks the cluster */ ! 293: if(ch_dirty_locked(ch,0,uc)<0) ! 294: { printk(KERN_ERR "DMSDOS: write_file: ch_dirty failed!\n"); ! 295: ch_free(ch); ! 296: ch=NULL; ! 297: ret=-EIO; ! 298: break; ! 299: } ! 300: ch_free(ch); ! 301: ch=NULL; ! 302: ! 303: offset=0; ! 304: b+=canwrite; ! 305: *ppos+=canwrite; ! 306: written+=canwrite; ! 307: count-=canwrite; ! 308: ! 309: if(count==0)break; /* braucht keinen neuen cluster mehr*/ ! 310: ! 311: /* next cluster ? */ ! 312: cluster=get_cluster(inode,*ppos>>clustersize_bits); ! 313: if(cluster<=0) ! 314: { LOG_CLUST("DMSDOS: write_file: write_loop: allocating new cluster\n"); ! 315: if(dblsb->s_full==2) ! 316: { printk(KERN_ERR "DMSDOS: write_file: CVF full (full flag set)\n"); ! 317: ret=-ENOSPC; ! 318: break; ! 319: } ! 320: if(dblsb->s_free_sectors<MIN_FREE_SECTORS) ! 321: { printk(KERN_ERR "DMSDOS: write_file: CVF full (free sector count too low)\n"); ! 322: ret=-ENOSPC; ! 323: break; ! 324: } ! 325: if(fat_add_cluster(inode)<0) ! 326: { printk(KERN_ERR "DMSDOS: write_file: fat_add_cluster failed\n"); ! 327: ret=-ENOSPC; ! 328: break; ! 329: } ! 330: cluster=get_cluster(inode,*ppos>>clustersize_bits); ! 331: if(cluster<=0) ! 332: { printk(KERN_ERR "DMSDOS: write_file: something's wrong, cannot happen\n"); ! 333: ret=-EIO; ! 334: break; ! 335: } ! 336: } ! 337: } ! 338: ! 339: if(*ppos>inode->i_size) ! 340: { inode->i_size=*ppos; ! 341: /*inode->i_dirt=1; .... HMMM .... */ ! 342: mark_inode_dirty(inode); ! 343: } ! 344: ! 345: return (written==0)?ret:written; ! 346: } ! 347: ! 348: ! 349: /* Grmpf.... partially untested code. Don't know an application that does ! 350: writable mmaps, but it would be needed for testing this piece of code */ ! 351: ! 352: /* idea: kernel does buffer reads with bmap calculated buffers - impossible ! 353: for dmsdos. (see kernel mmap code). ! 354: kernel emulates writable mmap by calling file_write when no buffers ! 355: are present - should work for dmsdos. ! 356: so we do file_read-emulated readable mmaps here though the ! 357: generic_mmap function is used. ! 358: */ ! 359: ! 360: #ifdef DMSDOS_USE_READPAGE ! 361: ! 362: #ifdef __FOR_KERNEL_2_3_99 ! 363: #error kernels >= 2.3.99 need mmap interface ! 364: #endif ! 365: ! 366: int read_the_page(unsigned long address, unsigned long pos, ! 367: struct inode*inode) ! 368: { ! 369: unsigned int clear; ! 370: long gap; /* distance from eof to pos */ ! 371: ! 372: LOG_FS("DMSDOS: read_the_page\n"); ! 373: ! 374: address &= PAGE_MASK; ! 375: ! 376: clear = 0; ! 377: gap = inode->i_size - pos; ! 378: if (gap <= 0){ ! 379: /* mmaping beyond end of file */ ! 380: clear = PAGE_SIZE; ! 381: }else{ ! 382: int cur_read; ! 383: int need_read; ! 384: struct file filp; ! 385: if (gap < PAGE_SIZE){ ! 386: clear = PAGE_SIZE - gap; ! 387: } ! 388: filp.f_reada = 0; ! 389: filp.f_pos = pos; ! 390: need_read = PAGE_SIZE - clear; ! 391: { mm_segment_t cur_fs = get_fs(); ! 392: filp.f_dentry=kmalloc(sizeof(struct dentry),GFP_KERNEL); ! 393: if(filp.f_dentry==NULL) ! 394: { printk(KERN_ERR "DMSDOS: read_the_page: no memory!\n"); ! 395: return -1; ! 396: } ! 397: filp.f_dentry->d_inode=inode; ! 398: ! 399: set_fs (KERNEL_DS); ! 400: LOG_FS("DMSDOS: read_the_page: calling file_read...\n"); ! 401: cur_read = dblspace_file_read (&filp, ! 402: (char*)address, ! 403: need_read, ! 404: &(filp.f_pos)); ! 405: set_fs (cur_fs); ! 406: kfree(filp.f_dentry); ! 407: } ! 408: if (cur_read != need_read){ ! 409: printk ("DMSDOS: Error while reading an mmap file %d <> %d\n" ! 410: ,cur_read,need_read); ! 411: return -1; ! 412: } ! 413: } ! 414: if (clear > 0){ ! 415: memset ((char*)address+PAGE_SIZE-clear,0,clear); ! 416: } ! 417: return 0; ! 418: } ! 419: ! 420: #ifdef READPAGE_DENTRY ! 421: int dblspace_readpage(struct dentry*dentry, struct page *page) ! 422: { unsigned long address; ! 423: int error = -1; ! 424: struct inode*inode=dentry->d_inode; ! 425: #else ! 426: int dblspace_readpage(struct inode *inode, struct page *page) ! 427: { unsigned long address; ! 428: int error = -1; ! 429: #endif ! 430: LOG_FS("DMSDOS: readpage %08lx\n", page_address(page)); ! 431: ! 432: address = page_address(page); ! 433: atomic_inc(&page->count); ! 434: set_bit(PG_locked, &page->flags); ! 435: ! 436: /* now read the data */ ! 437: error=read_the_page(address,page->offset,inode); ! 438: ! 439: LOG_FS("DMSDOS: readpage: read_the_page returned %d\n",error); ! 440: ! 441: if(error==0)set_bit(PG_uptodate, &page->flags); ! 442: ! 443: clear_bit(PG_locked, &page->flags); ! 444: wake_up(&page->wait); ! 445: ! 446: free_page(address); ! 447: return error; ! 448: } ! 449: ! 450: #else /* from: ifdef DMSDOS_USE_READPAGE */ ! 451: ! 452: /* this is supposed to be obsolete stuff for older kernels... */ ! 453: ! 454: /* we reactivate it for 2.3.99 kernel */ ! 455: ! 456: /* ! 457: * Fill in the supplied page for mmap ! 458: */ ! 459: struct page* dblspace_file_mmap_nopage( ! 460: struct vm_area_struct * area, ! 461: unsigned long address, ! 462: int write_access) ! 463: { ! 464: struct inode * inode = area->vm_file->f_dentry->d_inode; ! 465: struct page* page; ! 466: unsigned int clear; ! 467: int pos; ! 468: long gap; /* distance from eof to pos */ ! 469: ! 470: LOG_FS("DMSDOS: file_mmap_nopage\n"); ! 471: /*page = __get_free_page(GFP_KERNEL);*/ ! 472: page=alloc_pages(GFP_KERNEL,0); ! 473: if (!page)return NULL; ! 474: LOG_FS("DMSDOS: file_mmap_nopage: got page\n"); ! 475: ! 476: address &= PAGE_MASK; ! 477: pos = address - area->vm_start + area->vm_pgoff*PAGE_SIZE; ! 478: ! 479: clear = 0; ! 480: gap = inode->i_size - pos; ! 481: if (gap <= 0){ ! 482: /* mmaping beyond end of file */ ! 483: clear = PAGE_SIZE; ! 484: }else{ ! 485: int cur_read; ! 486: int need_read; ! 487: struct file filp; ! 488: if (gap < PAGE_SIZE){ ! 489: clear = PAGE_SIZE - gap; ! 490: } ! 491: filp.f_reada = 0; ! 492: filp.f_pos = pos; ! 493: need_read = PAGE_SIZE - clear; ! 494: { mm_segment_t cur_fs = get_fs(); ! 495: filp.f_dentry=kmalloc(sizeof(struct dentry),GFP_KERNEL); ! 496: if(filp.f_dentry==NULL) ! 497: { printk(KERN_ERR "DMSDOS: file_mmap_nopage: no memory!\n"); ! 498: return NULL; ! 499: } ! 500: filp.f_dentry->d_inode=inode; ! 501: ! 502: set_fs (KERNEL_DS); ! 503: LOG_FS("DMSDOS: file_mmap_nopage: calling file_read...\n"); ! 504: cur_read = dblspace_file_read(&filp, ! 505: (char*)page_address(page), ! 506: need_read,&filp.f_pos); ! 507: LOG_FS("DMSDOS: file_mmap_nopage: file_read returned\n"); ! 508: set_fs (cur_fs); ! 509: kfree(filp.f_dentry); ! 510: } ! 511: if (cur_read != need_read){ ! 512: printk ("DMSDOS: Error while reading an mmap file %d <> %d\n" ! 513: ,cur_read,need_read); ! 514: } ! 515: } ! 516: if (clear > 0){ ! 517: memset ((char*)page+PAGE_SIZE-clear,0,clear); ! 518: } ! 519: LOG_FS("DMSDOS: file_mmap_nopage: end\n"); ! 520: return page; ! 521: } ! 522: ! 523: struct vm_operations_struct dblspace_file_mmap = { ! 524: NULL, /* open */ ! 525: NULL, /* close */ ! 526: NULL, /* unmap */ ! 527: NULL, /* protect */ ! 528: NULL, /* sync */ ! 529: dblspace_file_mmap_nopage, /* nopage */ ! 530: NULL, /* wppage */ ! 531: NULL, /* swapout */ ! 532: }; ! 533: ! 534: /* ! 535: * This is used for a general mmap of a dmsdos file ! 536: * Returns 0 if ok, or a negative error code if not. ! 537: */ ! 538: int dblspace_mmap(struct file*file,struct vm_area_struct*vma) ! 539: { struct inode *inode = file->f_dentry->d_inode; ! 540: LOG_FS("DMSDOS: mmap ino=%ld\n",inode->i_ino); ! 541: if (vma->vm_flags & VM_SHARED) /* only PAGE_COW or read-only supported now */ ! 542: return -EINVAL; ! 543: /* ! 544: if (vma->vm_offset & (inode->i_sb->s_blocksize - 1)) ! 545: return -EINVAL; ! 546: */ ! 547: if (!inode->i_sb || !S_ISREG(inode->i_mode)) ! 548: return -EACCES; ! 549: if (!IS_RDONLY(inode)) { ! 550: inode->i_atime = CURRENT_TIME; ! 551: /*inode->i_dirt = 1;*/ ! 552: mark_inode_dirty(inode); ! 553: } ! 554: ! 555: vma->vm_file = file; ! 556: vma->vm_ops = &dblspace_file_mmap; ! 557: return 0; ! 558: } ! 559: ! 560: #endif /* from: else / ifdef DMSDOS_USE_READPAGE */ ! 561:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.