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