include upstream ip1000a driver version 2.09f
[linux-2.4.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 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  */
33 int 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
97 out_unlock:
98         up(&NCP_FINFO(inode)->open_sem);
99 out:
100         return error;
101 }
102
103 static ssize_t
104 ncp_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);
185 outrel:
186         ncp_inode_close(inode);         
187 out:
188         return already_read ? already_read : error;
189 }
190
191 static ssize_t
192 ncp_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);
269 outrel:
270         ncp_inode_close(inode);         
271 out:
272         return already_written ? already_written : errno;
273 }
274
275 static 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
282 struct 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
293 struct inode_operations ncp_file_inode_operations =
294 {
295         setattr:        ncp_notify_change,
296 };