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