Project

General

Profile

Statistics
| Branch: | Revision:

scoutos / prex-0.9.0 / usr / server / fs / vfs / vfs_syscalls.c @ 03e9c04a

History | View | Annotate | Download (13.8 KB)

1 03e9c04a Brad Neuman
/*
2
 * Copyright (c) 2005-2007, Kohsuke Ohtani
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
 * 2. Redistributions in binary form must reproduce the above copyright
11
 *    notice, this list of conditions and the following disclaimer in the
12
 *    documentation and/or other materials provided with the distribution.
13
 * 3. Neither the name of the author nor the names of any co-contributors
14
 *    may be used to endorse or promote products derived from this software
15
 *    without specific prior written permission.
16
 *
17
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27
 * SUCH DAMAGE.
28
 */
29
30
/*
31
 * vfs_syscalls.c - everything in this file is a routine implementing
32
 *                  a VFS system call.
33
 */
34
35
#include <sys/prex.h>
36
#include <sys/stat.h>
37
#include <sys/vnode.h>
38
#include <sys/file.h>
39
#include <sys/mount.h>
40
#include <sys/dirent.h>
41
#include <sys/list.h>
42
#include <sys/buf.h>
43
44
#include <limits.h>
45
#include <unistd.h>
46
#include <stdlib.h>
47
#include <string.h>
48
#include <stdio.h>
49
#include <errno.h>
50
#include <fcntl.h>
51
52
#include "vfs.h"
53
54
int
55
sys_open(char *path, int flags, mode_t mode, file_t *pfp)
56
{
57
        vnode_t vp, dvp;
58
        file_t fp;
59
        char *filename;
60
        int error;
61
62
        DPRINTF(VFSDB_SYSCALL, ("sys_open: path=%s flags=%x mode=%x\n",
63
                                path, flags, mode));
64
65
        flags = FFLAGS(flags);
66
        if  ((flags & (FREAD | FWRITE)) == 0)
67
                return EINVAL;
68
        if (flags & O_CREAT) {
69
                error = namei(path, &vp);
70
                if (error == ENOENT) {
71
                        /* Create new file. */
72
                        if ((error = lookup(path, &dvp, &filename)) != 0)
73
                                return error;
74
                        if ((error = vn_access(dvp, VWRITE)) != 0) {
75
                                vput(dvp);
76
                                return error;
77
                        }
78
                        mode &= ~S_IFMT;
79
                        mode |= S_IFREG;
80
                        error = VOP_CREATE(dvp, filename, mode);
81
                        vput(dvp);
82
                        if (error)
83
                                return error;
84
                        if ((error = namei(path, &vp)) != 0)
85
                                return error;
86
                        flags &= ~O_TRUNC;
87
                } else if (error) {
88
                        return error;
89
                } else {
90
                        /* File already exits */
91
                        if (flags & O_EXCL) {
92
                                vput(vp);
93
                                return EEXIST;
94
                        }
95
                        flags &= ~O_CREAT;
96
                }
97
        } else {
98
                /* Open */
99
                if ((error = namei(path, &vp)) != 0)
100
                        return error;
101
        }
102
        if ((flags & O_CREAT) == 0) {
103
                if (flags & FWRITE || flags & O_TRUNC) {
104
                        if ((error = vn_access(vp, VWRITE)) != 0) {
105
                                vput(vp);
106
                                return error;
107
                        }
108
                        if (vp->v_type == VDIR) {
109
                                /* Openning directory with writable. */
110
                                vput(vp);
111
                                return EISDIR;
112
                        }
113
                }
114
        }
115
        /* Process truncate request */
116
        if (flags & O_TRUNC) {
117
                if (!(flags & FWRITE) || (vp->v_type == VDIR)) {
118
                        vput(vp);
119
                        return EINVAL;
120
                }
121
                if ((error = VOP_TRUNCATE(vp, 0)) != 0) {
122
                        vput(vp);
123
                        return error;
124
                }
125
        }
126
        /* Setup file structure */
127
        if (!(fp = malloc(sizeof(struct file)))) {
128
                vput(vp);
129
                return ENOMEM;
130
        }
131
        /* Request to file system */
132
        if ((error = VOP_OPEN(vp, flags)) != 0) {
133
                free(fp);
134
                vput(vp);
135
                return error;
136
        }
137
        memset(fp, 0, sizeof(struct file));
138
        fp->f_vnode = vp;
139
        fp->f_flags = flags;
140
        fp->f_offset = 0;
141
        fp->f_count = 1;
142
        *pfp = fp;
143
        vn_unlock(vp);
144
        return 0;
145
}
146
147
int
148
sys_close(file_t fp)
149
{
150
        vnode_t vp;
151
        int error;
152
153
        DPRINTF(VFSDB_SYSCALL, ("sys_close: fp=%x count=%d\n",
154
                                (u_int)fp, fp->f_count));
155
156
        if (fp->f_count <= 0)
157
                sys_panic("sys_close");
158
159
        vp = fp->f_vnode;
160
        if (--fp->f_count > 0) {
161
                vrele(vp);
162
                return 0;
163
        }
164
        vn_lock(vp);
165
        if ((error = VOP_CLOSE(vp, fp)) != 0) {
166
                vn_unlock(vp);
167
                return error;
168
        }
169
        vput(vp);
170
        free(fp);
171
        return 0;
172
}
173
174
int
175
sys_read(file_t fp, void *buf, size_t size, size_t *count)
176
{
177
        vnode_t vp;
178
        int error;
179
180
        DPRINTF(VFSDB_SYSCALL, ("sys_read: fp=%x buf=%x size=%d\n",
181
                                (u_int)fp, (u_int)buf, size));
182
183
        if ((fp->f_flags & FREAD) == 0)
184
                return EBADF;
185
        if (size == 0) {
186
                *count = 0;
187
                return 0;
188
        }
189
        vp = fp->f_vnode;
190
        vn_lock(vp);
191
        error = VOP_READ(vp, fp, buf, size, count);
192
        vn_unlock(vp);
193
        return error;
194
}
195
196
int
197
sys_write(file_t fp, void *buf, size_t size, size_t *count)
198
{
199
        vnode_t vp;
200
        int error;
201
202
        DPRINTF(VFSDB_SYSCALL, ("sys_write: fp=%x buf=%x size=%d\n",
203
                                (u_int)fp, (u_int)buf, size));
204
205
        if ((fp->f_flags & FWRITE) == 0)
206
                return EBADF;
207
        if (size == 0) {
208
                *count = 0;
209
                return 0;
210
        }
211
        vp = fp->f_vnode;
212
        vn_lock(vp);
213
        error = VOP_WRITE(vp, fp, buf, size, count);
214
        vn_unlock(vp);
215
        return error;
216
}
217
218
int
219
sys_lseek(file_t fp, off_t off, int type, off_t *origin)
220
{
221
        vnode_t vp;
222
223
        DPRINTF(VFSDB_SYSCALL, ("sys_seek: fp=%x off=%d type=%d\n",
224
                                (u_int)fp, (u_int)off, type));
225
226
        vp = fp->f_vnode;
227
        vn_lock(vp);
228
        switch (type) {
229
        case SEEK_SET:
230
                if (off < 0)
231
                        off = 0;
232
                if (off > (off_t)vp->v_size)
233
                        off = vp->v_size;
234
                break;
235
        case SEEK_CUR:
236
                if (fp->f_offset + off > (off_t)vp->v_size)
237
                        off = vp->v_size;
238
                else if (fp->f_offset + off < 0)
239
                        off = 0;
240
                else
241
                        off = fp->f_offset + off;
242
                break;
243
        case SEEK_END:
244
                if (off > 0)
245
                        off = vp->v_size;
246
                else if ((int)vp->v_size + off < 0)
247
                        off = 0;
248
                else
249
                        off = vp->v_size + off;
250
                break;
251
        default:
252
                vn_unlock(vp);
253
                return EINVAL;
254
        }
255
        /* Request to check the file offset */
256
        if (VOP_SEEK(vp, fp, fp->f_offset, off) != 0) {
257
                vn_unlock(vp);
258
                return EINVAL;
259
        }
260
        *origin = off;
261
        fp->f_offset = off;
262
        vn_unlock(vp);
263
        return 0;
264
}
265
266
int
267
sys_ioctl(file_t fp, u_long request, void *buf)
268
{
269
        vnode_t vp;
270
        int error;
271
272
        DPRINTF(VFSDB_SYSCALL, ("sys_ioctl: fp=%x request=%x\n", fp, request));
273
274
        if ((fp->f_flags & (FREAD | FWRITE)) == 0)
275
                return EBADF;
276
277
        vp = fp->f_vnode;
278
        vn_lock(vp);
279
        error = VOP_IOCTL(vp, fp, request, buf);
280
        vn_unlock(vp);
281
        DPRINTF(VFSDB_SYSCALL, ("sys_ioctl: comp error=%d\n", error));
282
        return error;
283
}
284
285
int
286
sys_fsync(file_t fp)
287
{
288
        vnode_t vp;
289
        int error;
290
291
        DPRINTF(VFSDB_SYSCALL, ("sys_fsync: fp=%x\n", fp));
292
293
        if ((fp->f_flags & FWRITE) == 0)
294
                return EBADF;
295
296
        vp = fp->f_vnode;
297
        vn_lock(vp);
298
        error = VOP_FSYNC(vp, fp);
299
        vn_unlock(vp);
300
        return error;
301
}
302
303
int
304
sys_fstat(file_t fp, struct stat *st)
305
{
306
        vnode_t vp;
307
        int error = 0;
308
309
        DPRINTF(VFSDB_SYSCALL, ("sys_fstat: fp=%x\n", fp));
310
311
        vp = fp->f_vnode;
312
        vn_lock(vp);
313
        error = vn_stat(vp, st);
314
        vn_unlock(vp);
315
        return error;
316
}
317
318
/*
319
 * Return 0 if directory is empty
320
 */
321
static int
322
check_dir_empty(char *path)
323
{
324
        int error;
325
        file_t fp;
326
        struct dirent dir;
327
328
        DPRINTF(VFSDB_SYSCALL, ("check_dir_empty\n"));
329
330
        if ((error = sys_opendir(path, &fp)) != 0)
331
                return error;
332
        do {
333
                error = sys_readdir(fp, &dir);
334
                if (error != 0 && error != EACCES)
335
                        break;
336
        } while (!strcmp(dir.d_name, ".") || !strcmp(dir.d_name, ".."));
337
338
        sys_closedir(fp);
339
340
        if (error == ENOENT)
341
                return 0;
342
        else if (error == 0)
343
                return EEXIST;
344
        return error;
345
}
346
347
int
348
sys_opendir(char *path, file_t *file)
349
{
350
        vnode_t dvp;
351
        file_t fp;
352
        int error;
353
354
        DPRINTF(VFSDB_SYSCALL, ("sys_opendir: path=%s\n", path));
355
356
        if ((error = sys_open(path, O_RDONLY, 0, &fp)) != 0)
357
                return error;
358
359
        dvp = fp->f_vnode;
360
        vn_lock(dvp);
361
        if (dvp->v_type != VDIR) {
362
                vn_unlock(dvp);
363
                sys_close(fp);
364
                return ENOTDIR;
365
        }
366
        vn_unlock(dvp);
367
368
        *file = fp;
369
        return 0;
370
}
371
372
int
373
sys_closedir(file_t fp)
374
{
375
        vnode_t dvp;
376
        int error;
377
378
        DPRINTF(VFSDB_SYSCALL, ("sys_closedir: fp=%x\n", fp));
379
380
        dvp = fp->f_vnode;
381
        vn_lock(dvp);
382
        if (dvp->v_type != VDIR) {
383
                vn_unlock(dvp);
384
                return EBADF;
385
        }
386
        vn_unlock(dvp);
387
        error = sys_close(fp);
388
        return error;
389
}
390
391
int
392
sys_readdir(file_t fp, struct dirent *dir)
393
{
394
        vnode_t dvp;
395
        int error;
396
397
        DPRINTF(VFSDB_SYSCALL, ("sys_readdir: fp=%x\n", fp));
398
399
        dvp = fp->f_vnode;
400
        vn_lock(dvp);
401
        if (dvp->v_type != VDIR) {
402
                vn_unlock(dvp);
403
                return EBADF;
404
        }
405
        error = VOP_READDIR(dvp, fp, dir);
406
        DPRINTF(VFSDB_SYSCALL, ("sys_readdir: error=%d path=%s\n",
407
                                error, dir->d_name));
408
        vn_unlock(dvp);
409
        return error;
410
}
411
412
int
413
sys_rewinddir(file_t fp)
414
{
415
        vnode_t dvp;
416
417
        dvp = fp->f_vnode;
418
        vn_lock(dvp);
419
        if (dvp->v_type != VDIR) {
420
                vn_unlock(dvp);
421
                return EBADF;
422
        }
423
        fp->f_offset = 0;
424
        vn_unlock(dvp);
425
        return 0;
426
}
427
428
int
429
sys_seekdir(file_t fp, long loc)
430
{
431
        vnode_t dvp;
432
433
        dvp = fp->f_vnode;
434
        vn_lock(dvp);
435
        if (dvp->v_type != VDIR) {
436
                vn_unlock(dvp);
437
                return EBADF;
438
        }
439
        fp->f_offset = (off_t)loc;
440
        vn_unlock(dvp);
441
        return 0;
442
}
443
444
int
445
sys_telldir(file_t fp, long *loc)
446
{
447
        vnode_t dvp;
448
449
        dvp = fp->f_vnode;
450
        vn_lock(dvp);
451
        if (dvp->v_type != VDIR) {
452
                vn_unlock(dvp);
453
                return EBADF;
454
        }
455
        *loc = (long)fp->f_offset;
456
        vn_unlock(dvp);
457
        return 0;
458
}
459
460
int
461
sys_mkdir(char *path, mode_t mode)
462
{
463
        char *name;
464
        vnode_t vp, dvp;
465
        int error;
466
467
        DPRINTF(VFSDB_SYSCALL, ("sys_mkdir: path=%s mode=%d\n",        path, mode));
468
469
        if ((error = namei(path, &vp)) == 0) {
470
                /* File already exists */
471
                vput(vp);
472
                return EEXIST;
473
        }
474
        /* Notice: vp is invalid here! */
475
476
        if ((error = lookup(path, &dvp, &name)) != 0) {
477
                /* Directory already exists */
478
                return error;
479
        }
480
        if ((error = vn_access(dvp, VWRITE)) != 0)
481
                goto out;
482
        mode &= ~S_IFMT;
483
        mode |= S_IFDIR;
484
485
        error = VOP_MKDIR(dvp, name, mode);
486
 out:
487
        vput(dvp);
488
        return error;
489
}
490
491
int
492
sys_rmdir(char *path)
493
{
494
        vnode_t vp, dvp;
495
        int error;
496
        char *name;
497
498
        DPRINTF(VFSDB_SYSCALL, ("sys_rmdir: path=%s\n", path));
499
500
        if ((error = check_dir_empty(path)) != 0)
501
                return error;
502
        if ((error = namei(path, &vp)) != 0)
503
                return error;
504
        if ((error = vn_access(vp, VWRITE)) != 0)
505
                goto out;
506
        if (vp->v_type != VDIR) {
507
                error = ENOTDIR;
508
                goto out;
509
        }
510
        if (vp->v_flags & VROOT || vcount(vp) >= 2) {
511
                error = EBUSY;
512
                goto out;
513
        }
514
        if ((error = lookup(path, &dvp, &name)) != 0)
515
                goto out;
516
517
        error = VOP_RMDIR(dvp, vp, name);
518
        vn_unlock(vp);
519
        vgone(vp);
520
        vput(dvp);
521
        return error;
522
523
 out:
524
        vput(vp);
525
        return error;
526
}
527
528
int
529
sys_mknod(char *path, mode_t mode)
530
{
531
        char *name;
532
        vnode_t vp, dvp;
533
        int error;
534
535
        DPRINTF(VFSDB_SYSCALL, ("sys_mknod: path=%s mode=%d\n",        path, mode));
536
537
        switch (mode & S_IFMT) {
538
        case S_IFREG:
539
        case S_IFDIR:
540
        case S_IFIFO:
541
        case S_IFSOCK:
542
                /* OK */
543
                break;
544
        default:
545
                return EINVAL;
546
        }
547
548
        if ((error = namei(path, &vp)) == 0) {
549
                vput(vp);
550
                return EEXIST;
551
        }
552
553
        if ((error = lookup(path, &dvp, &name)) != 0)
554
                return error;
555
        if ((error = vn_access(dvp, VWRITE)) != 0)
556
                goto out;
557
        if (S_ISDIR(mode))
558
                error = VOP_MKDIR(dvp, name, mode);
559
        else
560
                error = VOP_CREATE(dvp, name, mode);
561
 out:
562
        vput(dvp);
563
        return error;
564
}
565
566
int
567
sys_rename(char *src, char *dest)
568
{
569
        vnode_t vp1, vp2 = 0, dvp1, dvp2;
570
        char *sname, *dname;
571
        int error;
572
        size_t len;
573
        char root[] = "/";
574
575
        DPRINTF(VFSDB_SYSCALL, ("sys_rename: src=%s dest=%s\n", src, dest));
576
577
        if ((error = namei(src, &vp1)) != 0)
578
                return error;
579
        if ((error = vn_access(vp1, VWRITE)) != 0)
580
                goto err1;
581
582
        /* If source and dest are the same, do nothing */
583
        if (!strncmp(src, dest, PATH_MAX))
584
                goto err1;
585
586
        /* Check if target is directory of source */
587
        len = strlen(dest);
588
        if (!strncmp(src, dest, len)) {
589
                error = EINVAL;
590
                goto err1;
591
        }
592
        /* Is the source busy ? */
593
        if (vcount(vp1) >= 2) {
594
                error = EBUSY;
595
                goto err1;
596
        }
597
        /* Check type of source & target */
598
        error = namei(dest, &vp2);
599
        if (error == 0) {
600
                /* target exists */
601
                if (vp1->v_type == VDIR && vp2->v_type != VDIR) {
602
                        error = ENOTDIR;
603
                        goto err2;
604
                } else if (vp1->v_type != VDIR && vp2->v_type == VDIR) {
605
                        error = EISDIR;
606
                        goto err2;
607
                }
608
                if (vp2->v_type == VDIR && check_dir_empty(dest)) {
609
                        error = EEXIST;
610
                        goto err2;
611
                }
612
613
                if (vcount(vp2) >= 2) {
614
                        error = EBUSY;
615
                        goto err2;
616
                }
617
        }
618
619
        dname = strrchr(dest, '/');
620
        if (dname == NULL) {
621
                error = ENOTDIR;
622
                goto err2;
623
        }
624
        if (dname == dest)
625
                dest = root;
626
627
        *dname = 0;
628
        dname++;
629
630
        if ((error = lookup(src, &dvp1, &sname)) != 0)
631
                goto err2;
632
633
        if ((error = namei(dest, &dvp2)) != 0)
634
                goto err3;
635
636
        /* The source and dest must be same file system */
637
        if (dvp1->v_mount != dvp2->v_mount) {
638
                error = EXDEV;
639
                goto err4;
640
        }
641
        error = VOP_RENAME(dvp1, vp1, sname, dvp2, vp2, dname);
642
 err4:
643
        vput(dvp2);
644
 err3:
645
        vput(dvp1);
646
 err2:
647
        if (vp2)
648
                vput(vp2);
649
 err1:
650
        vput(vp1);
651
        return error;
652
}
653
654
int
655
sys_unlink(char *path)
656
{
657
        char *name;
658
        vnode_t vp, dvp;
659
        int error;
660
661
        DPRINTF(VFSDB_SYSCALL, ("sys_unlink: path=%s\n", path));
662
663
        if ((error = namei(path, &vp)) != 0)
664
                return error;
665
        if ((error = vn_access(vp, VWRITE)) != 0)
666
                goto out;
667
        if (vp->v_type == VDIR) {
668
                error = EPERM;
669
                goto out;
670
        }
671
        /* XXX: Need to allow unlink for opened file. */
672
        if (vp->v_flags & VROOT || vcount(vp) >= 2) {
673
                error = EBUSY;
674
                goto out;
675
        }
676
        if ((error = lookup(path, &dvp, &name)) != 0)
677
                goto out;
678
679
        error = VOP_REMOVE(dvp, vp, name);
680
681
        vn_unlock(vp);
682
        vgone(vp);
683
        vput(dvp);
684
        return 0;
685
 out:
686
        vput(vp);
687
        return error;
688
}
689
690
int
691
sys_access(char *path, int mode)
692
{
693
        vnode_t vp;
694
        int error, flags;
695
696
        DPRINTF(VFSDB_SYSCALL, ("sys_access: path=%s mode=%x\n", path, mode));
697
698
        /* If F_OK is set, we return here if file is not found. */
699
        if ((error = namei(path, &vp)) != 0)
700
                return error;
701
702
        flags = 0;
703
        if (mode & R_OK)
704
                flags |= VREAD;
705
        if (mode & W_OK)
706
                flags |= VWRITE;
707
        if (mode & X_OK)
708
                flags |= VEXEC;
709
710
        error = vn_access(vp, flags);
711
712
        vput(vp);
713
        return error;
714
}
715
716
int
717
sys_stat(char *path, struct stat *st)
718
{
719
        vnode_t vp;
720
        int error;
721
722
        DPRINTF(VFSDB_SYSCALL, ("sys_stat: path=%s\n", path));
723
724
        if ((error = namei(path, &vp)) != 0)
725
                return error;
726
        error = vn_stat(vp, st);
727
        vput(vp);
728
        return error;
729
}
730
731
int
732
sys_truncate(char *path, off_t length)
733
{
734
        return 0;
735
}
736
737
int
738
sys_ftruncate(file_t fp, off_t length)
739
{
740
        return 0;
741
}
742
743
int
744
sys_fchdir(file_t fp, char *cwd)
745
{
746
        vnode_t dvp;
747
748
        dvp = fp->f_vnode;
749
        vn_lock(dvp);
750
        if (dvp->v_type != VDIR) {
751
                vn_unlock(dvp);
752
                return EBADF;
753
        }
754
        strlcpy(cwd, dvp->v_path, PATH_MAX);
755
        vn_unlock(dvp);
756
        return 0;
757
}