1 /*
2  * Copyright (c) 2000-2004 Silicon Graphics, Inc.  All Rights Reserved.
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of version 2 of the GNU General Public License as
6  * published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it would be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11  *
12  * Further, this software is distributed without any warranty that it is
13  * free of the rightful claim of any third person regarding infringement
14  * or the like.  Any license provided herein, whether implied or
15  * otherwise, applies only to this software file.  Patent licenses, if
16  * any, provided herein do not apply to combinations of this program with
17  * other software, or any other product whatsoever.
18  *
19  * You should have received a copy of the GNU General Public License along
20  * with this program; if not, write the Free Software Foundation, Inc., 59
21  * Temple Place - Suite 330, Boston MA 02111-1307, USA.
22  *
23  * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
24  * Mountain View, CA  94043, or:
25  *
26  * http://www.sgi.com
27  *
28  * For further information regarding this notice, see:
29  *
30  * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
31  */
32 
33 #include "xfs.h"
34 #include "xfs_inum.h"
35 #include "xfs_log.h"
36 #include "xfs_sb.h"
37 #include "xfs_dir.h"
38 #include "xfs_dir2.h"
39 #include "xfs_trans.h"
40 #include "xfs_dmapi.h"
41 #include "xfs_mount.h"
42 #include "xfs_bmap_btree.h"
43 #include "xfs_alloc_btree.h"
44 #include "xfs_ialloc_btree.h"
45 #include "xfs_alloc.h"
46 #include "xfs_btree.h"
47 #include "xfs_attr_sf.h"
48 #include "xfs_dir_sf.h"
49 #include "xfs_dir2_sf.h"
50 #include "xfs_dinode.h"
51 #include "xfs_inode.h"
52 #include "xfs_error.h"
53 #include "xfs_rw.h"
54 #include <linux/iobuf.h>
55 
56 #include <linux/dcache.h>
57 #include <linux/smp_lock.h>
58 #include <linux/mman.h> /* for PROT_WRITE */
59 
60 static struct vm_operations_struct linvfs_file_vm_ops;
61 
62 STATIC inline ssize_t
__linvfs_read(struct file * file,char * buf,int ioflags,size_t size,loff_t * offset)63 __linvfs_read(
64 	struct file	*file,
65 	char		*buf,
66 	int		ioflags,
67 	size_t		size,
68 	loff_t		*offset)
69 {
70 	struct inode	*inode = file->f_dentry->d_inode;
71 	vnode_t		*vp = LINVFS_GET_VP(inode);
72 	ssize_t		rval;
73 
74 	if (unlikely(file->f_flags & O_DIRECT)) {
75 		ioflags |= IO_ISDIRECT;
76 		down_read(&inode->i_alloc_sem);
77 		VOP_READ(vp, file, buf, size, offset, ioflags, NULL, rval);
78 		up_read(&inode->i_alloc_sem);
79 	} else {
80 		VOP_READ(vp, file, buf, size, offset, ioflags, NULL, rval);
81 	}
82 
83 	return rval;
84 }
85 
86 STATIC ssize_t
linvfs_read(struct file * file,char * buf,size_t size,loff_t * offset)87 linvfs_read(
88 	struct file	*file,
89 	char		*buf,
90 	size_t		size,
91 	loff_t		*offset)
92 {
93 	return __linvfs_read(file, buf, 0, size, offset);
94 }
95 
96 STATIC ssize_t
linvfs_read_invis(struct file * file,char * buf,size_t size,loff_t * offset)97 linvfs_read_invis(
98 	struct file	*file,
99 	char		*buf,
100 	size_t		size,
101 	loff_t		*offset)
102 {
103 	return __linvfs_read(file, buf, IO_INVIS, size, offset);
104 }
105 
106 
107 STATIC inline ssize_t
__linvfs_write(struct file * file,const char * buf,int ioflags,size_t count,loff_t * ppos)108 __linvfs_write(
109 	struct file	*file,
110 	const char	*buf,
111 	int		ioflags,
112 	size_t		count,
113 	loff_t		*ppos)
114 {
115 	struct inode	*inode = file->f_dentry->d_inode;
116 	vnode_t		*vp = LINVFS_GET_VP(inode);
117 	loff_t		pos;
118 	ssize_t		rval;	/* Use negative errors in this f'n */
119 
120 	if ((ssize_t) count < 0)
121 		return -EINVAL;
122 
123 	if (!access_ok(VERIFY_READ, buf, count))
124 		return -EFAULT;
125 
126 	pos = *ppos;
127 	if (pos < 0)
128 		return -EINVAL;
129 
130 	rval = file->f_error;
131 	if (rval) {
132 		file->f_error = 0;
133 		return rval;
134 	}
135 
136 	/* We allow multiple direct writers in, there is no
137 	 * potential call to vmtruncate in that path.
138 	 */
139 	if (unlikely(file->f_flags & O_DIRECT)) {
140 		ioflags |= IO_ISDIRECT;
141 		down_read(&inode->i_alloc_sem);
142 		VOP_WRITE(vp, file, buf, count, &pos, ioflags, NULL, rval);
143 		*ppos = pos;
144 		up_read(&inode->i_alloc_sem);
145 	} else {
146 		down(&inode->i_sem);
147 		VOP_WRITE(vp, file, buf, count, &pos, ioflags, NULL, rval);
148 		*ppos = pos;
149 		up(&inode->i_sem);
150 	}
151 
152 	return rval;
153 }
154 
155 STATIC inline ssize_t
linvfs_write(struct file * file,const char * buf,size_t count,loff_t * ppos)156 linvfs_write(
157 	struct file	*file,
158 	const char	*buf,
159 	size_t		count,
160 	loff_t		*ppos)
161 {
162 	return __linvfs_write(file, buf, 0, count, ppos);
163 }
164 
165 STATIC inline ssize_t
linvfs_write_invis(struct file * file,const char * buf,size_t count,loff_t * ppos)166 linvfs_write_invis(
167 	struct file	*file,
168 	const char	*buf,
169 	size_t		count,
170 	loff_t		*ppos)
171 {
172 	return __linvfs_write(file, buf, IO_INVIS, count, ppos);
173 }
174 
175 STATIC int
linvfs_open(struct inode * inode,struct file * filp)176 linvfs_open(
177 	struct inode	*inode,
178 	struct file	*filp)
179 {
180 	vnode_t		*vp = LINVFS_GET_VP(inode);
181 	int		error;
182 
183 	if (!(filp->f_flags & O_LARGEFILE) && i_size_read(inode) > MAX_NON_LFS)
184 		return -EFBIG;
185 
186 	ASSERT(vp);
187 	VOP_OPEN(vp, NULL, error);
188 	return -error;
189 }
190 
191 
192 STATIC int
linvfs_release(struct inode * inode,struct file * filp)193 linvfs_release(
194 	struct inode	*inode,
195 	struct file	*filp)
196 {
197 	vnode_t		*vp = LINVFS_GET_VP(inode);
198 	int		error = 0;
199 
200 	if (vp)
201 		VOP_RELEASE(vp, error);
202 	return -error;
203 }
204 
205 
206 STATIC int
linvfs_fsync(struct file * filp,struct dentry * dentry,int datasync)207 linvfs_fsync(
208 	struct file	*filp,
209 	struct dentry	*dentry,
210 	int		datasync)
211 {
212 	struct inode	*inode = dentry->d_inode;
213 	vnode_t		*vp = LINVFS_GET_VP(inode);
214 	int		error;
215 	int		flags = FSYNC_WAIT;
216 
217 	error = fsync_inode_data_buffers(inode);
218 	if (error)
219 		return error;
220 
221 	if (datasync)
222 		flags |= FSYNC_DATA;
223 
224 	ASSERT(vp);
225 	VOP_FSYNC(vp, flags, NULL, (xfs_off_t)0, (xfs_off_t)-1, error);
226 	return -error;
227 }
228 
229 /*
230  * linvfs_readdir maps to VOP_READDIR().
231  * We need to build a uio, cred, ...
232  */
233 
234 #define nextdp(dp)      ((struct xfs_dirent *)((char *)(dp) + (dp)->d_reclen))
235 
236 STATIC int
linvfs_readdir(struct file * filp,void * dirent,filldir_t filldir)237 linvfs_readdir(
238 	struct file	*filp,
239 	void		*dirent,
240 	filldir_t	filldir)
241 {
242 	int		error = 0;
243 	vnode_t		*vp;
244 	uio_t		uio;
245 	iovec_t		iov;
246 	int		eof = 0;
247 	caddr_t		read_buf;
248 	int		namelen, size = 0;
249 	size_t		rlen = PAGE_CACHE_SIZE;
250 	xfs_off_t	start_offset, curr_offset;
251 	xfs_dirent_t	*dbp = NULL;
252 
253 	vp = LINVFS_GET_VP(filp->f_dentry->d_inode);
254 	ASSERT(vp);
255 
256 	/* Try fairly hard to get memory */
257 	do {
258 		if ((read_buf = (caddr_t)kmalloc(rlen, GFP_KERNEL)))
259 			break;
260 		rlen >>= 1;
261 	} while (rlen >= 1024);
262 
263 	if (read_buf == NULL)
264 		return -ENOMEM;
265 
266 	uio.uio_iov = &iov;
267 	uio.uio_segflg = UIO_SYSSPACE;
268 	curr_offset = filp->f_pos;
269 	if (filp->f_pos != 0x7fffffff)
270 		uio.uio_offset = filp->f_pos;
271 	else
272 		uio.uio_offset = 0xffffffff;
273 
274 	while (!eof) {
275 		uio.uio_resid = iov.iov_len = rlen;
276 		iov.iov_base = read_buf;
277 		uio.uio_iovcnt = 1;
278 
279 		start_offset = uio.uio_offset;
280 
281 		VOP_READDIR(vp, &uio, NULL, &eof, error);
282 		if ((uio.uio_offset == start_offset) || error) {
283 			size = 0;
284 			break;
285 		}
286 
287 		size = rlen - uio.uio_resid;
288 		dbp = (xfs_dirent_t *)read_buf;
289 		while (size > 0) {
290 			namelen = strlen(dbp->d_name);
291 
292 			if (filldir(dirent, dbp->d_name, namelen,
293 					(loff_t) curr_offset & 0x7fffffff,
294 					(ino_t) dbp->d_ino,
295 					DT_UNKNOWN)) {
296 				goto done;
297 			}
298 			size -= dbp->d_reclen;
299 			curr_offset = (loff_t)dbp->d_off /* & 0x7fffffff */;
300 			dbp = nextdp(dbp);
301 		}
302 	}
303 done:
304 	if (!error) {
305 		if (size == 0)
306 			filp->f_pos = uio.uio_offset & 0x7fffffff;
307 		else if (dbp)
308 			filp->f_pos = curr_offset;
309 	}
310 
311 	kfree(read_buf);
312 	return -error;
313 }
314 
315 STATIC int
linvfs_file_mmap(struct file * filp,struct vm_area_struct * vma)316 linvfs_file_mmap(
317 	struct file	*filp,
318 	struct vm_area_struct *vma)
319 {
320 	struct inode	*ip = filp->f_dentry->d_inode;
321 	vnode_t		*vp = LINVFS_GET_VP(ip);
322 	vattr_t		va = { .va_mask = XFS_AT_UPDATIME };
323 	int		error;
324 
325 	if (vp->v_vfsp->vfs_flag & VFS_DMI) {
326 		xfs_mount_t	*mp = XFS_VFSTOM(vp->v_vfsp);
327 
328 		error = -XFS_SEND_MMAP(mp, vma, 0);
329 		if (error)
330 			return error;
331 	}
332 
333 	vma->vm_ops = &linvfs_file_vm_ops;
334 
335 	VOP_SETATTR(vp, &va, XFS_AT_UPDATIME, NULL, error);
336 	if (!error)
337 		vn_revalidate(vp);	/* update Linux inode flags */
338 	return 0;
339 }
340 
341 
342 STATIC int
linvfs_ioctl(struct inode * inode,struct file * filp,unsigned int cmd,unsigned long arg)343 linvfs_ioctl(
344 	struct inode	*inode,
345 	struct file	*filp,
346 	unsigned int	cmd,
347 	unsigned long	arg)
348 {
349 	int		error;
350 	vnode_t		*vp = LINVFS_GET_VP(inode);
351 
352 	unlock_kernel();
353 	VOP_IOCTL(vp, inode, filp, 0, cmd, arg, error);
354 	VMODIFY(vp);
355 	lock_kernel();
356 
357 	/* NOTE:  some of the ioctl's return positive #'s as a
358 	 *	  byte count indicating success, such as
359 	 *	  readlink_by_handle.  So we don't "sign flip"
360 	 *	  like most other routines.  This means true
361 	 *	  errors need to be returned as a negative value.
362 	 */
363 	return error;
364 }
365 
366 STATIC int
linvfs_ioctl_invis(struct inode * inode,struct file * filp,unsigned int cmd,unsigned long arg)367 linvfs_ioctl_invis(
368 	struct inode	*inode,
369 	struct file	*filp,
370 	unsigned int	cmd,
371 	unsigned long	arg)
372 {
373 	int		error;
374 	vnode_t		*vp = LINVFS_GET_VP(inode);
375 
376 	unlock_kernel();
377 	VOP_IOCTL(vp, inode, filp, IO_INVIS, cmd, arg, error);
378 	VMODIFY(vp);
379 	lock_kernel();
380 
381 	/* NOTE:  some of the ioctl's return positive #'s as a
382 	 *	  byte count indicating success, such as
383 	 *	  readlink_by_handle.  So we don't "sign flip"
384 	 *	  like most other routines.  This means true
385 	 *	  errors need to be returned as a negative value.
386 	 */
387 	return error;
388 }
389 
390 #ifdef HAVE_VMOP_MPROTECT
391 STATIC int
linvfs_mprotect(struct vm_area_struct * vma,unsigned int newflags)392 linvfs_mprotect(
393 	struct vm_area_struct *vma,
394 	unsigned int	newflags)
395 {
396 	vnode_t		*vp = LINVFS_GET_VP(vma->vm_file->f_dentry->d_inode);
397 	int		error = 0;
398 
399 	if (vp->v_vfsp->vfs_flag & VFS_DMI) {
400 		if ((vma->vm_flags & VM_MAYSHARE) &&
401 		    (newflags & PROT_WRITE) && !(vma->vm_flags & PROT_WRITE)) {
402 			xfs_mount_t	*mp = XFS_VFSTOM(vp->v_vfsp);
403 
404 			error = XFS_SEND_MMAP(mp, vma, VM_WRITE);
405 		    }
406 	}
407 	return error;
408 }
409 #endif /* HAVE_VMOP_MPROTECT */
410 
411 
412 struct file_operations linvfs_file_operations = {
413 	.llseek		= generic_file_llseek,
414 	.read		= linvfs_read,
415 	.write		= linvfs_write,
416 	.ioctl		= linvfs_ioctl,
417 	.mmap		= linvfs_file_mmap,
418 	.open		= linvfs_open,
419 	.release	= linvfs_release,
420 	.fsync		= linvfs_fsync,
421 };
422 
423 struct file_operations linvfs_invis_file_operations = {
424 	.llseek		= generic_file_llseek,
425 	.read		= linvfs_read_invis,
426 	.write		= linvfs_write_invis,
427 	.ioctl		= linvfs_ioctl_invis,
428 	.mmap		= linvfs_file_mmap,
429 	.open		= linvfs_open,
430 	.release	= linvfs_release,
431 	.fsync		= linvfs_fsync,
432 };
433 
434 
435 struct file_operations linvfs_dir_operations = {
436 	.read		= generic_read_dir,
437 	.readdir	= linvfs_readdir,
438 	.ioctl		= linvfs_ioctl,
439 	.fsync		= linvfs_fsync,
440 };
441 
442 static struct vm_operations_struct linvfs_file_vm_ops = {
443 	.nopage		= filemap_nopage,
444 #ifdef HAVE_VMOP_MPROTECT
445 	.mprotect	= linvfs_mprotect,
446 #endif
447 };
448