1/* 2 * file.c 3 * 4 * Copyright (C) 1995, 1996 by Volker Lendecke 5 * Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache 6 * 7 */ 8 9#include <asm/uaccess.h> 10#include <asm/system.h> 11 12#include <linux/time.h> 13#include <linux/kernel.h> 14#include <linux/errno.h> 15#include <linux/fcntl.h> 16#include <linux/stat.h> 17#include <linux/mm.h> 18#include <linux/slab.h> 19#include <linux/vmalloc.h> 20#include <linux/sched.h> 21 22#include <linux/ncp_fs.h> 23#include "ncplib_kernel.h" 24 25static int ncp_fsync(struct file *file, struct dentry *dentry, int datasync) 26{ 27 return 0; 28} 29 30/* 31 * Open a file with the specified read/write mode. 32 */ 33int ncp_make_open(struct inode *inode, int right) 34{ 35 int error; 36 int access; 37 38 error = -EINVAL; 39 if (!inode) { 40 printk(KERN_ERR "ncp_make_open: got NULL inode\n"); 41 goto out; 42 } 43 44 DPRINTK("ncp_make_open: opened=%d, volume # %u, dir entry # %u\n", 45 atomic_read(&NCP_FINFO(inode)->opened), 46 NCP_FINFO(inode)->volNumber, 47 NCP_FINFO(inode)->dirEntNum); 48 error = -EACCES; 49 mutex_lock(&NCP_FINFO(inode)->open_mutex); 50 if (!atomic_read(&NCP_FINFO(inode)->opened)) { 51 struct ncp_entry_info finfo; 52 int result; 53 54 /* tries max. rights */ 55 finfo.access = O_RDWR; 56 result = ncp_open_create_file_or_subdir(NCP_SERVER(inode), 57 inode, NULL, OC_MODE_OPEN, 58 0, AR_READ | AR_WRITE, &finfo); 59 if (!result) 60 goto update; 61 /* RDWR did not succeeded, try readonly or writeonly as requested */ 62 switch (right) { 63 case O_RDONLY: 64 finfo.access = O_RDONLY; 65 result = ncp_open_create_file_or_subdir(NCP_SERVER(inode), 66 inode, NULL, OC_MODE_OPEN, 67 0, AR_READ, &finfo); 68 break; 69 case O_WRONLY: 70 finfo.access = O_WRONLY; 71 result = ncp_open_create_file_or_subdir(NCP_SERVER(inode), 72 inode, NULL, OC_MODE_OPEN, 73 0, AR_WRITE, &finfo); 74 break; 75 } 76 if (result) { 77 PPRINTK("ncp_make_open: failed, result=%d\n", result); 78 goto out_unlock; 79 } 80 /* 81 * Update the inode information. 82 */ 83 update: 84 ncp_update_inode(inode, &finfo); 85 atomic_set(&NCP_FINFO(inode)->opened, 1); 86 } 87 88 access = NCP_FINFO(inode)->access; 89 PPRINTK("ncp_make_open: file open, access=%x\n", access); 90 if (access == right || access == O_RDWR) { 91 atomic_inc(&NCP_FINFO(inode)->opened); 92 error = 0; 93 } 94 95out_unlock: 96 mutex_unlock(&NCP_FINFO(inode)->open_mutex); 97out: 98 return error; 99} 100 101static ssize_t 102ncp_file_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) 103{ 104 struct dentry *dentry = file->f_path.dentry; 105 struct inode *inode = dentry->d_inode; 106 size_t already_read = 0; 107 off_t pos; 108 size_t bufsize; 109 int error; 110 void* freepage; 111 size_t freelen; 112 113 DPRINTK("ncp_file_read: enter %s/%s\n", 114 dentry->d_parent->d_name.name, dentry->d_name.name); 115 116 if (!ncp_conn_valid(NCP_SERVER(inode))) 117 return -EIO; 118 119 pos = *ppos; 120 121 if ((ssize_t) count < 0) { 122 return -EINVAL; 123 } 124 if (!count) 125 return 0; 126 if (pos > inode->i_sb->s_maxbytes) 127 return 0; 128 if (pos + count > inode->i_sb->s_maxbytes) { 129 count = inode->i_sb->s_maxbytes - pos; 130 } 131 132 error = ncp_make_open(inode, O_RDONLY); 133 if (error) { 134 DPRINTK(KERN_ERR "ncp_file_read: open failed, error=%d\n", error); 135 return error; 136 } 137 138 bufsize = NCP_SERVER(inode)->buffer_size; 139 140 error = -EIO; 141 freelen = ncp_read_bounce_size(bufsize); 142 freepage = vmalloc(freelen); 143 if (!freepage) 144 goto outrel; 145 error = 0; 146 /* First read in as much as possible for each bufsize. */ 147 while (already_read < count) { 148 int read_this_time; 149 size_t to_read = min_t(unsigned int, 150 bufsize - (pos % bufsize), 151 count - already_read); 152 153 error = ncp_read_bounce(NCP_SERVER(inode), 154 NCP_FINFO(inode)->file_handle, 155 pos, to_read, buf, &read_this_time, 156 freepage, freelen); 157 if (error) { 158 error = -EIO; /* NW errno -> Linux errno */ 159 break; 160 } 161 pos += read_this_time; 162 buf += read_this_time; 163 already_read += read_this_time; 164 165 if (read_this_time != to_read) { 166 break; 167 } 168 } 169 vfree(freepage); 170 171 *ppos = pos; 172 173 file_accessed(file); 174 175 DPRINTK("ncp_file_read: exit %s/%s\n", 176 dentry->d_parent->d_name.name, dentry->d_name.name); 177outrel: 178 ncp_inode_close(inode); 179 return already_read ? already_read : error; 180} 181 182static ssize_t 183ncp_file_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) 184{ 185 struct dentry *dentry = file->f_path.dentry; 186 struct inode *inode = dentry->d_inode; 187 size_t already_written = 0; 188 off_t pos; 189 size_t bufsize; 190 int errno; 191 void* bouncebuffer; 192 193 DPRINTK("ncp_file_write: enter %s/%s\n", 194 dentry->d_parent->d_name.name, dentry->d_name.name); 195 if (!ncp_conn_valid(NCP_SERVER(inode))) 196 return -EIO; 197 if ((ssize_t) count < 0) 198 return -EINVAL; 199 pos = *ppos; 200 if (file->f_flags & O_APPEND) { 201 pos = inode->i_size; 202 } 203 204 if (pos + count > MAX_NON_LFS && !(file->f_flags&O_LARGEFILE)) { 205 if (pos >= MAX_NON_LFS) { 206 send_sig(SIGXFSZ, current, 0); 207 return -EFBIG; 208 } 209 if (count > MAX_NON_LFS - (u32)pos) { 210 count = MAX_NON_LFS - (u32)pos; 211 } 212 } 213 if (pos >= inode->i_sb->s_maxbytes) { 214 if (count || pos > inode->i_sb->s_maxbytes) { 215 send_sig(SIGXFSZ, current, 0); 216 return -EFBIG; 217 } 218 } 219 if (pos + count > inode->i_sb->s_maxbytes) { 220 count = inode->i_sb->s_maxbytes - pos; 221 } 222 223 if (!count) 224 return 0; 225 errno = ncp_make_open(inode, O_WRONLY); 226 if (errno) { 227 DPRINTK(KERN_ERR "ncp_file_write: open failed, error=%d\n", errno); 228 return errno; 229 } 230 bufsize = NCP_SERVER(inode)->buffer_size; 231 232 already_written = 0; 233 234 bouncebuffer = vmalloc(bufsize); 235 if (!bouncebuffer) { 236 errno = -EIO; /* -ENOMEM */ 237 goto outrel; 238 } 239 while (already_written < count) { 240 int written_this_time; 241 size_t to_write = min_t(unsigned int, 242 bufsize - (pos % bufsize), 243 count - already_written); 244 245 if (copy_from_user(bouncebuffer, buf, to_write)) { 246 errno = -EFAULT; 247 break; 248 } 249 if (ncp_write_kernel(NCP_SERVER(inode), 250 NCP_FINFO(inode)->file_handle, 251 pos, to_write, bouncebuffer, &written_this_time) != 0) { 252 errno = -EIO; 253 break; 254 } 255 pos += written_this_time; 256 buf += written_this_time; 257 already_written += written_this_time; 258 259 if (written_this_time != to_write) { 260 break; 261 } 262 } 263 vfree(bouncebuffer); 264 265 file_update_time(file); 266 267 *ppos = pos; 268 269 if (pos > inode->i_size) { 270 inode->i_size = pos; 271 } 272 DPRINTK("ncp_file_write: exit %s/%s\n", 273 dentry->d_parent->d_name.name, dentry->d_name.name); 274outrel: 275 ncp_inode_close(inode); 276 return already_written ? already_written : errno; 277} 278 279static int ncp_release(struct inode *inode, struct file *file) { 280 if (ncp_make_closed(inode)) { 281 DPRINTK("ncp_release: failed to close\n"); 282 } 283 return 0; 284} 285 286const struct file_operations ncp_file_operations = 287{ 288 .llseek = remote_llseek, 289 .read = ncp_file_read, 290 .write = ncp_file_write, 291 .ioctl = ncp_ioctl, 292#ifdef CONFIG_COMPAT 293 .compat_ioctl = ncp_compat_ioctl, 294#endif 295 .mmap = ncp_mmap, 296 .release = ncp_release, 297 .fsync = ncp_fsync, 298}; 299 300const struct inode_operations ncp_file_inode_operations = 301{ 302 .setattr = ncp_notify_change, 303}; 304