v2.4.8 -> v2.4.8.1
[opensuse:kernel.git] / 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/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
25 static inline unsigned int min(unsigned int a, unsigned int b)
26 {
27         return a < b ? a : b;
28 }
29
30 static int ncp_fsync(struct file *file, struct dentry *dentry, int datasync)
31 {
32         return 0;
33 }
34
35 /*
36  * Open a file with the specified read/write mode.
37  */
38 int ncp_make_open(struct inode *inode, int right)
39 {
40         int error;
41         int access;
42
43         error = -EINVAL;
44         if (!inode) {
45                 printk(KERN_ERR "ncp_make_open: got NULL inode\n");
46                 goto out;
47         }
48
49         DPRINTK("ncp_make_open: opened=%d, volume # %u, dir entry # %u\n",
50                 atomic_read(&NCP_FINFO(inode)->opened), 
51                 NCP_FINFO(inode)->volNumber, 
52                 NCP_FINFO(inode)->dirEntNum);
53         error = -EACCES;
54         down(&NCP_FINFO(inode)->open_sem);
55         if (!atomic_read(&NCP_FINFO(inode)->opened)) {
56                 struct ncp_entry_info finfo;
57                 int result;
58
59                 finfo.i.dirEntNum = NCP_FINFO(inode)->dirEntNum;
60                 finfo.i.volNumber = NCP_FINFO(inode)->volNumber;
61                 /* tries max. rights */
62                 finfo.access = O_RDWR;
63                 result = ncp_open_create_file_or_subdir(NCP_SERVER(inode),
64                                         NULL, NULL, OC_MODE_OPEN,
65                                         0, AR_READ | AR_WRITE, &finfo);
66                 if (!result)
67                         goto update;
68                 /* RDWR did not succeeded, try readonly or writeonly as requested */
69                 switch (right) {
70                         case O_RDONLY:
71                                 finfo.access = O_RDONLY;
72                                 result = ncp_open_create_file_or_subdir(NCP_SERVER(inode),
73                                         NULL, NULL, OC_MODE_OPEN,
74                                         0, AR_READ, &finfo);
75                                 break;
76                         case O_WRONLY:
77                                 finfo.access = O_WRONLY;
78                                 result = ncp_open_create_file_or_subdir(NCP_SERVER(inode),
79                                         NULL, NULL, OC_MODE_OPEN,
80                                         0, AR_WRITE, &finfo);
81                                 break;
82                 }
83                 if (result) {
84                         PPRINTK("ncp_make_open: failed, result=%d\n", result);
85                         goto out_unlock;
86                 }
87                 /*
88                  * Update the inode information.
89                  */
90         update:
91                 ncp_update_inode(inode, &finfo);
92                 atomic_set(&NCP_FINFO(inode)->opened, 1);
93         }
94
95         access = NCP_FINFO(inode)->access;
96         PPRINTK("ncp_make_open: file open, access=%x\n", access);
97         if (access == right || access == O_RDWR) {
98                 atomic_inc(&NCP_FINFO(inode)->opened);
99                 error = 0;
100         }
101
102 out_unlock:
103         up(&NCP_FINFO(inode)->open_sem);
104 out:
105         return error;
106 }
107
108 static ssize_t
109 ncp_file_read(struct file *file, char *buf, size_t count, loff_t *ppos)
110 {
111         struct dentry *dentry = file->f_dentry;
112         struct inode *inode = dentry->d_inode;
113         size_t already_read = 0;
114         off_t pos;
115         size_t bufsize;
116         int error;
117         void* freepage;
118         size_t freelen;
119
120         DPRINTK("ncp_file_read: enter %s/%s\n",
121                 dentry->d_parent->d_name.name, dentry->d_name.name);
122
123         error = -EIO;
124         if (!ncp_conn_valid(NCP_SERVER(inode)))
125                 goto out;
126         error = -EINVAL;
127         if (!S_ISREG(inode->i_mode)) {
128                 DPRINTK("ncp_file_read: read from non-file, mode %07o\n",
129                         inode->i_mode);
130                 goto out;
131         }
132
133         pos = *ppos;
134 /* leave it out on server ...
135         if (pos + count > inode->i_size) {
136                 count = inode->i_size - pos;
137         }
138 */
139         error = 0;
140         if (!count)     /* size_t is never < 0 */
141                 goto out;
142
143         error = ncp_make_open(inode, O_RDONLY);
144         if (error) {
145                 DPRINTK(KERN_ERR "ncp_file_read: open failed, error=%d\n", error);
146                 goto out;
147         }
148
149         bufsize = NCP_SERVER(inode)->buffer_size;
150
151         error = -EIO;
152         freelen = ncp_read_bounce_size(bufsize);
153         freepage = vmalloc(freelen);
154         if (!freepage)
155                 goto outrel;
156         error = 0;
157         /* First read in as much as possible for each bufsize. */
158         while (already_read < count) {
159                 int read_this_time;
160                 size_t to_read = min(bufsize - (pos % bufsize),
161                                   count - already_read);
162
163                 error = ncp_read_bounce(NCP_SERVER(inode),
164                                 NCP_FINFO(inode)->file_handle,
165                                 pos, to_read, buf, &read_this_time, 
166                                 freepage, freelen);
167                 if (error) {
168                         error = -EIO;   /* NW errno -> Linux errno */
169                         break;
170                 }
171                 pos += read_this_time;
172                 buf += read_this_time;
173                 already_read += read_this_time;
174
175                 if (read_this_time != to_read) {
176                         break;
177                 }
178         }
179         vfree(freepage);
180
181         *ppos = pos;
182
183         if (!IS_RDONLY(inode)) {
184                 inode->i_atime = CURRENT_TIME;
185         }
186         
187         DPRINTK("ncp_file_read: exit %s/%s\n",
188                 dentry->d_parent->d_name.name, dentry->d_name.name);
189 outrel:
190         ncp_inode_close(inode);         
191 out:
192         return already_read ? already_read : error;
193 }
194
195 static ssize_t
196 ncp_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
197 {
198         struct dentry *dentry = file->f_dentry;
199         struct inode *inode = dentry->d_inode;
200         size_t already_written = 0;
201         off_t pos;
202         size_t bufsize;
203         int errno;
204         void* bouncebuffer;
205
206         DPRINTK("ncp_file_write: enter %s/%s\n",
207                 dentry->d_parent->d_name.name, dentry->d_name.name);
208         errno = -EIO;
209         if (!ncp_conn_valid(NCP_SERVER(inode)))
210                 goto out;
211         if (!S_ISREG(inode->i_mode)) {
212                 DPRINTK("ncp_file_write: write to non-file, mode %07o\n",
213                         inode->i_mode);
214                 return -EINVAL;
215         }
216
217         errno = 0;
218         if (!count)
219                 goto out;
220         errno = ncp_make_open(inode, O_WRONLY);
221         if (errno) {
222                 DPRINTK(KERN_ERR "ncp_file_write: open failed, error=%d\n", errno);
223                 return errno;
224         }
225         pos = *ppos;
226
227         if (file->f_flags & O_APPEND) {
228                 pos = inode->i_size;
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(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         inode->i_mtime = inode->i_atime = CURRENT_TIME;
264         
265         *ppos = pos;
266
267         if (pos > inode->i_size) {
268                 inode->i_size = pos;
269         }
270         DPRINTK("ncp_file_write: exit %s/%s\n",
271                 dentry->d_parent->d_name.name, dentry->d_name.name);
272 outrel:
273         ncp_inode_close(inode);         
274 out:
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 struct file_operations ncp_file_operations =
286 {
287         llseek:         generic_file_llseek,
288         read:           ncp_file_read,
289         write:          ncp_file_write,
290         ioctl:          ncp_ioctl,
291         mmap:           ncp_mmap,
292         release:        ncp_release,
293         fsync:          ncp_fsync,
294 };
295
296 struct inode_operations ncp_file_inode_operations =
297 {
298         setattr:        ncp_notify_change,
299 };