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