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