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