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/malloc.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 read: ncp_file_read,
288 write: ncp_file_write,
289 ioctl: ncp_ioctl,
290 mmap: ncp_mmap,
291 release: ncp_release,
292 fsync: ncp_fsync,
293 };
294
295 struct inode_operations ncp_file_inode_operations =
296 {
297 setattr: ncp_notify_change,
298 };
299
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.