linux_stats.c revision 83221
1/*-
2 * Copyright (c) 1994-1995 S�ren Schmidt
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer
10 *    in this position and unchanged.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 *    derived from this software withough specific prior written permission
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * $FreeBSD: head/sys/compat/linux/linux_stats.c 83221 2001-09-08 19:07:04Z marcel $
29 */
30
31#include <sys/param.h>
32#include <sys/conf.h>
33#include <sys/dirent.h>
34#include <sys/file.h>
35#include <sys/filedesc.h>
36#include <sys/proc.h>
37#include <sys/mount.h>
38#include <sys/namei.h>
39#include <sys/stat.h>
40#include <sys/sysctl.h>
41#include <sys/systm.h>
42#include <sys/vnode.h>
43
44#include <machine/../linux/linux.h>
45#include <machine/../linux/linux_proto.h>
46#include <compat/linux/linux_util.h>
47
48static int
49newstat_copyout(struct stat *buf, void *ubuf)
50{
51	struct l_newstat tbuf;
52	struct cdevsw *cdevsw;
53	dev_t dev;
54
55	tbuf.st_dev = uminor(buf->st_dev) | (umajor(buf->st_dev) << 8);
56	tbuf.st_ino = buf->st_ino;
57	tbuf.st_mode = buf->st_mode;
58	tbuf.st_nlink = buf->st_nlink;
59	tbuf.st_uid = buf->st_uid;
60	tbuf.st_gid = buf->st_gid;
61	tbuf.st_rdev = buf->st_rdev;
62	tbuf.st_size = buf->st_size;
63	tbuf.st_atime = buf->st_atime;
64	tbuf.st_mtime = buf->st_mtime;
65	tbuf.st_ctime = buf->st_ctime;
66	tbuf.st_blksize = buf->st_blksize;
67	tbuf.st_blocks = buf->st_blocks;
68
69	/* Lie about disk drives which are character devices
70	 * in FreeBSD but block devices under Linux.
71	 */
72	if (S_ISCHR(tbuf.st_mode) &&
73	    (dev = udev2dev(buf->st_rdev, 0)) != NODEV) {
74		cdevsw = devsw(dev);
75		if (cdevsw != NULL && (cdevsw->d_flags & D_DISK)) {
76			tbuf.st_mode &= ~S_IFMT;
77			tbuf.st_mode |= S_IFBLK;
78
79			/* XXX this may not be quite right */
80			/* Map major number to 0 */
81			tbuf.st_dev = uminor(buf->st_dev) & 0xf;
82			tbuf.st_rdev = buf->st_rdev & 0xff;
83		}
84	}
85
86	return (copyout(&tbuf, ubuf, sizeof(tbuf)));
87}
88
89int
90linux_newstat(struct proc *p, struct linux_newstat_args *args)
91{
92	struct stat buf;
93	struct nameidata nd;
94	int error;
95	caddr_t sg;
96
97	sg = stackgap_init();
98	CHECKALTEXIST(p, &sg, args->path);
99
100#ifdef DEBUG
101	if (ldebug(newstat))
102		printf(ARGS(newstat, "%s, *"), args->path);
103#endif
104
105	NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE,
106	    args->path, p);
107	error = namei(&nd);
108	if (error)
109		return (error);
110	NDFREE(&nd, NDF_ONLY_PNBUF);
111
112	error = vn_stat(nd.ni_vp, &buf, p);
113	vput(nd.ni_vp);
114	if (error)
115		return (error);
116
117	return (newstat_copyout(&buf, args->buf));
118}
119
120int
121linux_newlstat(struct proc *p, struct linux_newlstat_args *args)
122{
123	int error;
124	struct stat sb;
125	struct nameidata nd;
126	caddr_t sg;
127
128	sg = stackgap_init();
129	CHECKALTEXIST(p, &sg, args->path);
130
131#ifdef DEBUG
132	if (ldebug(newlstat))
133		printf(ARGS(newlstat, "%s, *"), args->path);
134#endif
135
136	NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE,
137	    args->path, p);
138	error = namei(&nd);
139	if (error)
140		return (error);
141	NDFREE(&nd, NDF_ONLY_PNBUF);
142
143	error = vn_stat(nd.ni_vp, &sb, p);
144	vput(nd.ni_vp);
145	if (error)
146		return (error);
147
148	return (newstat_copyout(&sb, args->buf));
149}
150
151int
152linux_newfstat(struct proc *p, struct linux_newfstat_args *args)
153{
154	struct filedesc *fdp;
155	struct file *fp;
156	struct stat buf;
157	int error;
158
159#ifdef DEBUG
160	if (ldebug(newfstat))
161		printf(ARGS(newfstat, "%d, *"), args->fd);
162#endif
163
164	fdp = p->p_fd;
165	if ((unsigned)args->fd >= fdp->fd_nfiles ||
166	    (fp = fdp->fd_ofiles[args->fd]) == NULL)
167		return (EBADF);
168
169	error = fo_stat(fp, &buf, p);
170	if (!error)
171		error = newstat_copyout(&buf, args->buf);
172
173	return (error);
174}
175
176/* XXX - All fields of type l_int are defined as l_long on i386 */
177struct l_statfs {
178	l_int		f_type;
179	l_int		f_bsize;
180	l_int		f_blocks;
181	l_int		f_bfree;
182	l_int		f_bavail;
183	l_int		f_files;
184	l_int		f_ffree;
185	l_fsid_t	f_fsid;
186	l_int		f_namelen;
187	l_int		f_spare[6];
188};
189
190#ifndef VT_NWFS
191#define	VT_NWFS	VT_TFS	/* XXX - bug compat. with sys/fs/nwfs/nwfs_node.h */
192#endif
193
194#define	LINUX_CODA_SUPER_MAGIC	0x73757245L
195#define	LINUX_EXT2_SUPER_MAGIC	0xEF53L
196#define	LINUX_HPFS_SUPER_MAGIC	0xf995e849L
197#define	LINUX_ISOFS_SUPER_MAGIC	0x9660L
198#define	LINUX_MSDOS_SUPER_MAGIC	0x4d44L
199#define	LINUX_NCP_SUPER_MAGIC	0x564cL
200#define	LINUX_NFS_SUPER_MAGIC	0x6969L
201#define	LINUX_NTFS_SUPER_MAGIC	0x5346544EL
202#define	LINUX_PROC_SUPER_MAGIC	0x9fa0L
203#define	LINUX_UFS_SUPER_MAGIC	0x00011954L	/* XXX - UFS_MAGIC in Linux */
204
205/*
206 * ext2fs uses the VT_UFS tag. A mounted ext2 filesystem will therefore
207 * be seen as an ufs filesystem.
208 */
209static long
210bsd_to_linux_ftype(int tag)
211{
212
213	switch (tag) {
214	case VT_CODA:
215		return (LINUX_CODA_SUPER_MAGIC);
216	case VT_HPFS:
217		return (LINUX_HPFS_SUPER_MAGIC);
218	case VT_ISOFS:
219		return (LINUX_ISOFS_SUPER_MAGIC);
220	case VT_MSDOSFS:
221		return (LINUX_MSDOS_SUPER_MAGIC);
222	case VT_NFS:
223		return (LINUX_NFS_SUPER_MAGIC);
224	case VT_NTFS:
225		return (LINUX_NTFS_SUPER_MAGIC);
226	case VT_NWFS:
227		return (LINUX_NCP_SUPER_MAGIC);
228	case VT_PROCFS:
229		return (LINUX_PROC_SUPER_MAGIC);
230	case VT_UFS:
231		return (LINUX_UFS_SUPER_MAGIC);
232	}
233
234	return (0L);
235}
236
237int
238linux_statfs(struct proc *p, struct linux_statfs_args *args)
239{
240	struct mount *mp;
241	struct nameidata *ndp;
242	struct statfs *bsd_statfs;
243	struct nameidata nd;
244	struct l_statfs linux_statfs;
245	int error;
246	caddr_t sg;
247
248	sg = stackgap_init();
249	CHECKALTEXIST(p, &sg, args->path);
250
251#ifdef DEBUG
252	if (ldebug(statfs))
253		printf(ARGS(statfs, "%s, *"), args->path);
254#endif
255	ndp = &nd;
256	NDINIT(ndp, LOOKUP, FOLLOW, UIO_USERSPACE, args->path, curproc);
257	error = namei(ndp);
258	if (error)
259		return error;
260	NDFREE(ndp, NDF_ONLY_PNBUF);
261	mp = ndp->ni_vp->v_mount;
262	bsd_statfs = &mp->mnt_stat;
263	vrele(ndp->ni_vp);
264	error = VFS_STATFS(mp, bsd_statfs, p);
265	if (error)
266		return error;
267	bsd_statfs->f_flags = mp->mnt_flag & MNT_VISFLAGMASK;
268	linux_statfs.f_type = bsd_to_linux_ftype(bsd_statfs->f_type);
269	linux_statfs.f_bsize = bsd_statfs->f_bsize;
270	linux_statfs.f_blocks = bsd_statfs->f_blocks;
271	linux_statfs.f_bfree = bsd_statfs->f_bfree;
272	linux_statfs.f_bavail = bsd_statfs->f_bavail;
273  	linux_statfs.f_ffree = bsd_statfs->f_ffree;
274	linux_statfs.f_files = bsd_statfs->f_files;
275	linux_statfs.f_fsid.val[0] = bsd_statfs->f_fsid.val[0];
276	linux_statfs.f_fsid.val[1] = bsd_statfs->f_fsid.val[1];
277	linux_statfs.f_namelen = MAXNAMLEN;
278	return copyout((caddr_t)&linux_statfs, (caddr_t)args->buf,
279	    sizeof(linux_statfs));
280}
281
282int
283linux_fstatfs(struct proc *p, struct linux_fstatfs_args *args)
284{
285	struct file *fp;
286	struct mount *mp;
287	struct statfs *bsd_statfs;
288	struct l_statfs linux_statfs;
289	int error;
290
291#ifdef DEBUG
292	if (ldebug(fstatfs))
293		printf(ARGS(fstatfs, "%d, *"), args->fd);
294#endif
295	error = getvnode(p->p_fd, args->fd, &fp);
296	if (error)
297		return error;
298	mp = ((struct vnode *)fp->f_data)->v_mount;
299	bsd_statfs = &mp->mnt_stat;
300	error = VFS_STATFS(mp, bsd_statfs, p);
301	if (error)
302		return error;
303	bsd_statfs->f_flags = mp->mnt_flag & MNT_VISFLAGMASK;
304	linux_statfs.f_type = bsd_to_linux_ftype(bsd_statfs->f_type);
305	linux_statfs.f_bsize = bsd_statfs->f_bsize;
306	linux_statfs.f_blocks = bsd_statfs->f_blocks;
307	linux_statfs.f_bfree = bsd_statfs->f_bfree;
308	linux_statfs.f_bavail = bsd_statfs->f_bavail;
309  	linux_statfs.f_ffree = bsd_statfs->f_ffree;
310	linux_statfs.f_files = bsd_statfs->f_files;
311	linux_statfs.f_fsid.val[0] = bsd_statfs->f_fsid.val[0];
312	linux_statfs.f_fsid.val[1] = bsd_statfs->f_fsid.val[1];
313	linux_statfs.f_namelen = MAXNAMLEN;
314	return copyout((caddr_t)&linux_statfs, (caddr_t)args->buf,
315	    sizeof(linux_statfs));
316}
317
318struct l_ustat
319{
320	l_daddr_t	f_tfree;
321	l_ino_t		f_tinode;
322	char		f_fname[6];
323	char		f_fpack[6];
324};
325
326int
327linux_ustat(struct proc *p, struct linux_ustat_args *args)
328{
329	struct l_ustat lu;
330	dev_t dev;
331	struct vnode *vp;
332	struct statfs *stat;
333	int error;
334
335#ifdef DEBUG
336	if (ldebug(ustat))
337		printf(ARGS(ustat, "%d, *"), args->dev);
338#endif
339
340	/*
341	 * lu.f_fname and lu.f_fpack are not used. They are always zeroed.
342	 * lu.f_tinode and lu.f_tfree are set from the device's super block.
343	 */
344	bzero(&lu, sizeof(lu));
345
346	/*
347	 * XXX - Don't return an error if we can't find a vnode for the
348	 * device. Our dev_t is 32-bits whereas Linux only has a 16-bits
349	 * dev_t. The dev_t that is used now may as well be a truncated
350	 * dev_t returned from previous syscalls. Just return a bzeroed
351	 * ustat in that case.
352	 */
353	dev = makedev(args->dev >> 8, args->dev & 0xFF);
354	if (vfinddev(dev, VCHR, &vp)) {
355		if (vp->v_mount == NULL)
356			return (EINVAL);
357		stat = &(vp->v_mount->mnt_stat);
358		error = VFS_STATFS(vp->v_mount, stat, p);
359		if (error)
360			return (error);
361
362		lu.f_tfree = stat->f_bfree;
363		lu.f_tinode = stat->f_ffree;
364	}
365
366	return (copyout(&lu, args->ubuf, sizeof(lu)));
367}
368
369#if defined(__i386__)
370
371static int
372stat64_copyout(struct stat *buf, void *ubuf)
373{
374	struct l_stat64 lbuf;
375
376	bzero(&lbuf, sizeof(lbuf));
377	lbuf.st_dev = uminor(buf->st_dev) | (umajor(buf->st_dev) << 8);
378	lbuf.st_ino = buf->st_ino;
379	lbuf.st_mode = buf->st_mode;
380	lbuf.st_nlink = buf->st_nlink;
381	lbuf.st_uid = buf->st_uid;
382	lbuf.st_gid = buf->st_gid;
383	lbuf.st_rdev = buf->st_rdev;
384	lbuf.st_size = buf->st_size;
385	lbuf.st_atime = buf->st_atime;
386	lbuf.st_mtime = buf->st_mtime;
387	lbuf.st_ctime = buf->st_ctime;
388	lbuf.st_blksize = buf->st_blksize;
389	lbuf.st_blocks = buf->st_blocks;
390
391	/*
392	 * The __st_ino field makes all the difference. In the Linux kernel
393	 * it is conditionally compiled based on STAT64_HAS_BROKEN_ST_INO,
394	 * but without the assignment to __st_ino the runtime linker refuses
395	 * to mmap(2) any shared libraries. I guess it's broken alright :-)
396	 */
397	lbuf.__st_ino = buf->st_ino;
398
399	return (copyout(&lbuf, ubuf, sizeof(lbuf)));
400}
401
402int
403linux_stat64(struct proc *p, struct linux_stat64_args *args)
404{
405	struct stat buf;
406	struct nameidata nd;
407	int error;
408	caddr_t sg;
409
410	sg = stackgap_init();
411	CHECKALTEXIST(p, &sg, args->filename);
412
413#ifdef DEBUG
414	if (ldebug(stat64))
415		printf(ARGS(stat64, "%s, *"), args->filename);
416#endif
417
418	NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE,
419	    args->filename, p);
420	error = namei(&nd);
421	if (error)
422		return (error);
423	NDFREE(&nd, NDF_ONLY_PNBUF);
424
425	error = vn_stat(nd.ni_vp, &buf, p);
426	vput(nd.ni_vp);
427	if (error)
428		return (error);
429
430	return (stat64_copyout(&buf, args->statbuf));
431}
432
433int
434linux_lstat64(struct proc *p, struct linux_lstat64_args *args)
435{
436	int error;
437	struct stat sb;
438	struct nameidata nd;
439	caddr_t sg;
440
441	sg = stackgap_init();
442	CHECKALTEXIST(p, &sg, args->filename);
443
444#ifdef DEBUG
445	if (ldebug(lstat64))
446		printf(ARGS(lstat64, "%s, *"), args->filename);
447#endif
448
449	NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE,
450	    args->filename, p);
451	error = namei(&nd);
452	if (error)
453		return (error);
454	NDFREE(&nd, NDF_ONLY_PNBUF);
455
456	error = vn_stat(nd.ni_vp, &sb, p);
457	vput(nd.ni_vp);
458	if (error)
459		return (error);
460
461	return (stat64_copyout(&sb, args->statbuf));
462}
463
464int
465linux_fstat64(struct proc *p, struct linux_fstat64_args *args)
466{
467	struct filedesc *fdp;
468	struct file *fp;
469	struct stat buf;
470	int error;
471
472#ifdef DEBUG
473	if (ldebug(fstat64))
474		printf(ARGS(fstat64, "%d, *"), args->fd);
475#endif
476
477	fdp = p->p_fd;
478	if ((unsigned)args->fd >= fdp->fd_nfiles ||
479	    (fp = fdp->fd_ofiles[args->fd]) == NULL)
480		return (EBADF);
481
482	error = fo_stat(fp, &buf, p);
483	if (!error)
484		error = stat64_copyout(&buf, args->statbuf);
485
486	return (error);
487}
488
489#endif /* __i386__ */
490