static int ncp_symlink_readpage(struct file *file, struct page *page) { struct inode *inode = page->mapping->host; int error, length, len; char *link, *rawlink; char *buf = kmap(page); error = -ENOMEM; rawlink = kmalloc(NCP_MAX_SYMLINK_SIZE, GFP_KERNEL); if (!rawlink) goto fail; if (ncp_make_open(inode,O_RDONLY)) goto failEIO; error=ncp_read_kernel(NCP_SERVER(inode),NCP_FINFO(inode)->file_handle, 0,NCP_MAX_SYMLINK_SIZE,rawlink,&length); ncp_inode_close(inode); /* Close file handle if no other users... */ ncp_make_closed(inode); if (error) goto failEIO; if (NCP_FINFO(inode)->flags & NCPI_KLUDGE_SYMLINK) { if (length<NCP_MIN_SYMLINK_SIZE || ((__le32 *)rawlink)[0]!=NCP_SYMLINK_MAGIC0 || ((__le32 *)rawlink)[1]!=NCP_SYMLINK_MAGIC1) goto failEIO; link = rawlink + 8; length -= 8; } else { link = rawlink; } len = NCP_MAX_SYMLINK_SIZE; error = ncp_vol2io(NCP_SERVER(inode), buf, &len, link, length, 0); kfree(rawlink); if (error) goto fail; SetPageUptodate(page); kunmap(page); unlock_page(page); return 0; failEIO: error = -EIO; kfree(rawlink); fail: SetPageError(page); kunmap(page); unlock_page(page); return error; }
static struct dentry *ncp_follow_link(struct dentry *dentry, struct dentry *base, unsigned int follow) { struct inode *inode=dentry->d_inode; int error, length, cnt; char *link; #ifdef DEBUG printk("ncp_follow_link(dentry=%p,base=%p,follow=%u)\n",dentry,base,follow); #endif if(!S_ISLNK(inode->i_mode)) { dput(base); return ERR_PTR(-EINVAL); } if(ncp_make_open(inode,O_RDONLY)) { dput(base); return ERR_PTR(-EIO); } for (cnt = 0; (link=(char *)kmalloc(NCP_MAX_SYMLINK_SIZE+1, GFP_NFS))==NULL; cnt++) { if (cnt > 10) { dput(base); return ERR_PTR(-EAGAIN); /* -ENOMEM? */ } schedule(); } error=ncp_read_kernel(NCP_SERVER(inode),NCP_FINFO(inode)->file_handle, 0,NCP_MAX_SYMLINK_SIZE,link,&length); if (error!=0 || length<NCP_MIN_SYMLINK_SIZE || ((__u32 *)link)[0]!=NCP_SYMLINK_MAGIC0 || ((__u32 *)link)[1]!=NCP_SYMLINK_MAGIC1) { dput(base); kfree(link); return ERR_PTR(-EIO); } link[length]=0; vol2io(NCP_SERVER(inode), link+8, 0); /* UPDATE_ATIME(inode); */ base=lookup_dentry(link+8, base, follow); kfree(link); return base; }
static int ncp_readlink(struct dentry * dentry, char * buffer, int buflen) { struct inode *inode=dentry->d_inode; char *link; int length,error; #ifdef DEBUG printk("ncp_readlink(dentry=%p,buffer=%p,buflen=%d)\n",dentry,buffer,buflen); #endif if(!S_ISLNK(inode->i_mode)) return -EINVAL; if(ncp_make_open(inode,O_RDONLY)) return -EIO; if((link=(char *)kmalloc(NCP_MAX_SYMLINK_SIZE+1,GFP_NFS))==NULL) return -ENOMEM; error = ncp_read_kernel(NCP_SERVER(inode),NCP_FINFO(inode)->file_handle, 0,NCP_MAX_SYMLINK_SIZE,link,&length); if (error!=0 || length < NCP_MIN_SYMLINK_SIZE || buflen < (length-8) || ((__u32 *)link)[0]!=NCP_SYMLINK_MAGIC0 ||((__u32 *)link)[1]!=NCP_SYMLINK_MAGIC1) { error = -EIO; goto out; } link[length] = 0; vol2io(NCP_SERVER(inode), link+8, 0); error = length - 8; if(copy_to_user(buffer, link+8, error)) error = -EFAULT; out:; kfree(link); return error; }
/* * Fill in the supplied page for mmap * XXX: how are we excluding truncate/invalidate here? Maybe need to lock * page? */ static int ncp_file_mmap_fault(struct vm_area_struct *area, struct vm_fault *vmf) { struct file *file = area->vm_file; struct dentry *dentry = file->f_path.dentry; struct inode *inode = dentry->d_inode; char *pg_addr; unsigned int already_read; unsigned int count; int bufsize; int pos; /* XXX: loff_t ? */ /* * ncpfs has nothing against high pages as long * as recvmsg and memset works on it */ vmf->page = alloc_page(GFP_HIGHUSER); if (!vmf->page) return VM_FAULT_OOM; pg_addr = kmap(vmf->page); pos = vmf->pgoff << PAGE_SHIFT; count = PAGE_SIZE; /* what we can read in one go */ bufsize = NCP_SERVER(inode)->buffer_size; already_read = 0; if (ncp_make_open(inode, O_RDONLY) >= 0) { while (already_read < count) { int read_this_time; int to_read; to_read = bufsize - (pos % bufsize); to_read = min_t(unsigned int, to_read, count - already_read); if (ncp_read_kernel(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle, pos, to_read, pg_addr + already_read, &read_this_time) != 0) { read_this_time = 0; } pos += read_this_time; already_read += read_this_time; if (read_this_time < to_read) { break; } } ncp_inode_close(inode); } if (already_read < PAGE_SIZE) memset(pg_addr + already_read, 0, PAGE_SIZE - already_read); flush_dcache_page(vmf->page); kunmap(vmf->page); /* * If I understand ncp_read_kernel() properly, the above always * fetches from the network, here the analogue of disk. * -- nyc */ count_vm_event(PGMAJFAULT); mem_cgroup_count_vm_event(area->vm_mm, PGMAJFAULT); return VM_FAULT_MAJOR; }