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