Annotation of dmsdos/src/dblspace_fileops.c-2.3.99, revision 1.1.1.1

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: 

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.