Whamcloud - gitweb
c66c1edc26a5f53cc763a5bb4dc4e4ffd3a3e53c
[fs/lustre-release.git] / libsysio / drivers / native / fs_native.c
1 /*
2  *    This Cplant(TM) source code is the property of Sandia National
3  *    Laboratories.
4  *
5  *    This Cplant(TM) source code is copyrighted by Sandia National
6  *    Laboratories.
7  *
8  *    The redistribution of this Cplant(TM) source code is subject to the
9  *    terms of the GNU Lesser General Public License
10  *    (see cit/LGPL or http://www.gnu.org/licenses/lgpl.html)
11  *
12  *    Cplant(TM) Copyright 1998-2004 Sandia Corporation. 
13  *    Under the terms of Contract DE-AC04-94AL85000, there is a non-exclusive
14  *    license for use of this work by or on behalf of the US Government.
15  *    Export of this program may require a license from the United States
16  *    Government.
17  */
18
19 /*
20  * This library is free software; you can redistribute it and/or
21  * modify it under the terms of the GNU Lesser General Public
22  * License as published by the Free Software Foundation; either
23  * version 2.1 of the License, or (at your option) any later version.
24  * 
25  * This library is distributed in the hope that it will be useful,
26  * but WITHOUT ANY WARRANTY; without even the implied warranty of
27  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
28  * Lesser General Public License for more details.
29  * 
30  * You should have received a copy of the GNU Lesser General Public
31  * License along with this library; if not, write to the Free Software
32  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
33  *
34  * Questions or comments about this library should be sent to:
35  *
36  * Lee Ward
37  * Sandia National Laboratories, New Mexico
38  * P.O. Box 5800
39  * Albuquerque, NM 87185-1110
40  *
41  * lee@sandia.gov
42  */
43
44 #ifdef __linux__
45 #define _BSD_SOURCE
46 #endif
47
48 #include <stdio.h>                                      /* for NULL */
49 #include <stdlib.h>
50 #ifdef __linux__
51 #include <string.h>
52 #endif
53 #include <unistd.h>
54 #include <errno.h>
55 #include <assert.h>
56 #include <syscall.h>
57 #include <sys/types.h>
58 #include <sys/stat.h>
59 #include <sys/fcntl.h>
60 #if 0
61 #include <sys/vfs.h>
62 #endif
63 #ifdef _HAVE_STATVFS
64 #include <sys/statvfs.h>
65 #include <sys/statfs.h>
66 #endif
67 #include <utime.h>
68 #include <sys/queue.h>
69 #if !(defined(REDSTORM) || defined(MAX_IOVEC))
70 #include <limits.h>
71 #endif
72
73 #include "sysio.h"
74 #include "fs.h"
75 #include "mount.h"
76 #include "inode.h"
77 #include "xtio.h"
78
79 #include "fs_native.h"
80
81 #ifdef REDSTORM
82 #include <sys/uio.h>
83 #endif
84
85 #if defined(SYS_getdirentries)
86 #define DIR_STREAMED 0
87 #define DIR_CVT_64 0
88 #elif defined(SYS_getdents64)
89 #define DIR_STREAMED 1
90 #define DIR_CVT_64 0
91 #elif defined(SYS_getdents)
92 #define DIR_STREAMED 1
93 #if defined(_LARGEFILE64_SOURCE)
94 #define DIR_CVT_64 1
95 /*
96  * Kernel version of directory entry.
97  */
98 struct linux_dirent {
99         unsigned long ld_ino;
100         unsigned long ld_off;
101         unsigned short ld_reclen;
102         char    ld_name[1];
103 };
104 #include <dirent.h>
105 #else /* !defined(_LARGEFILE64_SOURCE) */
106 #define DIR_CVT_64 0
107 #endif /* defined(_LARGEFILE64_SOURCE) */
108 #else /* catch-none */
109 #error No usable directory fill entries interface available
110 #endif
111
112 /*
113  * Local host file system driver.
114  */
115
116 #if defined(ALPHA_LINUX)
117
118 /* stat struct from asm/stat.h, as returned 
119  * by alpha linux kernel
120  */
121 struct __native_stat {
122         unsigned int    st_dev;
123         unsigned int    st_ino;
124         unsigned int    st_mode;
125         unsigned int    st_nlink;
126         unsigned int    st_uid;
127         unsigned int    st_gid;
128         unsigned int    st_rdev;
129         long            st_size;
130         unsigned long   st_atime;
131         unsigned long   st_mtime;
132         unsigned long   st_ctime;
133         unsigned int    st_blksize;
134         int             st_blocks;
135         unsigned int    st_flags;
136         unsigned int    st_gen;
137 };
138
139 #define COPY_STAT(src, dest)                    \
140 do {                                            \
141         memset((dest), 0, sizeof((*dest)));     \
142         (dest)->st_dev     = (src)->st_dev;     \
143         (dest)->st_ino     = (src)->st_ino;     \
144         (dest)->st_mode    = (src)->st_mode;    \
145         (dest)->st_nlink   = (src)->st_nlink;   \
146         (dest)->st_uid     = (src)->st_uid;     \
147         (dest)->st_gid     = (src)->st_gid;     \
148         (dest)->st_rdev    = (src)->st_rdev;    \
149         (dest)->st_size    = (src)->st_size;    \
150         (dest)->st_atime   = (src)->st_atime;   \
151         (dest)->st_mtime   = (src)->st_mtime;   \
152         (dest)->st_ctime   = (src)->st_ctime;   \
153         (dest)->st_blksize = (src)->st_blksize; \
154         (dest)->st_blocks  = (src)->st_blocks;  \
155         (dest)->st_flags   = (src)->st_flags;   \
156         (dest)->st_gen     = (src)->st_gen;     \
157 } while (0);
158
159 #else 
160 #define __native_stat intnl_stat
161 #define COPY_STAT(src, dest) *(dest) = *(src) 
162 #endif
163
164 #if defined(USE_NATIVE_STAT)
165 #define __SYS_STAT SYS_lstat
166 #define __SYS_FSTAT SYS_fstat
167 #define __SYS_TRUNCATE SYS_truncate
168 #define __SYS_FTRUNCATE SYS_ftruncate
169 #else
170 #define __SYS_STAT SYS_lstat64
171 #define __SYS_FSTAT SYS_fstat64
172 #define __SYS_TRUNCATE SYS_truncate64
173 #define __SYS_FTRUNCATE SYS_ftruncate64
174 #endif
175
176 #if defined(USE_NATIVE_FDATASYNC)
177 #define __SYS_FDATASYNC SYS_osf_fdatasync
178 #else
179 #define __SYS_FDATASYNC SYS_fdatasync
180 #endif
181
182 #if defined(USE_NATIVE_UTIME)
183 #define __SYS_UTIME SYS_utimes
184 #else
185 #define __SYS_UTIME SYS_utime
186 #endif
187
188 /*
189  * Native file identifiers format.
190  */
191 struct native_inode_identifier {
192         dev_t   dev;                                    /* device number */
193         ino_t   ino;                                    /* i-number */
194 #ifdef HAVE_GENERATION
195         unsigned int gen;                               /* generation number */
196 #endif
197 };
198
199 /*
200  * Driver-private i-node information we keep about local host file
201  * system objects.
202  */
203 struct native_inode {
204         unsigned
205                 ni_seekok               : 1;            /* can seek? */
206         struct native_inode_identifier ni_ident;        /* unique identifier */
207         struct file_identifier ni_fileid;               /* ditto */
208         int     ni_fd;                                  /* host fildes */
209         int     ni_oflags;                              /* flags, from open */
210         unsigned ni_nopens;                             /* soft ref count */
211         _SYSIO_OFF_T ni_fpos;                           /* current pos */
212 };
213
214 /*
215  * Native IO path arguments.
216  */
217 struct native_io {
218         char    nio_op;                                 /* 'r' or 'w' */
219         struct native_inode *nio_nino;                  /* native ino */
220 };
221
222 static int native_inop_lookup(struct pnode *pno,
223                               struct inode **inop,
224                               struct intent *intnt,
225                               const char *path);
226 static int native_inop_getattr(struct pnode *pno,
227                                struct inode *ino,
228                                struct intnl_stat *stbuf);
229 static int native_inop_setattr(struct pnode *pno,
230                                struct inode *ino,
231                                unsigned mask,
232                                struct intnl_stat *stbuf);
233 static ssize_t native_getdirentries(struct inode *ino,
234                                     char *buf,
235                                     size_t nbytes,
236                                     _SYSIO_OFF_T *basep);
237 static int native_inop_mkdir(struct pnode *pno, mode_t mode);
238 static int native_inop_rmdir(struct pnode *pno);
239 static int native_inop_symlink(struct pnode *pno, const char *data);
240 static int native_inop_readlink(struct pnode *pno, char *buf, size_t bufsiz);
241 static int native_inop_open(struct pnode *pno, int flags, mode_t mode);
242 static int native_inop_close(struct inode *ino);
243 static int native_inop_link(struct pnode *old, struct pnode *new);
244 static int native_inop_unlink(struct pnode *pno);
245 static int native_inop_rename(struct pnode *old, struct pnode *new);
246 static int native_inop_read(struct inode *ino, struct ioctx *ioctx);
247 static int native_inop_write(struct inode *ino, struct ioctx *ioctx);
248 static _SYSIO_OFF_T native_inop_pos(struct inode *ino, _SYSIO_OFF_T off);
249 static int native_inop_iodone(struct ioctx *ioctx);
250 static int native_inop_fcntl(struct inode *ino, int cmd, va_list ap);
251 static int native_inop_sync(struct inode *ino);
252 static int native_inop_datasync(struct inode *ino);
253 static int native_inop_ioctl(struct inode *ino,
254                              unsigned long int request,
255                              va_list ap);
256 static int native_inop_mknod(struct pnode *pno, mode_t mode, dev_t dev);
257 #ifdef _HAVE_STATVFS
258 static int native_inop_statvfs(struct pnode *pno,
259                                struct inode *ino,
260                                struct intnl_statvfs *buf);
261 #endif
262 static void native_inop_gone(struct inode *ino);
263
264 static struct inode_ops native_i_ops = {
265         native_inop_lookup,
266         native_inop_getattr,
267         native_inop_setattr,
268         native_getdirentries,
269         native_inop_mkdir,
270         native_inop_rmdir,
271         native_inop_symlink,
272         native_inop_readlink,
273         native_inop_open,
274         native_inop_close,
275         native_inop_link,
276         native_inop_unlink,
277         native_inop_rename,
278         native_inop_read,
279         native_inop_write,
280         native_inop_pos,
281         native_inop_iodone,
282         native_inop_fcntl,
283         native_inop_sync,
284         native_inop_datasync,
285         native_inop_ioctl,
286         native_inop_mknod,
287 #ifdef _HAVE_STATVFS
288         native_inop_statvfs,
289 #endif
290         native_inop_gone
291 };
292
293 static int native_fsswop_mount(const char *source,
294                                unsigned flags,
295                                const void *data,
296                                struct pnode *tocover,
297                                struct mount **mntp);
298
299 static struct fssw_ops native_fssw_ops = {
300         native_fsswop_mount
301 };
302
303 static void native_fsop_gone(struct filesys *fs);
304
305 static struct filesys_ops native_inodesys_ops = {
306         native_fsop_gone,
307 };
308
309 /*
310  * This example driver plays a strange game. It maintains a private,
311  * internal mount -- It's own separate, rooted, name space. The local
312  * file system's entire name space is available via this tree.
313  *
314  * This simplifies the implementation. At mount time, we need to generate
315  * a path-node to be used as a root. This allows us to look up the needed
316  * node in the host name space and leverage a whole lot of support from
317  * the system.
318  */
319 static struct mount *native_internal_mount = NULL;
320
321 /*
322  * Given i-node, return driver private part.
323  */
324 #define I2NI(ino)       ((struct native_inode *)((ino)->i_private))
325
326 /*
327  * stat -- by path.
328  */
329 static int
330 native_stat(const char *path, struct intnl_stat *buf)
331 {
332         int     err;
333         struct __native_stat stbuf;
334
335         err = syscall(__SYS_STAT, path, &stbuf);
336         if (err)
337                 err = -errno;
338         COPY_STAT(&stbuf, buf);
339
340         return err;
341 }
342
343 /*
344  * stat -- by fildes
345  */
346 static int
347 native_fstat(int fd, struct intnl_stat *buf)
348 {
349         int     err;
350         struct __native_stat stbuf;
351
352         err = syscall(__SYS_FSTAT, fd, &stbuf);
353         if (err)
354                 err = -errno;
355         COPY_STAT(&stbuf, buf);
356
357         return err;
358 }
359
360 /*
361  * Introduce an i-node to the system.
362  */
363 static struct inode *
364 native_i_new(struct filesys *fs, struct intnl_stat *buf)
365 {
366         struct native_inode *nino;
367         struct inode *ino;
368
369         nino = malloc(sizeof(struct native_inode));
370         if (!nino)
371                 return NULL;
372         bzero(&nino->ni_ident, sizeof(nino->ni_ident));
373         nino->ni_ident.dev = buf->st_dev;
374         nino->ni_ident.ino = buf->st_ino;
375 #ifdef HAVE_GENERATION
376         nino->ni_ident.gen = buf->st_gen;
377 #endif
378         nino->ni_fileid.fid_data = &nino->ni_ident;
379         nino->ni_fileid.fid_len = sizeof(nino->ni_ident);
380         nino->ni_fd = -1;
381         nino->ni_oflags = 0;
382         nino->ni_nopens = 0;
383         nino->ni_fpos = 0;
384         ino =
385             _sysio_i_new(fs,
386                          &nino->ni_fileid,
387 #ifndef AUTOMOUNT_FILE_NAME
388                          buf->st_mode & S_IFMT,
389 #else
390                          buf->st_mode,                  /* all of the bits! */
391 #endif
392                          0,
393                          0,
394                          &native_i_ops,
395                          nino);
396         if (!ino)
397                 free(nino);
398         return ino;
399 }
400
401 /*
402  * Initialize this driver.
403  */
404 int
405 _sysio_native_init()
406 {
407
408         /*
409          * Capture current process umask and reset our process umask to
410          * zero. All permission bits to open/creat/setattr are absolute --
411          * They've already had a umask applied, when appropriate.
412          */
413         _sysio_umask = syscall(SYS_umask, 0);
414
415         return _sysio_fssw_register("native", &native_fssw_ops);
416 }
417
418 /*
419  * Create private, internal, view of the hosts name space.
420  */
421 static int
422 create_internal_namespace()
423 {
424         int     err;
425         struct mount *mnt;
426         struct inode *rootino;
427         struct pnode_base *rootpb;
428         static struct qstr noname = { NULL, 0, 0 };
429         struct filesys *fs;
430         struct intnl_stat stbuf;
431
432         if (native_internal_mount) {
433                 /*
434                  * Reentered!
435                  */
436                 abort();
437         }
438
439         /*
440          * We maintain an artificial, internal, name space in order to
441          * have access to fully qualified path names in the various routines.
442          * Initialize that name space now.
443          */
444         mnt = NULL;
445         rootino = NULL;
446         rootpb = NULL;
447         fs = _sysio_fs_new(&native_inodesys_ops, 0, NULL);
448         if (!fs) {
449                 err = -ENOMEM;
450                 goto error;
451         }
452
453         /*
454          * Get root i-node.
455          */
456         err = native_stat("/", &stbuf);
457         if (err)
458                 goto error;
459         rootino = native_i_new(fs, &stbuf);
460         if (!rootino) {
461                 err = -ENOMEM;
462                 goto error;
463         }
464
465         /*
466          * Generate base path-node for root.
467          */
468         rootpb = _sysio_pb_new(&noname, NULL, rootino);
469         if (!rootpb) {
470                 err = -ENOMEM;
471                 goto error;
472         }
473
474         /*
475          * Mount it. This name space is disconnected from the
476          * rest of the system -- Only available within this driver.
477          */
478         err = _sysio_do_mount(fs, rootpb, 0, NULL, &mnt);
479         if (err)
480                 goto error;
481
482         native_internal_mount = mnt;
483         return 0;
484 error:
485         if (mnt) {
486                 if (_sysio_do_unmount(mnt) != 0)
487                         abort();
488                 fs = NULL;
489                 rootpb = NULL;
490                 rootino = NULL;
491         }
492         if (rootpb)
493                 _sysio_pb_gone(rootpb);
494         if (fs) {
495                 FS_RELE(fs);
496                 _sysio_fs_gone(fs);
497         }
498
499         return err;
500 }
501
502 static int
503 native_fsswop_mount(const char *source,
504                     unsigned flags,
505                     const void *data __IS_UNUSED,
506                     struct pnode *tocover,
507                     struct mount **mntp)
508 {
509         int     err;
510         struct nameidata nameidata;
511         struct mount *mnt;
512
513         /*
514          * Caller must use fully qualified path names when specifying
515          * the source.
516          */
517         if (*source != '/')
518                 return -ENOENT;
519
520         if (!native_internal_mount) {
521                 err = create_internal_namespace();
522                 if (err)
523                         return err;
524         }
525
526         /*
527          * Lookup the source in the internally maintained name space.
528          */
529         ND_INIT(&nameidata, 0, source, native_internal_mount->mnt_root, NULL);
530         err = _sysio_path_walk(native_internal_mount->mnt_root, &nameidata);
531         if (err)
532                 return err;
533
534         /*
535          * Have path-node specified by the given source argument. Let the
536          * system finish the job, now.
537          */
538         err =
539             _sysio_do_mount(native_internal_mount->mnt_fs,
540                             nameidata.nd_pno->p_base,
541                             flags,
542                             tocover,
543                             &mnt);
544         /*
545          * Release the internal name space pnode and clean up any
546          * aliases we might have generated. We really don't need to cache them
547          * as they are only used at mount time..
548          */
549         P_RELE(nameidata.nd_pno);
550         (void )_sysio_p_prune(native_internal_mount->mnt_root);
551
552         if (!err) {
553                 FS_REF(native_internal_mount->mnt_fs);
554                 *mntp = mnt;
555         }
556         return err;
557 }
558
559 static int
560 native_i_invalid(struct inode *inop, struct intnl_stat stbuf)
561 {
562         /*
563          * Validate passed in inode against stat struct info
564          */
565         struct native_inode *nino = I2NI(inop);
566         
567         if ((nino->ni_ident.dev != stbuf.st_dev ||
568              nino->ni_ident.ino != stbuf.st_ino ||
569 #ifdef HAVE_GENERATION
570              nino->ni_ident.gen != stbuf.st_gen ||
571 #endif
572              ((inop)->i_mode & S_IFMT) != (stbuf.st_mode & S_IFMT)) ||
573             (((inop)->i_rdev != stbuf.st_rdev) &&
574                (S_ISCHR((inop)->i_mode) || S_ISBLK((inop)->i_mode))))
575                 return 1;
576         
577         return 0;
578 }
579
580 /*
581  * Find, and validate, or create i-node by host-relative path. Returned i-node
582  * is referenced.
583  */
584 static int
585 native_iget(struct filesys *fs,
586             const char *path,
587             struct inode **inop,
588             int forced)
589 {
590         int     err;
591         struct inode *ino;
592         struct intnl_stat stbuf;
593         struct native_inode_identifier ident;
594         struct file_identifier fileid;
595
596         /*
597          * Get file status.
598          */
599         err = native_stat(path, &stbuf);
600         if (err) {
601                 *inop = NULL;
602                 return err;
603         }
604
605         /* 
606          * Validate?
607          */
608         if (*inop) {
609                 if (!native_i_invalid(*inop, stbuf))
610                         return 0;
611                 /*
612                  * Invalidate.
613                  */
614                 *inop = NULL;
615         }
616
617         /*
618          * I-node is not already known. Find or create it.
619          */
620         bzero(&ident, sizeof(ident)); 
621         ident.dev = stbuf.st_dev;
622         ident.ino = stbuf.st_ino;
623 #ifdef HAVE_GENERATION
624         ident.gen = stbuf.st_gen;
625 #endif
626         fileid.fid_data = &ident;
627         fileid.fid_len = sizeof(ident);
628         ino = _sysio_i_find(fs, &fileid);
629         if (ino && forced) {
630                 /*
631                  * Insertion was forced but it's already present!
632                  */
633                 if (native_i_invalid(ino, stbuf)) {
634                         /* 
635                          * Cached inode has stale attrs
636                          * make way for the new one
637                          */
638                         I_GONE(ino);
639                         ino = NULL;
640                 } else
641                         /* 
642                          * OK to reuse cached inode
643                          */
644                         goto out;
645         }
646
647         if (!ino) {
648                 ino = native_i_new(fs, &stbuf);
649                 if (!ino)
650                         err = -ENOMEM;
651         }
652 out:
653         if (!err)
654                 *inop = ino;
655         return err;
656 }
657
658 /*
659  * Look up named object in host's name space by path.
660  */
661 static int
662 native_path_lookup(struct filesys *fs, const char *path, struct inode **inop)
663 {
664
665         return native_iget(fs, path, inop, 0);
666 }
667
668 /*
669  * Look up object by it's path node.
670  */
671 static int
672 native_i_lookup(struct filesys *fs, struct pnode_base *pb, struct inode **inop)
673 {
674         int     err;
675         char    *path;
676
677         path = _sysio_pb_path(pb, '/');
678         if (!path)
679                 return -ENOMEM;
680         err = native_path_lookup(fs, path, inop);
681         free(path);
682         return err;
683 }
684
685 static int
686 native_inop_lookup(struct pnode *pno,
687                    struct inode **inop,
688                    struct intent *intnt __IS_UNUSED,
689                    const char *path __IS_UNUSED)
690 {
691         int     err;
692
693         *inop = pno->p_base->pb_ino;
694
695         /*
696          * Don't have an inode yet. Because we translate everything back to
697          * a single name space for the host, we will assume the object the
698          * caller is looking for has no existing alias in our internal
699          * name space. We don't see the same file on different mounts in the
700          * underlying host FS as the same file.
701          *
702          * The file identifier *will* be unique. It's got to have a different
703          * dev.
704          */
705         err = native_i_lookup(pno->p_mount->mnt_fs, pno->p_base, inop);
706         if (err)
707                 *inop = NULL;
708         return err;
709 }
710
711 static int
712 native_inop_getattr(struct pnode *pno, struct inode *ino, struct intnl_stat *stbuf)
713 {
714         char    *path;
715         int     err;
716
717         path = NULL;
718         if (!ino || I2NI(ino)->ni_fd < 0) {
719                 path = _sysio_pb_path(pno->p_base, '/');
720                 if (!path)
721                         return -ENOMEM;
722         }
723         err =
724             path
725               ? native_stat(path, stbuf)
726               : native_fstat(I2NI(ino)->ni_fd, stbuf);
727         if (path)
728                 free(path);
729         return err;
730 }
731
732 static int
733 native_inop_setattr(struct pnode *pno,
734                     struct inode *ino,
735                     unsigned mask,
736                     struct intnl_stat *stbuf)
737 {
738         char    *path;
739         int     fd;
740         struct intnl_stat st;
741         int     err;
742
743         path = NULL;
744         fd = ino ? I2NI(ino)->ni_fd : -1;
745         if (fd < 0 || mask & (SETATTR_MTIME|SETATTR_ATIME)) {
746                 if (!pno)
747                         return -EEXIST;
748                 path = _sysio_pb_path(pno->p_base, '/');
749                 if (!path)
750                         return -ENOMEM;
751         }
752
753         /*
754          * Get current status for undo.
755          */
756         err =
757             fd < 0
758               ? native_stat(path, &st)
759               : native_fstat(fd, &st);
760         if (err)
761                 goto out;
762
763         if (mask & SETATTR_MODE) {
764                 mode_t  mode;
765
766                 /*
767                  * Alter permissions attribute.
768                  */
769                 mode = stbuf->st_mode & 07777;
770                 err =
771                     fd < 0
772                       ? syscall(SYS_chmod, path, mode)
773                       : syscall(SYS_fchmod, fd, mode);
774                 if (err)
775                         err = -errno;
776         }
777         if (err)
778                 mask &= ~SETATTR_MODE;
779         else if (mask & (SETATTR_MTIME|SETATTR_ATIME)) {
780                 struct utimbuf ut;
781
782                 /*
783                  * Alter access and/or modify time attributes.
784                  */
785                 ut.actime = st.st_atime;
786                 ut.modtime = st.st_mtime;
787                 if (mask & SETATTR_MTIME)
788                         ut.modtime = stbuf->st_mtime;
789                 if (mask & SETATTR_ATIME)
790                         ut.actime = stbuf->st_atime;
791                 err = syscall(__SYS_UTIME, path, &ut);
792                 if (err)
793                         err = -errno;
794         }
795         if (err)
796                 mask &= ~(SETATTR_MTIME|SETATTR_ATIME);
797         else if (mask & (SETATTR_UID|SETATTR_GID)) {
798
799                 /*
800                  * Alter owner and/or group identifiers.
801                  */
802                 err =
803                     fd < 0
804                       ? syscall(SYS_chown,
805                                 path,
806                                 mask & SETATTR_UID
807                                   ? stbuf->st_uid
808                                   : (uid_t )-1,
809                                 mask & SETATTR_GID
810                                   ? stbuf->st_gid
811                                   : (gid_t )-1)
812                       : syscall(SYS_fchown,
813                                 fd,
814                                 mask & SETATTR_UID
815                                   ? stbuf->st_uid
816                                   : (uid_t )-1,
817                                 mask & SETATTR_GID
818                                   ? stbuf->st_gid
819                                   : (gid_t )-1);
820                 if (err)
821                         err = -errno;
822         }
823         if (err)
824                 mask &= ~(SETATTR_UID|SETATTR_GID);
825         else if (mask & SETATTR_LEN) {
826                 /*
827                  * Do the truncate last. It can't be undone.
828                  */
829                  (void )(fd < 0
830                            ? syscall(__SYS_TRUNCATE, path, stbuf->st_size)
831                            : syscall(__SYS_FTRUNCATE, fd, stbuf->st_size));
832         }
833         if (!err)
834                 goto out;
835         /*
836          * Undo after error. Some or all of this might not work... We
837          * can but try.
838          */
839         if (mask & (SETATTR_UID|SETATTR_GID)) {
840                  (void )(fd < 0
841                            ? syscall(SYS_chown,
842                                      path,
843                                      mask & SETATTR_UID
844                                        ? st.st_uid
845                                        : (uid_t )-1,
846                                      mask & SETATTR_GID
847                                        ? st.st_gid
848                                        : (gid_t )-1)
849                            : syscall(SYS_fchown,
850                                      fd,
851                                      mask & SETATTR_UID
852                                        ? st.st_uid
853                                        : (uid_t )-1,
854                                      mask & SETATTR_GID
855                                        ? st.st_gid
856                                        : (gid_t )-1));
857         }
858         if (mask & (SETATTR_MTIME|SETATTR_ATIME)) {
859                 struct utimbuf ut;
860
861                 ut.actime = st.st_atime;
862                 ut.modtime = st.st_mtime;
863                 (void )syscall(__SYS_UTIME, path, &ut);
864         }
865         if (mask & SETATTR_MODE) {
866                 fd < 0
867                   ? syscall(SYS_chmod, path, st.st_mode & 07777)
868                   : syscall(SYS_fchmod, fd, st.st_mode & 07777);
869         }
870 out:
871         if (path)
872                 free(path);
873         return err;
874 }
875
876 static int
877 native_pos(int fd, _SYSIO_OFF_T *offset, int whence)
878 {
879         _SYSIO_OFF_T off;
880
881         assert(fd >= 0);
882         assert(*offset >= 0);
883
884         off = *offset;
885 #if _LARGEFILE64_SOURCE && defined(SYS__llseek)
886         {
887                 int     err;
888                 err =
889                     syscall(SYS__llseek,
890                             (unsigned int)fd,
891                             (unsigned int)(off >> 32),
892                             (unsigned int)off,
893                             &off,
894                             whence);
895                 if (err == -1)
896                         return -errno;
897         }
898 #else
899         off =
900             syscall(SYS_lseek,
901                     fd,
902                     off,
903                     whence);
904         if (off == -1)
905                 return -errno;
906 #endif
907         *offset = off;
908
909         return 0;
910 }
911
912 static ssize_t
913 native_filldirentries(struct native_inode *nino,
914                       char *buf,
915                       size_t nbytes,
916                       _SYSIO_OFF_T *basep)
917 {
918         int     err;
919         ssize_t cc;
920
921         if (*basep < 0)
922                 return -EINVAL;
923
924 #if DIR_STREAMED
925         /*
926          * Stream-oriented access requires that we reposition prior to the
927          * fill call.
928          */
929         if ((err = native_pos(nino->ni_fd, basep, SEEK_SET)) != 0)
930                 return err;
931 #endif
932         nino->ni_fpos = *basep;
933
934         cc =
935 #if defined(SYS_getdirentries)
936             syscall(SYS_getdirentries,
937                     nino->ni_fd,
938                     buf,
939                     nbytes,
940                     basep);
941 #elif defined(SYS_getdents64)
942             syscall(SYS_getdents64, nino->ni_fd, buf, nbytes);
943 #elif defined(SYS_getdents)
944             syscall(SYS_getdents, nino->ni_fd, buf, nbytes);
945 #endif
946
947         if (cc < 0)
948                 return -errno;
949 #if DIR_STREAMED
950         /*
951          * Stream-oriented access requires that we discover where we are
952          * after the call.
953          */
954         *basep = 0;
955         if ((err = native_pos(nino->ni_fd, basep, SEEK_CUR)) != 0)
956                 return err;
957 #endif
958         nino->ni_fpos = *basep;
959         return cc;
960 }
961
962 static ssize_t
963 native_getdirentries(struct inode *ino,
964                      char *buf,
965                      size_t nbytes,
966                      _SYSIO_OFF_T *basep)
967 {
968         struct native_inode *nino = I2NI(ino);
969 #if DIR_CVT_64
970         char    *bp;
971         size_t  count;
972         struct linux_dirent *ldp;
973         struct dirent64 *d64p;
974         size_t  namlen;
975         size_t  reclen;
976 #else
977 #define bp buf
978 #define count nbytes
979 #endif
980         ssize_t cc;
981
982         assert(nino->ni_fd >= 0);
983
984 #if DIR_CVT_64
985         count = nbytes;
986         while (!(bp = malloc(count))) {
987                 count /= 2;
988                 if (count < sizeof(struct dirent))
989                         return -ENOMEM;
990         }
991 #endif
992         cc = native_filldirentries(nino, bp, count, basep);
993         if (cc < 0) {
994 #if DIR_CVT_64
995                 free(bp);
996 #endif
997                 return cc;
998         }
999 #if DIR_CVT_64
1000         ldp = (struct linux_dirent *)bp;
1001         d64p = (struct dirent64 *)buf;
1002         for (;;) {
1003                 if (cc < 0 || (size_t )cc <= sizeof(*ldp))
1004                         break;
1005                 namlen = strlen(ldp->ld_name);
1006                 reclen = sizeof(*d64p) - sizeof(d64p->d_name) + namlen + 1;
1007                 if (nbytes < reclen)
1008                         break;
1009                 d64p->d_ino = ldp->ld_ino;
1010                 d64p->d_off = ldp->ld_off;
1011                 d64p->d_reclen = 
1012                     (((reclen + sizeof(long) - 1)) / sizeof(long)) *
1013                     sizeof(long);
1014                 if (nbytes < d64p->d_reclen)
1015                         d64p->d_reclen = reclen;
1016                 d64p->d_type = DT_UNKNOWN;              /* you lose -- sorry. */
1017                 (void )strncpy(d64p->d_name, ldp->ld_name, namlen);
1018                 *(d64p->d_name + namlen) = '\0';
1019                 cc -= ldp->ld_reclen;
1020                 ldp = (struct linux_dirent *)((char *)ldp + ldp->ld_reclen);
1021                 nbytes -= d64p->d_reclen;
1022                 d64p = (struct dirent64 *)((char *)d64p + d64p->d_reclen);
1023         }
1024         free(bp);
1025         if (d64p == (struct dirent64 *)buf && cc)
1026                 cc = -EINVAL;                           /* buf too small */
1027         cc = (char *)d64p - buf;
1028 #else
1029 #undef bp
1030 #undef count
1031 #endif
1032         return cc;
1033 }
1034
1035 static int
1036 native_inop_mkdir(struct pnode *pno, mode_t mode)
1037 {
1038         char    *path;
1039         int     err;
1040
1041         path = _sysio_pb_path(pno->p_base, '/');
1042         if (!path)
1043                 return -ENOMEM;
1044
1045         err = syscall(SYS_mkdir, path, mode);
1046         if (err != 0)
1047                 err = -errno;
1048         free(path);
1049         return err;
1050 }
1051
1052 static int
1053 native_inop_rmdir(struct pnode *pno)
1054 {
1055         char    *path;
1056         int     err;
1057
1058         path = _sysio_pb_path(pno->p_base, '/');
1059         if (!path)
1060                 return -ENOMEM;
1061
1062         err = syscall(SYS_rmdir, path);
1063         if (err != 0)
1064                 err = -errno;
1065         free(path);
1066         return err;
1067 }
1068
1069 static int
1070 native_inop_symlink(struct pnode *pno, const char *data)
1071 {
1072         char    *path;
1073         int     err;
1074
1075         path = _sysio_pb_path(pno->p_base, '/');
1076         if (!path)
1077                 return -ENOMEM;
1078
1079         err = syscall(SYS_symlink, data, path);
1080         if (err != 0)
1081                 err = -errno;
1082         free(path);
1083         return err;
1084 }
1085
1086 static int
1087 native_inop_readlink(struct pnode *pno, char *buf, size_t bufsiz)
1088 {
1089         char    *path;
1090         int     i;
1091
1092         path = _sysio_pb_path(pno->p_base, '/');
1093         if (!path)
1094                 return -ENOMEM;
1095         i = syscall(SYS_readlink, path, buf, bufsiz);
1096         if (i < 0)
1097                 i = -errno;
1098         free(path);
1099         return i;
1100 }
1101
1102 static int 
1103 native_inop_open(struct pnode *pno, int flags, mode_t mode)
1104 {
1105         struct native_inode *nino;
1106         char    *path;
1107         int     fd;
1108
1109         path = _sysio_pb_path(pno->p_base, '/');
1110         if (!path)
1111                 return -ENOMEM;
1112
1113         /*
1114          * Whether the file is already open, or not, makes no difference.
1115          * Want to always give the host OS a chance to authorize in case
1116          * something has changed underneath us.
1117          */
1118         if (flags & O_WRONLY) {
1119                 /*
1120                  * Promote write-only attempt to RW.
1121                  */
1122                 flags &= ~O_WRONLY;
1123                 flags |= O_RDWR;
1124         }
1125 #ifdef O_LARGEFILE
1126         flags |= O_LARGEFILE;
1127 #endif
1128         fd = syscall(SYS_open, path, flags, mode);
1129         if (!pno->p_base->pb_ino && fd >= 0) {
1130                 int     err;
1131
1132                 /*
1133                  * Success but we need to return an i-node.
1134                  */
1135                 err =
1136                     native_iget(pno->p_mount->mnt_fs,
1137                                 path,
1138                                 &pno->p_base->pb_ino,
1139                                 1);
1140                 if (err) {
1141                         (void )syscall(SYS_close, fd);
1142                         if (err == -EEXIST)
1143                                 abort();
1144                         fd = err;
1145                 }
1146         }
1147         free(path);
1148         if (fd < 0)
1149                 return -errno;
1150
1151         /*
1152          * Remember this new open.
1153          */
1154         nino = I2NI(pno->p_base->pb_ino);
1155         nino->ni_nopens++;
1156         assert(nino->ni_nopens);
1157
1158         if (nino->ni_fd >= 0) {
1159                 if ((nino->ni_oflags & O_RDWR) ||
1160                     (flags & (O_RDONLY|O_WRONLY|O_RDWR)) == O_RDONLY) {
1161                         /*
1162                          * Keep existing.
1163                          */
1164                         (void )syscall(SYS_close, fd);
1165                         return 0;
1166                 }
1167                 (void )syscall(SYS_close, nino->ni_fd);
1168         }
1169         /*
1170          * Invariant; First open. Must init.
1171          */
1172         nino->ni_fpos = 0;
1173         nino->ni_fd = fd;
1174         /*
1175          * Need to know whether we can seek on this
1176          * descriptor.
1177          */
1178         nino->ni_seekok =
1179             native_pos(nino->ni_fd, &nino->ni_fpos, SEEK_CUR) != 0 ? 0 : 1;
1180
1181         return 0;
1182 }
1183
1184 static int
1185 native_inop_close(struct inode *ino)
1186 {
1187         struct native_inode *nino = I2NI(ino);
1188         int     err;
1189
1190         if (nino->ni_fd < 0)
1191                 abort();
1192
1193         assert(nino->ni_nopens);
1194         if (--nino->ni_nopens) {
1195                 /*
1196                  * Hmmm. We really don't need anything else. However, some
1197                  * filesystems try to implement a sync-on-close semantic.
1198                  * As this appears now, that is lost. Might want to change
1199                  * it somehow in the future?
1200                  */
1201                 return 0;
1202         }
1203
1204         err = syscall(SYS_close, nino->ni_fd);
1205         if (err)
1206                 return -errno;
1207
1208         nino->ni_fd = -1;
1209         nino->ni_fpos = 0;
1210         return 0;
1211 }
1212
1213 static int
1214 native_inop_link(struct pnode *old, struct pnode *new)
1215 {
1216         int     err;
1217         char    *opath, *npath;
1218
1219         err = 0;
1220
1221         opath = _sysio_pb_path(old->p_base, '/');
1222         npath = _sysio_pb_path(new->p_base, '/');
1223         if (!(opath && npath)) {
1224                 err = -ENOMEM;
1225                 goto out;
1226         }
1227
1228         err = syscall(SYS_link, opath, npath);
1229         if (err != 0)
1230                 err = -errno;
1231
1232 out:
1233         if (opath)
1234                 free(opath);
1235         if (npath)
1236                 free(npath);
1237
1238         return err;
1239 }
1240
1241 static int
1242 native_inop_unlink(struct pnode *pno)
1243 {
1244         char    *path;
1245         int     err = 0;
1246
1247         path = _sysio_pb_path(pno->p_base, '/');
1248         if (!path)
1249                 return -ENOMEM;
1250
1251         /*
1252          * For this driver, unlink is easy with open files. Since the
1253          * file remains open to the system, too, the descriptors are still
1254          * valid.
1255          *
1256          * Other drivers will have some difficulty here as the entry in the
1257          * file system name space must be removed without sacrificing access
1258          * to the file itself. In NFS this is done with a mechanism referred
1259          * to as a `silly delete'. The file is moved to a temporary name
1260          * (usually .NFSXXXXXX, where the X's are replaced by the PID and some
1261          * unique characters) in order to simulate the proper semantic.
1262          */
1263         if (syscall(SYS_unlink, path) != 0)
1264                 err = -errno;
1265         free(path);
1266         return err;
1267 }
1268
1269 static int
1270 native_inop_rename(struct pnode *old, struct pnode *new)
1271 {
1272         int     err;
1273         char    *opath, *npath;
1274
1275         opath = _sysio_pb_path(old->p_base, '/');
1276         npath = _sysio_pb_path(new->p_base, '/');
1277         if (!(opath && npath)) {
1278                 err = -ENOMEM;
1279                 goto out;
1280         }
1281
1282         err = syscall(SYS_rename, opath, npath);
1283         if (err != 0)
1284                 err = -errno;
1285
1286 out:
1287         if (opath)
1288                 free(opath);
1289         if (npath)
1290                 free(npath);
1291
1292         return err;
1293 }
1294
1295 static ssize_t
1296 dopio(void *buf, size_t count, _SYSIO_OFF_T off, struct native_io *nio)
1297 {
1298 #if defined(_LARGEFILE64_SOURCE) && \
1299     defined(SYS_pread64) && \
1300     defined(SYS_pwrite64)
1301 #define _NATIVE_SYSCALL_PREAD SYS_pread64
1302 #define _NATIVE_SYSCALL_PWRITE SYS_pwrite64
1303 #else
1304 #define _NATIVE_SYSCALL_PREAD SYS_pread
1305 #define _NATIVE_SYSCALL_PWRITE SYS_pwrite
1306 #endif
1307         ssize_t cc;
1308
1309         if (!(off == nio->nio_nino->ni_fpos || nio->nio_nino->ni_seekok))
1310                 return -ESPIPE;
1311                 
1312         if (!nio->nio_nino->ni_seekok) {
1313                 if (off != nio->nio_nino->ni_fpos) {
1314                         /*
1315                          * They've done a p{read,write} or somesuch. Can't
1316                          * seek on this descriptor so we err out now.
1317                          */
1318                         errno = ESPIPE;
1319                         return -1;
1320                 }
1321                 cc =
1322                     syscall(nio->nio_op == 'r' ? SYS_read : SYS_write,
1323                             nio->nio_nino->ni_fd,
1324                             buf,
1325                             count);
1326                 if (cc > 0)
1327                         nio->nio_nino->ni_fpos += cc;
1328         } else
1329                 cc =
1330                     syscall((nio->nio_op == 'r'
1331                                ? _NATIVE_SYSCALL_PREAD
1332                                : _NATIVE_SYSCALL_PWRITE),
1333                             nio->nio_nino->ni_fd,
1334                             buf,
1335                             count,
1336                             off);
1337
1338         return cc;
1339 #undef _NATIVE_SYSCALL_PREAD
1340 #undef _NATIVE_SYSCALL_PWRITE
1341 }
1342
1343 static ssize_t
1344 doiov(const struct iovec *iov,
1345       int count,
1346       _SYSIO_OFF_T off,
1347       ssize_t limit,
1348       struct native_io *nio)
1349 {
1350         ssize_t cc;
1351
1352 #if !(defined(REDSTORM) || defined(MAX_IOVEC))
1353 #define MAX_IOVEC       INT_MAX
1354 #endif
1355
1356         if (count <= 0)
1357                 return -EINVAL;
1358
1359         /*
1360          * Avoid the reposition call if we're already at the right place.
1361          * Allows us to access pipes and fifos.
1362          */
1363         if (off != nio->nio_nino->ni_fpos) {
1364                 int     err;
1365
1366                 err = native_pos(nio->nio_nino->ni_fd, &off, SEEK_SET);
1367                 if (err)
1368                         return err;
1369                 nio->nio_nino->ni_fpos = off;
1370         }
1371
1372         /*
1373          * The {read,write}v is safe as this routine is only ever called
1374          * by _sysio_enumerate_extents() and that routine is exact. It never
1375          * passes iovectors including tails.
1376          */
1377         cc =
1378 #ifndef REDSTORM
1379             count <= MAX_IOVEC
1380               ? syscall(nio->nio_op == 'r' ? SYS_readv : SYS_writev,
1381                         nio->nio_nino->ni_fd,
1382                         iov,
1383                         count)
1384               :
1385 #endif
1386                 _sysio_enumerate_iovec(iov,
1387                                        count,
1388                                        off,
1389                                        limit,
1390                                        (ssize_t (*)(void *,
1391                                                     size_t,
1392                                                     _SYSIO_OFF_T,
1393                                                     void *))dopio,
1394                                        nio);
1395         if (cc < 0)
1396                 cc = -errno;
1397         else
1398                 nio->nio_nino->ni_fpos += cc;
1399         return cc;
1400
1401 #if !(defined(REDSTORM) || defined(MAX_IOVEC))
1402 #undef MAX_IOVEC
1403 #endif
1404 }
1405
1406 #if 0
1407 static int
1408 lockop_all(struct native_inode *nino,
1409            struct intnl_xtvec *xtv,
1410            size_t count,
1411            short op)
1412 {
1413         struct flock flock;
1414         int     err;
1415
1416         if (!count)
1417                 return -EINVAL;
1418         flock.l_type = op;
1419         flock.l_whence = SEEK_SET;
1420         while (count--) {
1421                 flock.l_start = xtv->xtv_off;
1422                 flock.l_len = xtv->xtv_len;
1423                 xtv++;
1424                 err =
1425                     syscall(
1426 #if !_LARGEFILE64_SOURCE
1427                             SYS_fcntl64
1428 #else
1429                             SYS_fcntl
1430 #endif
1431                             ,
1432                             nino->ni_fd,
1433                             F_SETLK,
1434                             &flock);
1435                 if (err != 0)
1436                         return -errno;
1437         }
1438         return 0;
1439 }
1440
1441 static int
1442 order_xtv(const struct intnl_xtvec *xtv1, const struct intnl_xtvec *xtv2)
1443 {
1444
1445         if (xtv1->xtv_off < xtv2->xtv_off)
1446                 return -1;
1447         if (xtv1->xtv_off > xtv2->xtv_off)
1448                 return 1;
1449         return 0;
1450 }
1451 #endif
1452
1453 static int
1454 doio(char op, struct ioctx *ioctx)
1455 {
1456         struct native_inode *nino;
1457 #if 0
1458         int     dolocks;
1459         struct intnl_xtvec *oxtv;
1460         int     err;
1461 #endif
1462         struct native_io arguments;
1463         ssize_t cc;
1464 #if 0
1465         struct intnl_xtvec *front, *rear, tmp;
1466 #endif
1467
1468         nino = I2NI(ioctx->ioctx_ino);
1469 #if 0
1470         dolocks = ioctx->ioctx_xtvlen > 1 && nino->ni_seekok;
1471         if (dolocks) {
1472                 /*
1473                  * Must lock the regions (in order!) since we can't do
1474                  * strided-IO as a single atomic operation.
1475                  */
1476                 oxtv = malloc(ioctx->ioctx_xtvlen * sizeof(struct intnl_xtvec));
1477                 if (!oxtv)
1478                         return -ENOMEM;
1479                 (void )memcpy(oxtv,
1480                               ioctx->ioctx_xtv, 
1481                               ioctx->ioctx_xtvlen * sizeof(struct intnl_xtvec));
1482                 qsort(oxtv,
1483                       ioctx->ioctx_xtvlen,
1484                       sizeof(struct intnl_xtvec),
1485                       (int (*)(const void *, const void *))order_xtv);
1486                 err =
1487                     lockop_all(nino,
1488                                oxtv, ioctx->ioctx_xtvlen,
1489                                op == 'r' ? F_RDLCK : F_WRLCK);
1490                 if (err) {
1491                         free(oxtv);
1492                         return err;
1493                 }
1494         }
1495 #endif
1496         arguments.nio_op = op;
1497         arguments.nio_nino = nino;
1498         cc =
1499             _sysio_enumerate_extents(ioctx->ioctx_xtv, ioctx->ioctx_xtvlen, 
1500                                      ioctx->ioctx_iov, ioctx->ioctx_iovlen,
1501                                      (ssize_t (*)(const struct iovec *,
1502                                                   int,
1503                                                   _SYSIO_OFF_T,
1504                                                   ssize_t,
1505                                                   void *))doiov,
1506                                      &arguments);
1507 #if 0
1508         if (dolocks) {
1509                 /*
1510                  * Must unlock in reverse order.
1511                  */
1512                 front = oxtv;
1513                 rear = front + ioctx->ioctx_xtvlen - 1;
1514                 while (front < rear) {
1515                         tmp = *front;
1516                         *front++ = *rear;
1517                         *rear-- = tmp;
1518                 }
1519                 if (lockop_all(nino, oxtv, ioctx->ioctx_xtvlen, F_UNLCK) != 0)
1520                         abort();
1521                 free(oxtv);
1522         }
1523 #endif
1524         if ((ioctx->ioctx_cc = cc) < 0) {
1525                 ioctx->ioctx_errno = -ioctx->ioctx_cc;
1526                 ioctx->ioctx_cc = -1;
1527         }
1528         return 0;
1529 }
1530
1531 static int
1532 native_inop_read(struct inode *ino __IS_UNUSED, struct ioctx *ioctx)
1533 {
1534
1535         return doio('r', ioctx);
1536 }
1537
1538 static int
1539 native_inop_write(struct inode *ino __IS_UNUSED, struct ioctx *ioctx)
1540 {
1541
1542         return doio('w', ioctx);
1543 }
1544
1545 static _SYSIO_OFF_T
1546 native_inop_pos(struct inode *ino, _SYSIO_OFF_T off)
1547 {
1548         struct native_inode *nino = I2NI(ino);
1549         int     err;
1550
1551         err = native_pos(nino->ni_fd, &off, SEEK_SET);
1552         return err < 0 ? err : off;
1553 }
1554
1555 static int
1556 native_inop_iodone(struct ioctx *ioctxp __IS_UNUSED)
1557 {
1558
1559         /*
1560          * It's always done in this driver. It completed when posted.
1561          */
1562         return 1;
1563 }
1564
1565 static int
1566 native_inop_fcntl(struct inode *ino,
1567                   int cmd,
1568                   va_list ap)
1569 {
1570         struct native_inode *nino = I2NI(ino);
1571         long    arg;
1572         int     err;
1573
1574         if (nino->ni_fd < 0)
1575                 abort();
1576
1577         switch (cmd) {
1578         case F_GETFD:
1579         case F_GETFL:
1580         case F_GETOWN:
1581                 err = syscall(SYS_fcntl, nino->ni_fd, cmd);
1582                 if (err < 0)
1583                         err = -errno;
1584         case F_DUPFD:
1585         case F_SETFD:
1586         case F_SETFL:
1587         case F_GETLK:
1588         case F_SETLK:
1589         case F_SETLKW:
1590         case F_SETOWN:
1591                 arg = va_arg(ap, long);
1592                 err = syscall(SYS_fcntl, nino->ni_fd, cmd, arg);
1593                 if (err)
1594                         err = -errno;
1595         default:
1596                 err = -EINVAL;
1597         }
1598         return err;
1599 }
1600
1601 static int
1602 native_inop_mknod(struct pnode *pno __IS_UNUSED,
1603                   mode_t mode __IS_UNUSED,
1604                   dev_t dev __IS_UNUSED)
1605 {
1606
1607         return -ENOSYS;
1608 }
1609
1610 #ifdef _HAVE_STATVFS
1611 static int
1612 native_inop_statvfs(struct pnode *pno,
1613                     struct inode *ino,
1614                     struct intnl_statvfs *buf)
1615 {
1616         char    *path;
1617         int    rc;
1618         struct statfs fs;
1619
1620         path = NULL;
1621         if (!ino || I2NI(ino)->ni_fd < 0) {
1622                 path = _sysio_pb_path(pno->p_base, '/');
1623                 if (!path)
1624                         return -ENOMEM;
1625         }
1626
1627         /*
1628          * The syscall interface does not support SYS_fstatvfs.
1629          * Should possibly return ENOSYS, but thought it
1630          * better to use SYS_fstatfs and fill in as much of
1631          * the statvfs structure as possible.  This allows
1632          * for more of a test of the sysio user interface.
1633          */
1634         rc =
1635             path
1636               ? syscall(SYS_statfs, path, &fs)
1637               : syscall(SYS_fstatfs, I2NI(ino)->ni_fd, &fs);
1638         if (path)
1639                 free(path);
1640         if (rc < 0)
1641                 return -errno;
1642
1643         buf->f_bsize = fs.f_bsize;  /* file system block size */
1644         buf->f_frsize = fs.f_bsize; /* file system fundamental block size */
1645         buf->f_blocks = fs.f_blocks;
1646         buf->f_bfree = fs.f_bfree;
1647         buf->f_bavail = fs.f_bavail;
1648         buf->f_files = fs.f_files;  /* Total number serial numbers */
1649         buf->f_ffree = fs.f_ffree;  /* Number free serial numbers */
1650         buf->f_favail = fs.f_ffree; /* Number free ser num for non-privileged*/
1651         buf->f_fsid = fs.f_fsid.__val[1];
1652         buf->f_flag = 0;            /* No equiv in statfs; maybe use type? */
1653         buf->f_namemax = fs.f_namelen;
1654         return 0;
1655 }
1656 #endif
1657
1658 static int
1659 native_inop_sync(struct inode *ino)
1660 {
1661         int     err;
1662
1663         assert(I2NI(ino)->ni_fd >= 0);
1664
1665         err = syscall(SYS_fsync, I2NI(ino)->ni_fd);
1666         if (err)
1667                 err = -errno;
1668         return err;
1669 }
1670
1671 static int
1672 native_inop_datasync(struct inode *ino)
1673 {
1674         int     err;
1675
1676         assert(I2NI(ino)->ni_fd >= 0);
1677
1678 #ifdef NATIVE_FDATASYNC
1679         err = syscall(NATIVE_FDATASYNC, I2NI(ino)->ni_fd);
1680 #else
1681 #if 0
1682 #warning No fdatasync system call -- Using fsync instead!
1683 #endif
1684         err = syscall(SYS_fsync, I2NI(ino)->ni_fd);
1685 #endif
1686         if (err)
1687                 err = -errno;
1688         return err;
1689 }
1690
1691 static int
1692 native_inop_ioctl(struct inode *ino __IS_UNUSED,
1693                   unsigned long int request __IS_UNUSED,
1694                   va_list ap __IS_UNUSED)
1695 {
1696
1697         /*
1698          * I'm lazy. Maybe implemented later.
1699          */
1700         errno = ENOTTY;
1701         return -1;
1702 }
1703
1704 static void
1705 native_inop_gone(struct inode *ino)
1706 {
1707         struct native_inode *nino = I2NI(ino);
1708
1709         if (nino->ni_fd >= 0)
1710                 (void )syscall(SYS_close, nino->ni_fd);
1711         free(ino->i_private);
1712 }
1713
1714 static void
1715 native_fsop_gone(struct filesys *fs __IS_UNUSED)
1716 {
1717
1718         /*
1719          * Do nothing. There is no private part maintained for the
1720          * native file interface.
1721          */
1722 }