void ncp_update_inode(struct inode *inode, struct ncp_entry_info *nwinfo) { ncp_update_dirent(inode, nwinfo); NCP_FINFO(inode)->nwattr = nwinfo->i.attributes; NCP_FINFO(inode)->access = nwinfo->access; memcpy(NCP_FINFO(inode)->file_handle, nwinfo->file_handle, sizeof(nwinfo->file_handle)); DPRINTK("ncp_update_inode: updated %s, volnum=%d, dirent=%u\n", nwinfo->i.entryName, NCP_FINFO(inode)->volNumber, NCP_FINFO(inode)->dirEntNum); }
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; }
void ncp_update_inode2(struct inode* inode, struct ncp_entry_info *nwinfo) { NCP_FINFO(inode)->flags = 0; if (!atomic_read(&NCP_FINFO(inode)->opened)) { NCP_FINFO(inode)->nwattr = nwinfo->i.attributes; ncp_update_attrs(inode, nwinfo); } ncp_update_dates(inode, &nwinfo->i); ncp_update_dirent(inode, nwinfo); }
/* * Defer release of inode_info and file_info structures until the inode * has been cleared. This avoids a race condition allowing the inode to * be put back in use before being cleared. Also, temporarily increment * i_count after clear_inode() so that the inode can't be reused. */ static void ncp_put_inode(struct inode *inode) { struct super_block *sb = inode->i_sb; struct ncp_server *server = NCP_SERVER(inode); struct ncp_inode_info *iinfo = NCP_INOP(inode); struct nw_file_info *finfo = NCP_FINFO(inode); /* * This operation may block, so we lock before checking the count. */ lock_super(sb); if (inode->i_count > 1) { printk("ncp_put_inode: inode in use device %s, inode %ld, count=%ld\n", kdevname(inode->i_dev), inode->i_ino, inode->i_count); goto unlock; } DDPRINTK("ncp_put_inode: put %s\n", finfo->i.entryName); /* * This operation should never block. */ if (S_ISDIR(inode->i_mode)) { DDPRINTK("ncp_put_inode: put directory %ld\n", inode->i_ino); ncp_invalid_dir_cache(inode); } clear_inode(inode); /* * After clearing the inode i_count will be 0 in 2.0.xx kernels. * To keep the inode from being reused as free if we block while * closing the file, increment i_count temporarily. */ inode->i_count++; if (finfo->opened != 0) { if (ncp_close_file(server, finfo->file_handle) != 0) { /* We can't do anything but complain. */ printk("ncp_put_inode: could not close %s\n", finfo->i.entryName); } } ncp_free_inode_info(iinfo); inode->i_count--; unlock: unlock_super(sb); }
/* * Fill in the ncpfs-specific information in the inode. */ void ncp_update_inode(struct inode *inode, struct nw_file_info *nwinfo) { NCP_FINFO(inode)->DosDirNum = nwinfo->i.DosDirNum; NCP_FINFO(inode)->dirEntNum = nwinfo->i.dirEntNum; NCP_FINFO(inode)->volNumber = nwinfo->i.volNumber; NCP_FINFO(inode)->opened = nwinfo->opened; NCP_FINFO(inode)->access = nwinfo->access; NCP_FINFO(inode)->server_file_handle = nwinfo->server_file_handle; memcpy(NCP_FINFO(inode)->file_handle, nwinfo->file_handle, sizeof(nwinfo->file_handle)); #ifdef NCPFS_DEBUG_VERBOSE printk(KERN_DEBUG "ncp_update_inode: updated %s, volnum=%d, dirent=%u\n", nwinfo->i.entryName, NCP_FINFO(inode)->volNumber, NCP_FINFO(inode)->dirEntNum); #endif }
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 void ncp_delete_inode(struct inode *inode) { if (S_ISDIR(inode->i_mode)) { DDPRINTK(KERN_DEBUG "ncp_delete_inode: put directory %ld\n", inode->i_ino); ncp_invalid_dir_cache(inode); } if (NCP_FINFO(inode)->opened && ncp_make_closed(inode) != 0) { /* We can't do anything but complain. */ printk(KERN_ERR "ncp_delete_inode: could not close\n"); } clear_inode(inode); }
int ncp_make_open(struct inode *i, int right) { struct nw_file_info *finfo; if (i == NULL) { printk("ncp_make_open: got NULL inode\n"); return -EINVAL; } finfo = NCP_FINFO(i); DPRINTK("ncp_make_open: dirent->opened = %d\n", finfo->opened); lock_super(i->i_sb); if (finfo->opened == 0) { finfo->access = -1; /* tries max. rights */ if (ncp_open_create_file_or_subdir(NCP_SERVER(i), NULL, NULL, OC_MODE_OPEN, 0, AR_READ | AR_WRITE, finfo) == 0) { finfo->access = O_RDWR; } else if (ncp_open_create_file_or_subdir(NCP_SERVER(i), NULL, NULL, OC_MODE_OPEN, 0, AR_READ, finfo) == 0) { finfo->access = O_RDONLY; } } unlock_super(i->i_sb); if ( ((right == O_RDONLY) && ( (finfo->access == O_RDONLY) || (finfo->access == O_RDWR))) || ((right == O_WRONLY) && ( (finfo->access == O_WRONLY) || (finfo->access == O_RDWR))) || ((right == O_RDWR) && (finfo->access == O_RDWR))) return 0; return -EACCES; }
void ncp_update_inode2(struct inode* inode, struct nw_file_info *nwinfo) { struct nw_info_struct *nwi = &nwinfo->i; struct ncp_server *server = NCP_SERVER(inode); if (!NCP_FINFO(inode)->opened) { if (nwi->attributes & aDIR) { inode->i_mode = server->m.dir_mode; inode->i_size = 512; } else { inode->i_mode = server->m.file_mode; inode->i_size = le32_to_cpu(nwi->dataStreamSize); } if (nwi->attributes & aRONLY) inode->i_mode &= ~0222; } inode->i_blocks = 0; if ((inode->i_size)&&(inode->i_blksize)) { inode->i_blocks = (inode->i_size-1)/(inode->i_blksize)+1; } /* TODO: times? I'm not sure... */ NCP_FINFO(inode)->DosDirNum = nwinfo->i.DosDirNum; NCP_FINFO(inode)->dirEntNum = nwinfo->i.dirEntNum; NCP_FINFO(inode)->volNumber = nwinfo->i.volNumber; }
static void ncp_update_attrs(struct inode *inode, struct ncp_entry_info *nwinfo) { struct nw_info_struct *nwi = &nwinfo->i; struct ncp_server *server = NCP_SERVER(inode); if (nwi->attributes & aDIR) { inode->i_mode = server->m.dir_mode; /* for directories dataStreamSize seems to be some Object ID ??? */ i_size_write(inode, NCP_BLOCK_SIZE); } else { u32 size; inode->i_mode = server->m.file_mode; size = le32_to_cpu(nwi->dataStreamSize); i_size_write(inode, size); #ifdef CONFIG_NCPFS_EXTRAS if ((server->m.flags & (NCP_MOUNT_EXTRAS|NCP_MOUNT_SYMLINKS)) && (nwi->attributes & aSHARED)) { switch (nwi->attributes & (aHIDDEN|aSYSTEM)) { case aHIDDEN: if (server->m.flags & NCP_MOUNT_SYMLINKS) { if (/* (size >= NCP_MIN_SYMLINK_SIZE) && */ (size <= NCP_MAX_SYMLINK_SIZE)) { inode->i_mode = (inode->i_mode & ~S_IFMT) | S_IFLNK; NCP_FINFO(inode)->flags |= NCPI_KLUDGE_SYMLINK; break; } } /* FALLTHROUGH */ case 0: if (server->m.flags & NCP_MOUNT_EXTRAS) inode->i_mode |= S_IRUGO; break; case aSYSTEM: if (server->m.flags & NCP_MOUNT_EXTRAS) inode->i_mode |= (inode->i_mode >> 2) & S_IXUGO; break; /* case aSYSTEM|aHIDDEN: */ default: /* reserved combination */ break; } } #endif } if (nwi->attributes & aRONLY) inode->i_mode &= ~S_IWUGO; }
/* * Fill in the inode based on the ncp_entry_info structure. */ static void ncp_set_attr(struct inode *inode, struct ncp_entry_info *nwinfo) { struct ncp_server *server = NCP_SERVER(inode); NCP_FINFO(inode)->flags = 0; ncp_update_attrs(inode, nwinfo); DDPRINTK("ncp_read_inode: inode->i_mode = %u\n", inode->i_mode); inode->i_nlink = 1; inode->i_uid = server->m.uid; inode->i_gid = server->m.gid; ncp_update_dates(inode, &nwinfo->i); ncp_update_inode(inode, nwinfo); }
int ncp_symlink(struct inode *dir, struct dentry *dentry, const char *symname) { int i,length; struct inode *inode; char *link; #ifdef DEBUG printk("ncp_symlink(dir=%p,dentry=%p,symname=%s)\n",dir,dentry,symname); #endif if (!(NCP_SERVER(dir)->m.flags & NCP_MOUNT_SYMLINKS)) return -EPERM; /* EPERM is returned by VFS if symlink procedure does not exist */ if ((length=strlen(symname))>NCP_MAX_SYMLINK_SIZE) return -EINVAL; if ((link=(char *)kmalloc(length+9,GFP_NFS))==NULL) return -ENOMEM; if (ncp_create_new(dir,dentry,0,aSHARED|aHIDDEN)) { kfree(link); return -EIO; } inode=dentry->d_inode; ((__u32 *)link)[0]=NCP_SYMLINK_MAGIC0; ((__u32 *)link)[1]=NCP_SYMLINK_MAGIC1; memcpy(link+8, symname, length+1); /* including last zero for io2vol */ /* map to/from server charset, do not touch upper/lower case as symlink can point out of ncp filesystem */ io2vol(NCP_SERVER(inode), link+8, 0); if(ncp_write_kernel(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle, 0, length+8, link, &i) || i!=length+8) { kfree(link); return -EIO; } kfree(link); return 0; }
/* * Get a new inode. */ struct inode * ncp_iget(struct super_block *sb, struct ncp_entry_info *info) { struct inode *inode; if (info == NULL) { printk(KERN_ERR "ncp_iget: info is NULL\n"); return NULL; } inode = new_inode(sb); if (inode) { atomic_set(&NCP_FINFO(inode)->opened, info->opened); inode->i_mapping->backing_dev_info = sb->s_bdi; inode->i_ino = info->ino; ncp_set_attr(inode, info); if (S_ISREG(inode->i_mode)) { inode->i_op = &ncp_file_inode_operations; inode->i_fop = &ncp_file_operations; } else if (S_ISDIR(inode->i_mode)) { inode->i_op = &ncp_dir_inode_operations; inode->i_fop = &ncp_dir_operations; #ifdef CONFIG_NCPFS_NFS_NS } else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) || S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) { init_special_inode(inode, inode->i_mode, new_decode_dev(info->i.nfs.rdev)); #endif #if defined(CONFIG_NCPFS_EXTRAS) || defined(CONFIG_NCPFS_NFS_NS) } else if (S_ISLNK(inode->i_mode)) { inode->i_op = &ncp_symlink_inode_operations; inode->i_data.a_ops = &ncp_symlink_aops; #endif } else { make_bad_inode(inode); } insert_inode_hash(inode); } else printk(KERN_ERR "ncp_iget: iget failed!\n"); return inode; }
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; }
static ssize_t ncp_file_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { struct dentry *dentry = file->f_path.dentry; struct inode *inode = dentry->d_inode; size_t already_written = 0; off_t pos; size_t bufsize; int errno; void* bouncebuffer; DPRINTK("ncp_file_write: enter %s/%s\n", dentry->d_parent->d_name.name, dentry->d_name.name); if ((ssize_t) count < 0) return -EINVAL; pos = *ppos; if (file->f_flags & O_APPEND) { pos = i_size_read(inode); } if (pos + count > MAX_NON_LFS && !(file->f_flags&O_LARGEFILE)) { if (pos >= MAX_NON_LFS) { return -EFBIG; } if (count > MAX_NON_LFS - (u32)pos) { count = MAX_NON_LFS - (u32)pos; } } if (pos >= inode->i_sb->s_maxbytes) { if (count || pos > inode->i_sb->s_maxbytes) { return -EFBIG; } } if (pos + count > inode->i_sb->s_maxbytes) { count = inode->i_sb->s_maxbytes - pos; } if (!count) return 0; errno = ncp_make_open(inode, O_WRONLY); if (errno) { DPRINTK(KERN_ERR "ncp_file_write: open failed, error=%d\n", errno); return errno; } bufsize = NCP_SERVER(inode)->buffer_size; already_written = 0; bouncebuffer = vmalloc(bufsize); if (!bouncebuffer) { errno = -EIO; /* -ENOMEM */ goto outrel; } while (already_written < count) { int written_this_time; size_t to_write = min_t(unsigned int, bufsize - (pos % bufsize), count - already_written); if (copy_from_user(bouncebuffer, buf, to_write)) { errno = -EFAULT; break; } if (ncp_write_kernel(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle, pos, to_write, bouncebuffer, &written_this_time) != 0) { errno = -EIO; break; } pos += written_this_time; buf += written_this_time; already_written += written_this_time; if (written_this_time != to_write) { break; } } vfree(bouncebuffer); file_update_time(file); *ppos = pos; if (pos > i_size_read(inode)) { mutex_lock(&inode->i_mutex); if (pos > i_size_read(inode)) i_size_write(inode, pos); mutex_unlock(&inode->i_mutex); } DPRINTK("ncp_file_write: exit %s/%s\n", dentry->d_parent->d_name.name, dentry->d_name.name); outrel: ncp_inode_close(inode); return already_written ? already_written : errno; }
/* * Fill in the ncpfs-specific information in the inode. */ static void ncp_update_dirent(struct inode *inode, struct ncp_entry_info *nwinfo) { NCP_FINFO(inode)->DosDirNum = nwinfo->i.DosDirNum; NCP_FINFO(inode)->dirEntNum = nwinfo->i.dirEntNum; NCP_FINFO(inode)->volNumber = nwinfo->volume; }
static void ncp_i_callback(struct rcu_head *head) { struct inode *inode = container_of(head, struct inode, i_rcu); kmem_cache_free(ncp_inode_cachep, NCP_FINFO(inode)); }
static ssize_t ncp_file_read_iter(struct kiocb *iocb, struct iov_iter *to) { struct file *file = iocb->ki_filp; struct inode *inode = file_inode(file); size_t already_read = 0; off_t pos = iocb->ki_pos; size_t bufsize; int error; void *freepage; size_t freelen; ncp_dbg(1, "enter %pD2\n", file); if (!iov_iter_count(to)) return 0; if (pos > inode->i_sb->s_maxbytes) return 0; iov_iter_truncate(to, inode->i_sb->s_maxbytes - pos); error = ncp_make_open(inode, O_RDONLY); if (error) { ncp_dbg(1, "open failed, error=%d\n", error); return error; } bufsize = NCP_SERVER(inode)->buffer_size; error = -EIO; freelen = ncp_read_bounce_size(bufsize); freepage = vmalloc(freelen); if (!freepage) goto outrel; error = 0; /* First read in as much as possible for each bufsize. */ while (iov_iter_count(to)) { int read_this_time; size_t to_read = min_t(size_t, bufsize - (pos % bufsize), iov_iter_count(to)); error = ncp_read_bounce(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle, pos, to_read, to, &read_this_time, freepage, freelen); if (error) { error = -EIO; /* NW errno -> Linux errno */ break; } pos += read_this_time; already_read += read_this_time; if (read_this_time != to_read) break; } vfree(freepage); iocb->ki_pos = pos; file_accessed(file); ncp_dbg(1, "exit %pD2\n", file); outrel: ncp_inode_close(inode); return already_read ? already_read : error; }
static ssize_t ncp_file_write_iter(struct kiocb *iocb, struct iov_iter *from) { struct file *file = iocb->ki_filp; struct inode *inode = file_inode(file); size_t already_written = 0; size_t bufsize; int errno; void *bouncebuffer; off_t pos; ncp_dbg(1, "enter %pD2\n", file); errno = generic_write_checks(iocb, from); if (errno <= 0) return errno; errno = ncp_make_open(inode, O_WRONLY); if (errno) { ncp_dbg(1, "open failed, error=%d\n", errno); return errno; } bufsize = NCP_SERVER(inode)->buffer_size; errno = file_update_time(file); if (errno) goto outrel; bouncebuffer = vmalloc(bufsize); if (!bouncebuffer) { errno = -EIO; /* -ENOMEM */ goto outrel; } pos = iocb->ki_pos; while (iov_iter_count(from)) { int written_this_time; size_t to_write = min_t(size_t, bufsize - (pos % bufsize), iov_iter_count(from)); if (copy_from_iter(bouncebuffer, to_write, from) != to_write) { errno = -EFAULT; break; } if (ncp_write_kernel(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle, pos, to_write, bouncebuffer, &written_this_time) != 0) { errno = -EIO; break; } pos += written_this_time; already_written += written_this_time; if (written_this_time != to_write) break; } vfree(bouncebuffer); iocb->ki_pos = pos; if (pos > i_size_read(inode)) { mutex_lock(&inode->i_mutex); if (pos > i_size_read(inode)) i_size_write(inode, pos); mutex_unlock(&inode->i_mutex); } ncp_dbg(1, "exit %pD2\n", file); outrel: ncp_inode_close(inode); return already_written ? already_written : errno; }
static ssize_t ncp_file_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct dentry *dentry = file->f_path.dentry; struct inode *inode = dentry->d_inode; size_t already_read = 0; off_t pos; size_t bufsize; int error; void* freepage; size_t freelen; DPRINTK("ncp_file_read: enter %s/%s\n", dentry->d_parent->d_name.name, dentry->d_name.name); pos = *ppos; if ((ssize_t) count < 0) { return -EINVAL; } if (!count) return 0; if (pos > inode->i_sb->s_maxbytes) return 0; if (pos + count > inode->i_sb->s_maxbytes) { count = inode->i_sb->s_maxbytes - pos; } error = ncp_make_open(inode, O_RDONLY); if (error) { DPRINTK(KERN_ERR "ncp_file_read: open failed, error=%d\n", error); return error; } bufsize = NCP_SERVER(inode)->buffer_size; error = -EIO; freelen = ncp_read_bounce_size(bufsize); freepage = vmalloc(freelen); if (!freepage) goto outrel; error = 0; /* First read in as much as possible for each bufsize. */ while (already_read < count) { int read_this_time; size_t to_read = min_t(unsigned int, bufsize - (pos % bufsize), count - already_read); error = ncp_read_bounce(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle, pos, to_read, buf, &read_this_time, freepage, freelen); if (error) { error = -EIO; /* NW errno -> Linux errno */ break; } pos += read_this_time; buf += read_this_time; already_read += read_this_time; if (read_this_time != to_read) { break; } } vfree(freepage); *ppos = pos; file_accessed(file); DPRINTK("ncp_file_read: exit %s/%s\n", dentry->d_parent->d_name.name, dentry->d_name.name); outrel: ncp_inode_close(inode); return already_read ? already_read : error; }
static int ncp_statfs(struct dentry *dentry, struct kstatfs *buf) { struct dentry* d; struct inode* i; struct ncp_inode_info* ni; struct ncp_server* s; struct ncp_volume_info vi; struct super_block *sb = dentry->d_sb; int err; __u8 dh; d = sb->s_root; if (!d) { goto dflt; } i = d->d_inode; if (!i) { goto dflt; } ni = NCP_FINFO(i); if (!ni) { goto dflt; } s = NCP_SBP(sb); if (!s) { goto dflt; } if (!s->m.mounted_vol[0]) { goto dflt; } err = ncp_dirhandle_alloc(s, ni->volNumber, ni->DosDirNum, &dh); if (err) { goto dflt; } err = ncp_get_directory_info(s, dh, &vi); ncp_dirhandle_free(s, dh); if (err) { goto dflt; } buf->f_type = NCP_SUPER_MAGIC; buf->f_bsize = vi.sectors_per_block * 512; buf->f_blocks = vi.total_blocks; buf->f_bfree = vi.free_blocks; buf->f_bavail = vi.free_blocks; buf->f_files = vi.total_dir_entries; buf->f_ffree = vi.available_dir_entries; buf->f_namelen = 12; return 0; /* We cannot say how much disk space is left on a mounted NetWare Server, because free space is distributed over volumes, and the current user might have disk quotas. So free space is not that simple to determine. Our decision here is to err conservatively. */ dflt:; buf->f_type = NCP_SUPER_MAGIC; buf->f_bsize = NCP_BLOCK_SIZE; buf->f_blocks = 0; buf->f_bfree = 0; buf->f_bavail = 0; buf->f_namelen = 12; return 0; }
static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent) { struct ncp_mount_data_kernel data; struct ncp_server *server; struct file *ncp_filp; struct inode *root_inode; struct inode *sock_inode; struct socket *sock; int error; int default_bufsize; #ifdef CONFIG_NCPFS_PACKET_SIGNING int options; #endif struct ncp_entry_info finfo; data.wdog_pid = NULL; server = kzalloc(sizeof(struct ncp_server), GFP_KERNEL); if (!server) return -ENOMEM; sb->s_fs_info = server; error = -EFAULT; if (raw_data == NULL) goto out; switch (*(int*)raw_data) { case NCP_MOUNT_VERSION: { struct ncp_mount_data* md = (struct ncp_mount_data*)raw_data; data.flags = md->flags; data.int_flags = NCP_IMOUNT_LOGGEDIN_POSSIBLE; data.mounted_uid = md->mounted_uid; data.wdog_pid = find_get_pid(md->wdog_pid); data.ncp_fd = md->ncp_fd; data.time_out = md->time_out; data.retry_count = md->retry_count; data.uid = md->uid; data.gid = md->gid; data.file_mode = md->file_mode; data.dir_mode = md->dir_mode; data.info_fd = -1; memcpy(data.mounted_vol, md->mounted_vol, NCP_VOLNAME_LEN+1); } break; case NCP_MOUNT_VERSION_V4: { struct ncp_mount_data_v4* md = (struct ncp_mount_data_v4*)raw_data; data.flags = md->flags; data.int_flags = 0; data.mounted_uid = md->mounted_uid; data.wdog_pid = find_get_pid(md->wdog_pid); data.ncp_fd = md->ncp_fd; data.time_out = md->time_out; data.retry_count = md->retry_count; data.uid = md->uid; data.gid = md->gid; data.file_mode = md->file_mode; data.dir_mode = md->dir_mode; data.info_fd = -1; data.mounted_vol[0] = 0; } break; default: error = -ECHRNG; if (memcmp(raw_data, "vers", 4) == 0) { error = ncp_parse_options(&data, raw_data); } if (error) goto out; break; } error = -EBADF; ncp_filp = fget(data.ncp_fd); if (!ncp_filp) goto out; error = -ENOTSOCK; sock_inode = ncp_filp->f_path.dentry->d_inode; if (!S_ISSOCK(sock_inode->i_mode)) goto out_fput; sock = SOCKET_I(sock_inode); if (!sock) goto out_fput; if (sock->type == SOCK_STREAM) default_bufsize = 0xF000; else default_bufsize = 1024; sb->s_flags |= MS_NODIRATIME; /* probably even noatime */ sb->s_maxbytes = 0xFFFFFFFFU; sb->s_blocksize = 1024; /* Eh... Is this correct? */ sb->s_blocksize_bits = 10; sb->s_magic = NCP_SUPER_MAGIC; sb->s_op = &ncp_sops; server = NCP_SBP(sb); memset(server, 0, sizeof(*server)); server->ncp_filp = ncp_filp; server->ncp_sock = sock; if (data.info_fd != -1) { struct socket *info_sock; error = -EBADF; server->info_filp = fget(data.info_fd); if (!server->info_filp) goto out_fput; error = -ENOTSOCK; sock_inode = server->info_filp->f_path.dentry->d_inode; if (!S_ISSOCK(sock_inode->i_mode)) goto out_fput2; info_sock = SOCKET_I(sock_inode); if (!info_sock) goto out_fput2; error = -EBADFD; if (info_sock->type != SOCK_STREAM) goto out_fput2; server->info_sock = info_sock; } /* server->lock = 0; */ mutex_init(&server->mutex); server->packet = NULL; /* server->buffer_size = 0; */ /* server->conn_status = 0; */ /* server->root_dentry = NULL; */ /* server->root_setuped = 0; */ #ifdef CONFIG_NCPFS_PACKET_SIGNING /* server->sign_wanted = 0; */ /* server->sign_active = 0; */ #endif server->auth.auth_type = NCP_AUTH_NONE; /* server->auth.object_name_len = 0; */ /* server->auth.object_name = NULL; */ /* server->auth.object_type = 0; */ /* server->priv.len = 0; */ /* server->priv.data = NULL; */ server->m = data; /* Althought anything producing this is buggy, it happens now because of PATH_MAX changes.. */ if (server->m.time_out < 1) { server->m.time_out = 10; printk(KERN_INFO "You need to recompile your ncpfs utils..\n"); } server->m.time_out = server->m.time_out * HZ / 100; server->m.file_mode = (server->m.file_mode & S_IRWXUGO) | S_IFREG; server->m.dir_mode = (server->m.dir_mode & S_IRWXUGO) | S_IFDIR; #ifdef CONFIG_NCPFS_NLS /* load the default NLS charsets */ server->nls_vol = load_nls_default(); server->nls_io = load_nls_default(); #endif /* CONFIG_NCPFS_NLS */ server->dentry_ttl = 0; /* no caching */ INIT_LIST_HEAD(&server->tx.requests); mutex_init(&server->rcv.creq_mutex); server->tx.creq = NULL; server->rcv.creq = NULL; server->data_ready = sock->sk->sk_data_ready; server->write_space = sock->sk->sk_write_space; server->error_report = sock->sk->sk_error_report; sock->sk->sk_user_data = server; init_timer(&server->timeout_tm); #undef NCP_PACKET_SIZE #define NCP_PACKET_SIZE 131072 error = -ENOMEM; server->packet_size = NCP_PACKET_SIZE; server->packet = vmalloc(NCP_PACKET_SIZE); if (server->packet == NULL) goto out_nls; server->txbuf = vmalloc(NCP_PACKET_SIZE); if (server->txbuf == NULL) goto out_packet; server->rxbuf = vmalloc(NCP_PACKET_SIZE); if (server->rxbuf == NULL) goto out_txbuf; sock->sk->sk_data_ready = ncp_tcp_data_ready; sock->sk->sk_error_report = ncp_tcp_error_report; if (sock->type == SOCK_STREAM) { server->rcv.ptr = (unsigned char*)&server->rcv.buf; server->rcv.len = 10; server->rcv.state = 0; INIT_WORK(&server->rcv.tq, ncp_tcp_rcv_proc); INIT_WORK(&server->tx.tq, ncp_tcp_tx_proc); sock->sk->sk_write_space = ncp_tcp_write_space; } else { INIT_WORK(&server->rcv.tq, ncpdgram_rcv_proc); INIT_WORK(&server->timeout_tq, ncpdgram_timeout_proc); server->timeout_tm.data = (unsigned long)server; server->timeout_tm.function = ncpdgram_timeout_call; } ncp_lock_server(server); error = ncp_connect(server); ncp_unlock_server(server); if (error < 0) goto out_rxbuf; DPRINTK("ncp_fill_super: NCP_SBP(sb) = %x\n", (int) NCP_SBP(sb)); error = -EMSGSIZE; /* -EREMOTESIDEINCOMPATIBLE */ #ifdef CONFIG_NCPFS_PACKET_SIGNING if (ncp_negotiate_size_and_options(server, default_bufsize, NCP_DEFAULT_OPTIONS, &(server->buffer_size), &options) == 0) { if (options != NCP_DEFAULT_OPTIONS) { if (ncp_negotiate_size_and_options(server, default_bufsize, options & 2, &(server->buffer_size), &options) != 0) { goto out_disconnect; } } if (options & 2) server->sign_wanted = 1; } else #endif /* CONFIG_NCPFS_PACKET_SIGNING */ if (ncp_negotiate_buffersize(server, default_bufsize, &(server->buffer_size)) != 0) goto out_disconnect; DPRINTK("ncpfs: bufsize = %d\n", server->buffer_size); memset(&finfo, 0, sizeof(finfo)); finfo.i.attributes = aDIR; finfo.i.dataStreamSize = 0; /* ignored */ finfo.i.dirEntNum = 0; finfo.i.DosDirNum = 0; #ifdef CONFIG_NCPFS_SMALLDOS finfo.i.NSCreator = NW_NS_DOS; #endif finfo.volume = NCP_NUMBER_OF_VOLUMES; /* set dates of mountpoint to Jan 1, 1986; 00:00 */ finfo.i.creationTime = finfo.i.modifyTime = cpu_to_le16(0x0000); finfo.i.creationDate = finfo.i.modifyDate = finfo.i.lastAccessDate = cpu_to_le16(0x0C21); finfo.i.nameLen = 0; finfo.i.entryName[0] = '\0'; finfo.opened = 0; finfo.ino = 2; /* tradition */ server->name_space[finfo.volume] = NW_NS_DOS; error = -ENOMEM; root_inode = ncp_iget(sb, &finfo); if (!root_inode) goto out_disconnect; DPRINTK("ncp_fill_super: root vol=%d\n", NCP_FINFO(root_inode)->volNumber); sb->s_root = d_alloc_root(root_inode); if (!sb->s_root) goto out_no_root; sb->s_root->d_op = &ncp_root_dentry_operations; return 0; out_no_root: iput(root_inode); out_disconnect: ncp_lock_server(server); ncp_disconnect(server); ncp_unlock_server(server); out_rxbuf: ncp_stop_tasks(server); vfree(server->rxbuf); out_txbuf: vfree(server->txbuf); out_packet: vfree(server->packet); out_nls: #ifdef CONFIG_NCPFS_NLS unload_nls(server->nls_io); unload_nls(server->nls_vol); #endif out_fput2: if (server->info_filp) fput(server->info_filp); out_fput: /* 23/12/1998 Marcin Dalecki <*****@*****.**>: * * The previously used put_filp(ncp_filp); was bogous, since * it doesn't proper unlocking. */ fput(ncp_filp); out: put_pid(data.wdog_pid); sb->s_fs_info = NULL; kfree(server); return error; }
static int ncp_file_write(struct inode *inode, struct file *file, const char *buf, int count) { int bufsize, already_written; off_t pos; int errno; if (inode == NULL) { DPRINTK("ncp_file_write: inode = NULL\n"); return -EINVAL; } if (!ncp_conn_valid(NCP_SERVER(inode))) { return -EIO; } if (!S_ISREG(inode->i_mode)) { DPRINTK("ncp_file_write: write to non-file, mode %07o\n", inode->i_mode); return -EINVAL; } DPRINTK("ncp_file_write: enter %s\n", NCP_ISTRUCT(inode)->entryName); if (count <= 0) { return 0; } if ((errno = ncp_make_open(inode, O_RDWR)) != 0) { return errno; } pos = file->f_pos; if (file->f_flags & O_APPEND) { pos = inode->i_size; } bufsize = NCP_SERVER(inode)->buffer_size; already_written = 0; while (already_written < count) { int written_this_time; int to_write = min(bufsize - (pos % bufsize), count - already_written); if (ncp_write(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle, pos, to_write, buf, &written_this_time) != 0) { return -EIO; } pos += written_this_time; buf += written_this_time; already_written += written_this_time; if (written_this_time < to_write) { break; } } inode->i_mtime = inode->i_ctime = CURRENT_TIME; inode->i_dirt = 1; file->f_pos = pos; if (pos > inode->i_size) { inode->i_size = pos; ncp_invalid_dir_cache(NCP_INOP(inode)->dir->inode); } DPRINTK("ncp_file_write: exit %s\n", NCP_ISTRUCT(inode)->entryName); return already_written; }
static ssize_t ncp_file_read(struct file *file, char *buf, size_t count, loff_t *ppos) { struct dentry *dentry = file->f_dentry; struct inode *inode = dentry->d_inode; size_t already_read = 0; off_t pos; size_t bufsize; int error; void* freepage; size_t freelen; DPRINTK("ncp_file_read: enter %s/%s\n", dentry->d_parent->d_name.name, dentry->d_name.name); error = -EIO; if (!ncp_conn_valid(NCP_SERVER(inode))) goto out; error = -EINVAL; if (!S_ISREG(inode->i_mode)) { DPRINTK("ncp_file_read: read from non-file, mode %07o\n", inode->i_mode); goto out; } pos = *ppos; /* leave it out on server ... if (pos + count > inode->i_size) { count = inode->i_size - pos; } */ error = 0; if (!count) /* size_t is never < 0 */ goto out; error = ncp_make_open(inode, O_RDONLY); if (error) { DPRINTK(KERN_ERR "ncp_file_read: open failed, error=%d\n", error); goto out; } bufsize = NCP_SERVER(inode)->buffer_size; error = -EIO; freelen = ncp_read_bounce_size(bufsize); freepage = vmalloc(freelen); if (!freepage) goto outrel; error = 0; /* First read in as much as possible for each bufsize. */ while (already_read < count) { int read_this_time; size_t to_read = min_t(unsigned int, bufsize - (pos % bufsize), count - already_read); error = ncp_read_bounce(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle, pos, to_read, buf, &read_this_time, freepage, freelen); if (error) { error = -EIO; /* NW errno -> Linux errno */ break; } pos += read_this_time; buf += read_this_time; already_read += read_this_time; if (read_this_time != to_read) { break; } } vfree(freepage); *ppos = pos; if (!IS_RDONLY(inode)) { inode->i_atime = CURRENT_TIME; } DPRINTK("ncp_file_read: exit %s/%s\n", dentry->d_parent->d_name.name, dentry->d_name.name); outrel: ncp_inode_close(inode); out: return already_read ? already_read : error; }
/* * Open a file with the specified read/write mode. */ int ncp_make_open(struct inode *inode, int right) { int error; int access; error = -EINVAL; if (!inode) { pr_err("%s: got NULL inode\n", __func__); goto out; } ncp_dbg(1, "opened=%d, volume # %u, dir entry # %u\n", atomic_read(&NCP_FINFO(inode)->opened), NCP_FINFO(inode)->volNumber, NCP_FINFO(inode)->dirEntNum); error = -EACCES; mutex_lock(&NCP_FINFO(inode)->open_mutex); if (!atomic_read(&NCP_FINFO(inode)->opened)) { struct ncp_entry_info finfo; int result; /* tries max. rights */ finfo.access = O_RDWR; result = ncp_open_create_file_or_subdir(NCP_SERVER(inode), inode, NULL, OC_MODE_OPEN, 0, AR_READ | AR_WRITE, &finfo); if (!result) goto update; /* RDWR did not succeeded, try readonly or writeonly as requested */ switch (right) { case O_RDONLY: finfo.access = O_RDONLY; result = ncp_open_create_file_or_subdir(NCP_SERVER(inode), inode, NULL, OC_MODE_OPEN, 0, AR_READ, &finfo); break; case O_WRONLY: finfo.access = O_WRONLY; result = ncp_open_create_file_or_subdir(NCP_SERVER(inode), inode, NULL, OC_MODE_OPEN, 0, AR_WRITE, &finfo); break; } if (result) { ncp_vdbg("failed, result=%d\n", result); goto out_unlock; } /* * Update the inode information. */ update: ncp_update_inode(inode, &finfo); atomic_set(&NCP_FINFO(inode)->opened, 1); } access = NCP_FINFO(inode)->access; ncp_vdbg("file open, access=%x\n", access); if (access == right || access == O_RDWR) { atomic_inc(&NCP_FINFO(inode)->opened); error = 0; } out_unlock: mutex_unlock(&NCP_FINFO(inode)->open_mutex); out: return error; }
static ssize_t ncp_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) { struct dentry *dentry = file->f_dentry; struct inode *inode = dentry->d_inode; size_t already_written = 0; off_t pos; size_t bufsize; int errno; void* bouncebuffer; DPRINTK("ncp_file_write: enter %s/%s\n", dentry->d_parent->d_name.name, dentry->d_name.name); errno = -EIO; if (!ncp_conn_valid(NCP_SERVER(inode))) goto out; if (!S_ISREG(inode->i_mode)) { DPRINTK("ncp_file_write: write to non-file, mode %07o\n", inode->i_mode); return -EINVAL; } errno = 0; if (!count) goto out; errno = ncp_make_open(inode, O_WRONLY); if (errno) { DPRINTK(KERN_ERR "ncp_file_write: open failed, error=%d\n", errno); return errno; } pos = *ppos; if (file->f_flags & O_APPEND) { pos = inode->i_size; } bufsize = NCP_SERVER(inode)->buffer_size; already_written = 0; bouncebuffer = vmalloc(bufsize); if (!bouncebuffer) { errno = -EIO; /* -ENOMEM */ goto outrel; } while (already_written < count) { int written_this_time; size_t to_write = min_t(unsigned int, bufsize - (pos % bufsize), count - already_written); if (copy_from_user(bouncebuffer, buf, to_write)) { errno = -EFAULT; break; } if (ncp_write_kernel(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle, pos, to_write, bouncebuffer, &written_this_time) != 0) { errno = -EIO; break; } pos += written_this_time; buf += written_this_time; already_written += written_this_time; if (written_this_time != to_write) { break; } } vfree(bouncebuffer); inode->i_mtime = inode->i_atime = CURRENT_TIME; *ppos = pos; if (pos > inode->i_size) { inode->i_size = pos; } DPRINTK("ncp_file_write: exit %s/%s\n", dentry->d_parent->d_name.name, dentry->d_name.name); outrel: ncp_inode_close(inode); out: return already_written ? already_written : errno; }
static void ncp_destroy_inode(struct inode *inode) { kmem_cache_free(ncp_inode_cachep, NCP_FINFO(inode)); }
static int ncp_file_read(struct inode *inode, struct file *file, char *buf, int count) { int bufsize, already_read; off_t pos; int errno; DPRINTK("ncp_file_read: enter %s\n", NCP_ISTRUCT(inode)->entryName); if (inode == NULL) { DPRINTK("ncp_file_read: inode = NULL\n"); return -EINVAL; } if (!ncp_conn_valid(NCP_SERVER(inode))) { return -EIO; } if (!S_ISREG(inode->i_mode)) { DPRINTK("ncp_file_read: read from non-file, mode %07o\n", inode->i_mode); return -EINVAL; } pos = file->f_pos; if (pos + count > inode->i_size) { count = inode->i_size - pos; } if (count <= 0) { return 0; } if ((errno = ncp_make_open(inode, O_RDONLY)) != 0) { return errno; } bufsize = NCP_SERVER(inode)->buffer_size; already_read = 0; /* First read in as much as possible for each bufsize. */ while (already_read < count) { int read_this_time; int to_read = min(bufsize - (pos % bufsize), count - already_read); if (ncp_read(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle, pos, to_read, buf, &read_this_time) != 0) { return -EIO; /* This is not exact, i know.. */ } pos += read_this_time; buf += read_this_time; already_read += read_this_time; if (read_this_time < to_read) { break; } } file->f_pos = pos; if (!IS_RDONLY(inode)) { inode->i_atime = CURRENT_TIME; } inode->i_dirt = 1; DPRINTK("ncp_file_read: exit %s\n", NCP_ISTRUCT(inode)->entryName); return already_read; }
int ncp_notify_change(struct dentry *dentry, struct iattr *attr) { struct inode *inode = dentry->d_inode; int result = 0; __le32 info_mask; struct nw_modify_dos_info info; struct ncp_server *server; result = -EIO; lock_kernel(); server = NCP_SERVER(inode); if ((!server) || !ncp_conn_valid(server)) goto out; /* ageing the dentry to force validation */ ncp_age_dentry(server, dentry); result = inode_change_ok(inode, attr); if (result < 0) goto out; result = -EPERM; if (((attr->ia_valid & ATTR_UID) && (attr->ia_uid != server->m.uid))) goto out; if (((attr->ia_valid & ATTR_GID) && (attr->ia_gid != server->m.gid))) goto out; if (((attr->ia_valid & ATTR_MODE) && (attr->ia_mode & ~(S_IFREG | S_IFDIR | S_IRWXUGO)))) goto out; info_mask = 0; memset(&info, 0, sizeof(info)); #if 1 if ((attr->ia_valid & ATTR_MODE) != 0) { umode_t newmode = attr->ia_mode; info_mask |= DM_ATTRIBUTES; if (S_ISDIR(inode->i_mode)) { newmode &= server->m.dir_mode; } else { #ifdef CONFIG_NCPFS_EXTRAS if (server->m.flags & NCP_MOUNT_EXTRAS) { /* any non-default execute bit set */ if (newmode & ~server->m.file_mode & S_IXUGO) info.attributes |= aSHARED | aSYSTEM; /* read for group/world and not in default file_mode */ else if (newmode & ~server->m.file_mode & S_IRUGO) info.attributes |= aSHARED; } else #endif newmode &= server->m.file_mode; } if (newmode & S_IWUGO) info.attributes &= ~(aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT); else info.attributes |= (aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT); #ifdef CONFIG_NCPFS_NFS_NS if (ncp_is_nfs_extras(server, NCP_FINFO(inode)->volNumber)) { result = ncp_modify_nfs_info(server, NCP_FINFO(inode)->volNumber, NCP_FINFO(inode)->dirEntNum, attr->ia_mode, 0); if (result != 0) goto out; info.attributes &= ~(aSHARED | aSYSTEM); { /* mark partial success */ struct iattr tmpattr; tmpattr.ia_valid = ATTR_MODE; tmpattr.ia_mode = attr->ia_mode; result = inode_setattr(inode, &tmpattr); if (result) goto out; } } #endif } #endif /* Do SIZE before attributes, otherwise mtime together with size does not work... */ if ((attr->ia_valid & ATTR_SIZE) != 0) { int written; DPRINTK("ncpfs: trying to change size to %ld\n", attr->ia_size); if ((result = ncp_make_open(inode, O_WRONLY)) < 0) { result = -EACCES; goto out; } ncp_write_kernel(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle, attr->ia_size, 0, "", &written); /* According to ndir, the changes only take effect after closing the file */ ncp_inode_close(inode); result = ncp_make_closed(inode); if (result) goto out; { struct iattr tmpattr; tmpattr.ia_valid = ATTR_SIZE; tmpattr.ia_size = attr->ia_size; result = inode_setattr(inode, &tmpattr); if (result) goto out; } } if ((attr->ia_valid & ATTR_CTIME) != 0) { info_mask |= (DM_CREATE_TIME | DM_CREATE_DATE); ncp_date_unix2dos(attr->ia_ctime.tv_sec, &info.creationTime, &info.creationDate); } if ((attr->ia_valid & ATTR_MTIME) != 0) { info_mask |= (DM_MODIFY_TIME | DM_MODIFY_DATE); ncp_date_unix2dos(attr->ia_mtime.tv_sec, &info.modifyTime, &info.modifyDate); } if ((attr->ia_valid & ATTR_ATIME) != 0) { __le16 dummy; info_mask |= (DM_LAST_ACCESS_DATE); ncp_date_unix2dos(attr->ia_atime.tv_sec, &dummy, &info.lastAccessDate); } if (info_mask != 0) { result = ncp_modify_file_or_subdir_dos_info(NCP_SERVER(inode), inode, info_mask, &info); if (result != 0) { result = -EACCES; if (info_mask == (DM_CREATE_TIME | DM_CREATE_DATE)) { /* NetWare seems not to allow this. I do not know why. So, just tell the user everything went fine. This is a terrible hack, but I do not know how to do this correctly. */ result = 0; } else goto out; } #ifdef CONFIG_NCPFS_STRONG if ((!result) && (info_mask & DM_ATTRIBUTES)) NCP_FINFO(inode)->nwattr = info.attributes; #endif } if (!result) result = inode_setattr(inode, attr); out: unlock_kernel(); return result; }
/* * 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; }