kernel: Add some more missing crit_exit(), rel_mplock(), etc.
[dragonfly.git] / sys / emulation / linux / linux_stats.c
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 without 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: src/sys/compat/linux/linux_stats.c,v 1.22.2.3 2001/11/05 19:08:23 marcel Exp $
29  */
30
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/conf.h>
34 #include <sys/dirent.h>
35 #include <sys/file.h>
36 #include <sys/filedesc.h>
37 #include <sys/proc.h>
38 #include <sys/mount.h>
39 #include <sys/nlookup.h>
40 #include <sys/stat.h>
41 #include <sys/sysctl.h>
42 #include <sys/systm.h>
43 #include <sys/unistd.h>
44 #include <sys/vnode.h>
45 #include <sys/device.h>
46 #include <sys/kern_syscall.h>
47 #include <emulation/43bsd/stat.h>
48
49 #include <sys/file2.h>
50 #include <sys/mplock2.h>
51
52 #include <arch_linux/linux.h>
53 #include <arch_linux/linux_proto.h>
54 #include "linux_util.h"
55
56 static int
57 newstat_copyout(struct stat *buf, void *ubuf)
58 {
59         struct l_newstat tbuf;
60         int error;
61
62         bzero(&tbuf, sizeof(tbuf));
63         tbuf.st_dev = uminor(buf->st_dev) | (umajor(buf->st_dev) << 8);
64         tbuf.st_ino = INO64TO32(buf->st_ino);
65         tbuf.st_mode = buf->st_mode;
66         tbuf.st_nlink = buf->st_nlink;
67         tbuf.st_uid = buf->st_uid;
68         tbuf.st_gid = buf->st_gid;
69         tbuf.st_rdev = buf->st_rdev;
70         tbuf.st_size = buf->st_size;
71         tbuf.st_atime = buf->st_atime;
72         tbuf.st_mtime = buf->st_mtime;
73         tbuf.st_ctime = buf->st_ctime;
74         tbuf.st_blksize = buf->st_blksize;
75         tbuf.st_blocks = buf->st_blocks;
76
77         error = copyout(&tbuf, ubuf, sizeof(tbuf));
78         return (error);
79 }
80
81 static int
82 ostat_copyout(struct stat *st, struct ostat *uaddr)
83 {
84         struct ostat ost;
85         int error;
86
87         ost.st_dev = st->st_dev;
88         ost.st_ino = st->st_ino;
89         ost.st_mode = st->st_mode;
90         ost.st_nlink = st->st_nlink;
91         ost.st_uid = st->st_uid;
92         ost.st_gid = st->st_gid;
93         ost.st_rdev = st->st_rdev;
94         if (st->st_size < (quad_t)1 << 32)
95                 ost.st_size = st->st_size;
96         else
97                 ost.st_size = -2;
98         ost.st_atime = st->st_atime;
99         ost.st_mtime = st->st_mtime;
100         ost.st_ctime = st->st_ctime;
101         ost.st_blksize = st->st_blksize;
102         ost.st_blocks = st->st_blocks;
103         ost.st_flags = st->st_flags;
104         ost.st_gen = st->st_gen;
105
106         error = copyout(&ost, uaddr, sizeof(ost));
107         return (error);
108 }
109
110 /*
111  * MPALMOSTSAFE
112  */
113 int
114 sys_linux_newstat(struct linux_newstat_args *args)
115 {
116         struct stat buf;
117         struct nlookupdata nd;
118         char *path;
119         int error;
120
121         error = linux_copyin_path(args->path, &path, LINUX_PATH_EXISTS);
122         if (error)
123                 return (error);
124 #ifdef DEBUG
125         if (ldebug(newstat))
126                 kprintf(ARGS(newstat, "%s, *"), path);
127 #endif
128         get_mplock();
129         error = nlookup_init(&nd, path, UIO_SYSSPACE, NLC_FOLLOW);
130         if (error == 0) {
131                 error = kern_stat(&nd, &buf);
132                 if (error == 0)
133                         error = newstat_copyout(&buf, args->buf);
134                 nlookup_done(&nd);
135         }
136         rel_mplock();
137         linux_free_path(&path);
138         return (error);
139 }
140
141 /*
142  * MPALMOSTSAFE
143  */
144 int
145 sys_linux_newlstat(struct linux_newlstat_args *args)
146 {
147         struct stat sb;
148         struct nlookupdata nd;
149         char *path;
150         int error;
151
152         error = linux_copyin_path(args->path, &path, LINUX_PATH_EXISTS);
153         if (error)
154                 return (error);
155 #ifdef DEBUG
156         if (ldebug(newlstat))
157                 kprintf(ARGS(newlstat, "%s, *"), path);
158 #endif
159         get_mplock();
160         error = nlookup_init(&nd, path, UIO_SYSSPACE, 0);
161         if (error == 0) {
162                 error = kern_stat(&nd, &sb);
163                 if (error == 0)
164                         error = newstat_copyout(&sb, args->buf);
165                 nlookup_done(&nd);
166         }
167         rel_mplock();
168         linux_free_path(&path);
169         return (error);
170 }
171
172 /*
173  * MPALMOSTSAFE
174  */
175 int
176 sys_linux_newfstat(struct linux_newfstat_args *args)
177 {
178         struct stat buf;
179         int error;
180
181 #ifdef DEBUG
182         if (ldebug(newfstat))
183                 kprintf(ARGS(newfstat, "%d, *"), args->fd);
184 #endif
185         get_mplock();
186         error = kern_fstat(args->fd, &buf);
187         rel_mplock();
188
189         if (error == 0)
190                 error = newstat_copyout(&buf, args->buf);
191         return (error);
192 }
193
194 /* XXX - All fields of type l_int are defined as l_long on i386 */
195 struct l_statfs {
196         l_int           f_type;
197         l_int           f_bsize;
198         l_int           f_blocks;
199         l_int           f_bfree;
200         l_int           f_bavail;
201         l_int           f_files;
202         l_int           f_ffree;
203         l_fsid_t        f_fsid;
204         l_int           f_namelen;
205         l_int           f_spare[6];
206 };
207
208 #define LINUX_CODA_SUPER_MAGIC  0x73757245L
209 #define LINUX_EXT2_SUPER_MAGIC  0xEF53L
210 #define LINUX_HPFS_SUPER_MAGIC  0xf995e849L
211 #define LINUX_ISOFS_SUPER_MAGIC 0x9660L
212 #define LINUX_MSDOS_SUPER_MAGIC 0x4d44L
213 #define LINUX_NCP_SUPER_MAGIC   0x564cL
214 #define LINUX_NFS_SUPER_MAGIC   0x6969L
215 #define LINUX_NTFS_SUPER_MAGIC  0x5346544EL
216 #define LINUX_PROC_SUPER_MAGIC  0x9fa0L
217 #define LINUX_UFS_SUPER_MAGIC   0x00011954L     /* XXX - UFS_MAGIC in Linux */
218
219 static long
220 bsd_to_linux_ftype(const char *fstypename)
221 {
222         int i;
223         static struct {const char *bsd_name; long linux_type;} b2l_tbl[] = {
224                 {"ufs",     LINUX_UFS_SUPER_MAGIC},
225                 {"cd9660",  LINUX_ISOFS_SUPER_MAGIC},
226                 {"nfs",     LINUX_NFS_SUPER_MAGIC},
227                 {"ext2fs",  LINUX_EXT2_SUPER_MAGIC},
228                 {"procfs",  LINUX_PROC_SUPER_MAGIC},
229                 {"msdosfs", LINUX_MSDOS_SUPER_MAGIC},
230                 {"ntfs",    LINUX_NTFS_SUPER_MAGIC},
231                 {"hpfs",    LINUX_HPFS_SUPER_MAGIC},
232                 {NULL,      0L}};
233
234         for (i = 0; b2l_tbl[i].bsd_name != NULL; i++)
235                 if (strcmp(b2l_tbl[i].bsd_name, fstypename) == 0)
236                         return (b2l_tbl[i].linux_type);
237
238         return (0L);
239 }
240
241 static int
242 statfs_copyout(struct statfs *statfs, struct l_statfs_buf *buf, l_int namelen)
243 {
244         struct l_statfs linux_statfs;
245         int error;
246
247         linux_statfs.f_type = bsd_to_linux_ftype(statfs->f_fstypename);
248         linux_statfs.f_bsize = statfs->f_bsize;
249         linux_statfs.f_blocks = statfs->f_blocks;
250         linux_statfs.f_bfree = statfs->f_bfree;
251         linux_statfs.f_bavail = statfs->f_bavail;
252         linux_statfs.f_ffree = statfs->f_ffree;
253         linux_statfs.f_files = statfs->f_files;
254         linux_statfs.f_fsid.val[0] = statfs->f_fsid.val[0];
255         linux_statfs.f_fsid.val[1] = statfs->f_fsid.val[1];
256         linux_statfs.f_namelen = namelen;
257
258         error = copyout(&linux_statfs, buf, sizeof(linux_statfs));
259         return (error);
260 }
261
262 /*
263  * MPALMOSTSAFE
264  */
265 int
266 sys_linux_statfs(struct linux_statfs_args *args)
267 {
268         struct statfs statfs;
269         struct nlookupdata nd;
270         char *path;
271         int error, namelen;
272
273         error = linux_copyin_path(args->path, &path, LINUX_PATH_EXISTS);
274         if (error)
275                 return (error);
276 #ifdef DEBUG
277         if (ldebug(statfs))
278                 kprintf(ARGS(statfs, "%s, *"), path);
279 #endif
280         get_mplock();
281         error = nlookup_init(&nd, path, UIO_SYSSPACE, NLC_FOLLOW);
282         if (error == 0)
283                 error = kern_statfs(&nd, &statfs);
284         if (error == 0) {
285                 if (nd.nl_nch.ncp->nc_vp != NULL)
286                         error = vn_get_namelen(nd.nl_nch.ncp->nc_vp, &namelen);
287                 else
288                         error = EINVAL;
289         }
290         nlookup_done(&nd);
291         rel_mplock();
292         if (error == 0)
293                 error = statfs_copyout(&statfs, args->buf, (l_int)namelen);
294         linux_free_path(&path);
295         return (error);
296 }
297
298 /*
299  * MPALMOSTSAFE
300  */
301 int
302 sys_linux_fstatfs(struct linux_fstatfs_args *args)
303 {
304         struct proc *p = curthread->td_proc;
305         struct file *fp;
306         struct statfs statfs;
307         int error, namelen;
308
309 #ifdef DEBUG
310         if (ldebug(fstatfs))
311                 kprintf(ARGS(fstatfs, "%d, *"), args->fd);
312 #endif
313         get_mplock();
314         if ((error = kern_fstatfs(args->fd, &statfs)) != 0) {
315                 rel_mplock();
316                 return (error);
317         }
318         if ((error = holdvnode(p->p_fd, args->fd, &fp)) != 0) {
319                 rel_mplock();
320                 return (error);
321         }
322         error = vn_get_namelen((struct vnode *)fp->f_data, &namelen);
323         rel_mplock();
324         fdrop(fp);
325         if (error == 0)
326                 error = statfs_copyout(&statfs, args->buf, (l_int)namelen);
327         return (error);
328 }
329
330 struct l_ustat 
331 {
332         l_daddr_t       f_tfree;
333         l_ino_t         f_tinode;
334         char            f_fname[6];
335         char            f_fpack[6];
336 };
337
338 /*
339  * MPALMOSTSAFE
340  */
341 int
342 sys_linux_ustat(struct linux_ustat_args *args)
343 {
344         struct thread *td = curthread;
345         struct l_ustat lu;
346         cdev_t dev;
347         struct vnode *vp;
348         struct statfs *stat;
349         int error;
350
351 #ifdef DEBUG
352         if (ldebug(ustat))
353                 kprintf(ARGS(ustat, "%d, *"), args->dev);
354 #endif
355
356         /*
357          * lu.f_fname and lu.f_fpack are not used. They are always zeroed.
358          * lu.f_tinode and lu.f_tfree are set from the device's super block.
359          */
360         bzero(&lu, sizeof(lu));
361
362         /*
363          * XXX - Don't return an error if we can't find a vnode for the
364          * device. Our cdev_t is 32-bits whereas Linux only has a 16-bits
365          * cdev_t. The dev_t that is used now may as well be a truncated
366          * cdev_t returned from previous syscalls. Just return a bzeroed
367          * ustat in that case.
368          */
369         get_mplock();
370         dev = udev2dev(makeudev(args->dev >> 8, args->dev & 0xFF), 0);
371         if (dev != NULL && vfinddev(dev, VCHR, &vp)) {
372                 if (vp->v_mount == NULL) {
373                         vrele(vp);
374                         error = EINVAL;
375                         goto done;
376                 }
377                 stat = &(vp->v_mount->mnt_stat);
378                 error = VFS_STATFS(vp->v_mount, stat, td->td_ucred);
379                 vrele(vp);
380                 if (error == 0) {
381                         lu.f_tfree = stat->f_bfree;
382                         lu.f_tinode = stat->f_ffree;
383                 }
384         } else {
385                 error = 0;
386         }
387 done:
388         rel_mplock();
389         if (error == 0)
390                 error = copyout(&lu, args->ubuf, sizeof(lu));
391         return (error);
392 }
393
394 #if defined(__i386__)
395
396 static int
397 stat64_copyout(struct stat *buf, void *ubuf)
398 {
399         struct l_stat64 lbuf;
400         int error;
401
402         bzero(&lbuf, sizeof(lbuf));
403         lbuf.st_dev = uminor(buf->st_dev) | (umajor(buf->st_dev) << 8);
404         lbuf.st_ino = INO64TO32(buf->st_ino);
405         lbuf.st_mode = buf->st_mode;
406         lbuf.st_nlink = buf->st_nlink;
407         lbuf.st_uid = buf->st_uid;
408         lbuf.st_gid = buf->st_gid;
409         lbuf.st_rdev = buf->st_rdev;
410         lbuf.st_size = buf->st_size;
411         lbuf.st_atime = buf->st_atime;
412         lbuf.st_mtime = buf->st_mtime;
413         lbuf.st_ctime = buf->st_ctime;
414         lbuf.st_blksize = buf->st_blksize;
415         lbuf.st_blocks = buf->st_blocks;
416
417         /*
418          * The __st_ino field makes all the difference. In the Linux kernel
419          * it is conditionally compiled based on STAT64_HAS_BROKEN_ST_INO,
420          * but without the assignment to __st_ino the runtime linker refuses
421          * to mmap(2) any shared libraries. I guess it's broken alright :-)
422          */
423         lbuf.__st_ino = INO64TO32(buf->st_ino);
424
425         error = copyout(&lbuf, ubuf, sizeof(lbuf));
426         return (error);
427 }
428
429 /*
430  * MPALMOSTSAFE
431  */
432 int
433 sys_linux_stat64(struct linux_stat64_args *args)
434 {
435         struct nlookupdata nd;
436         struct stat buf;
437         char *path;
438         int error;
439
440         error = linux_copyin_path(args->filename, &path, LINUX_PATH_EXISTS);
441         if (error)
442                 return (error);
443 #ifdef DEBUG
444         if (ldebug(stat64))
445                 kprintf(ARGS(stat64, "%s, *"), path);
446 #endif
447         get_mplock();
448         error = nlookup_init(&nd, path, UIO_SYSSPACE, NLC_FOLLOW);
449         if (error == 0) {
450                 error = kern_stat(&nd, &buf);
451                 nlookup_done(&nd);
452         }
453         rel_mplock();
454         if (error == 0)
455                 error = stat64_copyout(&buf, args->statbuf);
456         linux_free_path(&path);
457         return (error);
458 }
459
460 /*
461  * MPALMOSTSAFE
462  */
463 int
464 sys_linux_lstat64(struct linux_lstat64_args *args)
465 {
466         struct nlookupdata nd;
467         struct stat sb;
468         char *path;
469         int error;
470
471         error = linux_copyin_path(args->filename, &path, LINUX_PATH_EXISTS);
472         if (error)
473                 return (error);
474 #ifdef DEBUG
475         if (ldebug(lstat64))
476                 kprintf(ARGS(lstat64, "%s, *"), path);
477 #endif
478         get_mplock();
479         error = nlookup_init(&nd, path, UIO_SYSSPACE, 0);
480         if (error == 0) {
481                 error = kern_stat(&nd, &sb);
482                 nlookup_done(&nd);
483         }
484         rel_mplock();
485         if (error == 0)
486                 error = stat64_copyout(&sb, args->statbuf);
487         linux_free_path(&path);
488         return (error);
489 }
490
491 /*
492  * MPALMOSTSAFE
493  */
494 int
495 sys_linux_fstat64(struct linux_fstat64_args *args)
496 {
497         struct stat buf;
498         int error;
499
500 #ifdef DEBUG
501         if (ldebug(fstat64))
502                 kprintf(ARGS(fstat64, "%d, *"), args->fd);
503 #endif
504         get_mplock();
505         error = kern_fstat(args->fd, &buf);
506         rel_mplock();
507
508         if (error == 0)
509                 error = stat64_copyout(&buf, args->statbuf);
510         return (error);
511 }
512
513 int
514 sys_linux_fstatat64(struct linux_fstatat64_args *args)
515 {
516         struct nlookupdata nd;
517         struct file *fp;
518         struct stat st;
519         char *path;
520         int error, flags, dfd;
521
522         if (args->flag & ~LINUX_AT_SYMLINK_NOFOLLOW)
523                 return (EINVAL);
524
525         error = linux_copyin_path(args->path, &path, LINUX_PATH_EXISTS);
526         if (error)
527                 return (error);
528 #ifdef DEBUG
529         if (ldebug(fstatat64))
530                 kprintf(ARGS(fstatat64, "%s"), path);
531 #endif
532         dfd = (args->dfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->dfd;
533         flags = (args->flag & LINUX_AT_SYMLINK_NOFOLLOW) ? 0 : NLC_FOLLOW;
534
535         error = nlookup_init_at(&nd, &fp, dfd, path, UIO_SYSSPACE, flags);
536         if (error == 0) {
537                 error = kern_stat(&nd, &st);
538                 if (error == 0)
539                         error = stat64_copyout(&st, args->statbuf);
540         }
541         nlookup_done_at(&nd, fp);
542         linux_free_path(&path);
543         return (error);
544 }
545
546 int
547 sys_linux_ostat(struct linux_ostat_args *args)
548 {
549         struct nlookupdata nd;
550         struct stat buf;
551         char *path;
552         int error;
553
554         error = linux_copyin_path(args->path, &path, LINUX_PATH_EXISTS);
555         if (error)
556                 return (error);
557 #ifdef DEBUG
558         if (ldebug(ostat))
559                 kprintf(ARGS(ostat, "%s, *"), path);
560 #endif
561         get_mplock();
562         error = nlookup_init(&nd, path, UIO_SYSSPACE, NLC_FOLLOW);
563         if (error == 0) {
564                 error = kern_stat(&nd, &buf);
565                 nlookup_done(&nd);
566         }
567         rel_mplock();
568         if (error == 0)
569                 error = ostat_copyout(&buf, args->statbuf);
570         linux_free_path(&path);
571         return (error);
572 }
573
574 #endif /* __i386__ */