diff -ur isofs.orig/inode.c isofs/inode.c --- isofs.orig/inode.c Thu Oct 19 16:48:42 2000 +++ isofs/inode.c Tue Nov 7 08:06:22 2000 @@ -985,6 +985,12 @@ sync_page: block_sync_page, bmap: _isofs_bmap }; +int readpage_form2(struct file *file, struct page *page); +static struct address_space_operations isofs_aops_form2 = { + readpage: readpage_form2, + sync_page: block_sync_page, + bmap: _isofs_bmap +}; static inline void test_and_set_uid(uid_t *p, uid_t value) { @@ -1092,6 +1098,196 @@ goto out; } +int cd_ioctl(struct super_block *s, int request, unsigned long arg) +{ + return ioctl_by_bdev(s->s_bdev,request,arg); +} + +static char *last_buf; +unsigned char* cd_read_raw_sector(struct inode *inode, int sector_nr) +{ + static DECLARE_MUTEX(last_buf_sem); + static struct inode* last_inode; + static int last_sector_nr = -1; + unsigned char *buf; + struct cdrom_msf *msf; + int lba; + + buf = kmalloc(CD_FRAMESIZE_RAW,GFP_KERNEL); + if(buf == NULL) return NULL; + + down(&last_buf_sem); + + if(sector_nr == last_sector_nr && inode == last_inode) + // how to do the right check/invalidate? + { + memcpy(buf,last_buf,CD_FRAMESIZE_RAW); + goto out; + } + + lba = isofs_bmap(inode,0) + 150 + sector_nr; + msf = (struct cdrom_msf *)buf; + msf->cdmsf_min0 = lba/75/60; + msf->cdmsf_sec0 = (lba/75)%60; + msf->cdmsf_frame0 = lba%75; + if(cd_ioctl(inode->i_sb,CDROMREADRAW,(unsigned long)msf) < 0) + { + kfree(buf); + buf = NULL; + } + else + { + last_inode = inode; + last_sector_nr = sector_nr; + memcpy(last_buf,buf,CD_FRAMESIZE_RAW); + } +out: + up(&last_buf_sem); + return buf; +} + +#define FORM2_DATA_SIZE 2324 +static int kisofsd_pid; +static int kisofsd_running = 0; +static spinlock_t kisofsd_req_lock = SPIN_LOCK_UNLOCKED; +static LIST_HEAD(kisofsd_req_list); /* List of requests needing servicing */ +static DECLARE_WAIT_QUEUE_HEAD(kisofsd_wait); + +struct kisofsd_req { + struct list_head req_list; + struct inode *inode; + struct page *page; +}; + +static int kisofsd_add_request(struct inode* inode, struct page *page) +{ + struct kisofsd_req *req = kmalloc(sizeof(struct kisofsd_req), + GFP_KERNEL); + if(req == NULL) return -ENOMEM; + + if (!PageLocked(page)) + PAGE_BUG(page); + clear_bit(PG_uptodate, &page->flags); + clear_bit(PG_error, &page->flags); + + INIT_LIST_HEAD(&req->req_list); + req->inode = inode; + req->page = page; + + spin_lock(&kisofsd_req_lock); + list_add(&req->req_list,kisofsd_req_list.prev); + wake_up(&kisofsd_wait); + spin_unlock(&kisofsd_req_lock); + + return 0; +} + +static void readpage_form2_real(struct inode* inode, struct page* page) +{ + char *buf = (char *)page_address(page); + int start_sector, start_byte; + int stop_sector, stop_byte; + int sector; + int offset = page->index << PAGE_CACHE_SHIFT;; + + // the code is adapted from cdXA.c of cdfs-0.41 + + start_sector = offset / FORM2_DATA_SIZE; + start_byte = offset % FORM2_DATA_SIZE; + stop_sector = (offset + PAGE_SIZE) / FORM2_DATA_SIZE; + stop_byte = (offset + PAGE_SIZE) % FORM2_DATA_SIZE; + + if(stop_byte == 0) + { + --stop_sector; + stop_byte = FORM2_DATA_SIZE; + } + + for(sector = start_sector; sector <= stop_sector; sector++) + { + int copy_length; + unsigned char *data; + unsigned char *raw_sector = cd_read_raw_sector(inode,sector); + if(raw_sector == NULL) break; + + data = raw_sector + 24; + if(sector == start_sector) + { + data += start_byte; + if(sector != stop_sector) + copy_length = FORM2_DATA_SIZE - start_byte; + else + copy_length = stop_byte - start_byte; + } + else if (sector == stop_sector) + copy_length = stop_byte; + else + copy_length = FORM2_DATA_SIZE; + + memcpy(buf,data,copy_length); + buf += copy_length; + kfree(raw_sector); + } + + if(sector > stop_sector) + set_bit(PG_uptodate, &page->flags); + else + set_bit(PG_error, &page->flags); + clear_bit(PG_locked, &page->flags); + wake_up(&page->wait); +} + +static void kisofsd_process_request(void) +{ + spin_lock(&kisofsd_req_lock); + while(!list_empty(&kisofsd_req_list)) + { + struct list_head * tmp; + struct kisofsd_req * req; + + tmp = kisofsd_req_list.next; + list_del(tmp); + + spin_unlock(&kisofsd_req_lock); + req = list_entry(tmp, struct kisofsd_req, req_list); + readpage_form2_real(req->inode, req->page); + kfree(req); + spin_lock(&kisofsd_req_lock); + } + spin_unlock(&kisofsd_req_lock); +} + +static int kisofsd_thread(void *unused) +{ + /* + * This thread doesn't need any user-level access, + * so get rid of all our resources + */ + exit_files(current); /* daemonize doesn't do exit_files */ + daemonize(); + + /* Setup a nice name */ + strcpy(current->comm, "kisofsd"); + + /* Send me a signal to get me die (for debugging) */ + do { + kisofsd_process_request(); + wait_event_interruptible(kisofsd_wait,!list_empty(&kisofsd_req_list)); + } while (!signal_pending(current)); + + kisofsd_running = 0; + return 0; +} + +int readpage_form2(struct file *file, struct page *page) +{ + struct inode *inode = file->f_dentry->d_inode; + if(kisofsd_running) + return kisofsd_add_request(inode, page); + else + return -EIO; +} + static void isofs_read_inode(struct inode * inode) { struct super_block *sb = inode->i_sb; @@ -1239,8 +1435,20 @@ #endif IGNORE_WRONG_MULTI_VOLUME_SPECS { if (S_ISREG(inode->i_mode)) { + unsigned char *buf; inode->i_fop = &generic_ro_fops; inode->i_data.a_ops = &isofs_aops; + buf = cd_read_raw_sector(inode,0); + if(buf) + { + if(buf[15] == 2 && (buf[18]&(1<<5))) + { + inode->i_data.a_ops = &isofs_aops_form2; + inode->i_size = (inode->i_size/2048)*FORM2_DATA_SIZE+ + (inode->i_size%2048); + } + kfree(buf); + } } else if (S_ISDIR(inode->i_mode)) { inode->i_op = &isofs_dir_inode_operations; inode->i_fop = &isofs_dir_operations; @@ -1430,12 +1638,43 @@ static int __init init_iso9660_fs(void) { - return register_filesystem(&iso9660_fs_type); + int err; + err = register_filesystem(&iso9660_fs_type); + if(err < 0) return err; + + last_buf = kmalloc(CD_FRAMESIZE_RAW,GFP_KERNEL); + if(last_buf == NULL) return -ENOMEM; + + kisofsd_pid = kernel_thread(kisofsd_thread, NULL, + CLONE_FS | CLONE_FILES | CLONE_SIGHAND); + if(kisofsd_pid > 0) + { + kisofsd_running = 1; + return 0; + } + + printk("init_iso9660_fs: kernel_thread failed.\n"); + unregister_filesystem(&iso9660_fs_type); + return -1; } static void __exit exit_iso9660_fs(void) { - unregister_filesystem(&iso9660_fs_type); + int ret; + ret = kill_proc(kisofsd_pid, SIGTERM, 1); + if(!ret) { + /* Wait 10 seconds */ + int count = 10 * 100; + while (kisofsd_running && --count) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + } + + if(!count) + printk("Giving up on killing kisofsd !"); + } + unregister_filesystem(&iso9660_fs_type); + kfree(last_buf); } EXPORT_NO_SYMBOLS;