Whamcloud - gitweb
Land b_release_1_4_6 onto HEAD (20060223_1455)
[fs/lustre-release.git] / libsysio / drivers / yod / fs_yod.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-2003 Sandia Corporation. 
13  *    Under the terms of Contract DE-AC04-94AL85000, there is a non-exclusive
14  *    license for use of this work by or on behalf of the US Government.
15  *    Export of this program may require a license from the United States
16  *    Government.
17  */
18
19 /*
20  * This library is free software; you can redistribute it and/or
21  * modify it under the terms of the GNU Lesser General Public
22  * License as published by the Free Software Foundation; either
23  * version 2.1 of the License, or (at your option) any later version.
24  * 
25  * This library is distributed in the hope that it will be useful,
26  * but WITHOUT ANY WARRANTY; without even the implied warranty of
27  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
28  * Lesser General Public License for more details.
29  * 
30  * You should have received a copy of the GNU Lesser General Public
31  * License along with this library; if not, write to the Free Software
32  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
33  *
34  * Questions or comments about this library should be sent to:
35  *
36  * Lee Ward
37  * Sandia National Laboratories, New Mexico
38  * P.O. Box 5800
39  * Albuquerque, NM 87185-1110
40  *
41  * lee@sandia.gov
42  */
43
44 #ifdef __linux__
45 #define _BSD_SOURCE
46 #endif
47
48 #include <stdio.h>                                      /* for NULL */
49 #include <stdlib.h>
50 #ifdef __linux__
51 #include <string.h>
52 #endif
53 #include <unistd.h>
54 #include <errno.h>
55 #include <assert.h>
56 #include <syscall.h>
57 #include <sys/types.h>
58 #include <sys/stat.h>
59 #include <sys/fcntl.h>
60 #if 0
61 #include <sys/vfs.h>
62 #endif
63 #ifdef _HAVE_STATVFS
64 #include <sys/statvfs.h>
65 #endif
66 #include <utime.h>
67 #include <sys/queue.h>
68
69 #include "xtio.h"
70 #include "sysio.h"
71 #include "fs.h"
72 #include "mount.h"
73 #include "inode.h"
74
75 #include "fs_yod.h"
76
77 /*
78  * Remote file system driver
79  * calls are re-directed to the initiating yod
80  */
81 #include "cplant-yod.h"
82
83 /* stat struct used by yod, which
84  * is not compiled with __USE_FILE_OFFSET64
85  */
86 #define __yod_stat stat
87 #ifdef ALPHA_LINUX
88 #define COPY_STAT(src, dest)                    \
89 do {                                            \
90         memset((dest), 0, sizeof((*dest)));     \
91         (dest)->st_dev     = (src)->st_dev;     \
92         (dest)->st_ino     = (src)->st_ino;     \
93         (dest)->st_mode    = (src)->st_mode;    \
94         (dest)->st_nlink   = (src)->st_nlink;   \
95         (dest)->st_uid     = (src)->st_uid;     \
96         (dest)->st_gid     = (src)->st_gid;     \
97         (dest)->st_rdev    = (src)->st_rdev;    \
98         (dest)->st_size    = (src)->st_size;    \
99         (dest)->st_atime   = (src)->st_atime;   \
100         (dest)->st_mtime   = (src)->st_mtime;   \
101         (dest)->st_ctime   = (src)->st_ctime;   \
102         (dest)->st_blksize = (src)->st_blksize; \
103         (dest)->st_blocks  = (src)->st_blocks;  \
104         (dest)->st_flags   = (src)->st_flags;   \
105         (dest)->st_gen     = (src)->st_gen;     \
106 } while (0);
107 #else
108 #define COPY_STAT(src, dest)                    \
109 do {                                            \
110         memset((dest), 0, sizeof((*dest)));     \
111         (dest)->st_dev     = (src)->st_dev;     \
112         (dest)->st_ino     = (src)->st_ino;     \
113         (dest)->st_mode    = (src)->st_mode;    \
114         (dest)->st_nlink   = (src)->st_nlink;   \
115         (dest)->st_uid     = (src)->st_uid;     \
116         (dest)->st_gid     = (src)->st_gid;     \
117         (dest)->st_rdev    = (src)->st_rdev;    \
118         (dest)->st_size    = (src)->st_size;    \
119         (dest)->st_atime   = (src)->st_atime;   \
120         (dest)->st_mtime   = (src)->st_mtime;   \
121         (dest)->st_ctime   = (src)->st_ctime;   \
122         (dest)->st_blksize = (src)->st_blksize; \
123         (dest)->st_blocks  = (src)->st_blocks;  \
124 } while (0);
125 #endif
126
127 /*
128  * Yod file identifiers format.
129  */
130 struct yod_inode_identifier {
131         dev_t   dev;                                    /* device number */
132         ino_t   ino;                                    /* i-number */
133 #ifdef HAVE_GENERATION
134         unsigned int gen;                               /* generation number */
135 #endif
136 };
137
138 /*
139  * Driver-private i-node information we keep about local host file
140  * system objects.
141  */
142 struct yod_inode {
143         unsigned ni_seekok              : 1;            /* can seek? */
144         struct yod_inode_identifier ni_ident;           /* unique identifier */
145         struct file_identifier ni_fileid;               /* ditto */
146         int     ni_fd;                                  /* host fildes */
147         int     ni_oflags;                              /* flags, from open */
148         unsigned ni_nopens;                             /* soft ref count */
149         _SYSIO_OFF_T ni_fpos;                           /* current pos */
150 };
151
152 static int yod_inop_lookup(struct pnode *pno,
153                               struct inode **inop,
154                               struct intent *intnt,
155                               const char *path);
156 static int yod_inop_getattr(struct pnode *pno,
157                                struct inode *ino,
158                                struct intnl_stat *stbuf);
159 static int yod_inop_setattr(struct pnode *pno,
160                                struct inode *ino,
161                                unsigned mask,
162                                struct intnl_stat *stbuf);
163 static ssize_t yod_filldirentries(struct inode *ino,
164                                   off64_t *posp,
165                                   char *buf,
166                                   size_t nbytes);
167 static int yod_inop_mkdir(struct pnode *pno, mode_t mode);
168 static int yod_inop_rmdir(struct pnode *pno);
169 static int yod_inop_symlink(struct pnode *pno, const char *data);
170 static int yod_inop_readlink(struct pnode *pno, char *buf, size_t bufsiz);
171 static int yod_inop_open(struct pnode *pno, int flags, mode_t mode);
172 static int yod_inop_close(struct inode *ino);
173 static int yod_inop_link(struct pnode *old, struct pnode *new);
174 static int yod_inop_unlink(struct pnode *pno);
175 static int yod_inop_rename(struct pnode *old, struct pnode *new);
176 static _SYSIO_OFF_T yod_inop_pos (struct inode *ino, _SYSIO_OFF_T off);  
177 static int yod_inop_read(struct inode *ino, struct ioctx *ioctx);
178 static int yod_inop_write(struct inode *ino, struct ioctx *ioctx);
179 static int yod_inop_iodone(struct ioctx *ioctx);
180 static int yod_inop_fcntl(struct inode *ino, int cmd, va_list ap, int *rtn);
181 static int yod_inop_sync(struct inode *ino);
182 static int yod_inop_datasync(struct inode *ino);
183 static int yod_inop_ioctl(struct inode *ino,
184                              unsigned long int request,
185                              va_list ap);
186 static int yod_inop_mknod(struct pnode *pno, mode_t mode, dev_t dev);
187 #ifdef _HAVE_STATVFS
188 static int yod_inop_statvfs(struct pnode *pno,
189                                struct inode *ino,
190                                struct intnl_statvfs *buf);
191 #endif
192 static void yod_inop_gone(struct inode *ino);
193
194 static struct inode_ops yod_i_ops = {
195         yod_inop_lookup,
196         yod_inop_getattr,
197         yod_inop_setattr,
198         yod_filldirentries,
199         yod_inop_mkdir,
200         yod_inop_rmdir,
201         yod_inop_symlink,
202         yod_inop_readlink,
203         yod_inop_open,
204         yod_inop_close,
205         yod_inop_link,
206         yod_inop_unlink,
207         yod_inop_rename,
208         yod_inop_read,
209         yod_inop_write,
210         yod_inop_pos,
211         yod_inop_iodone,
212         yod_inop_fcntl,
213         yod_inop_sync,
214         yod_inop_datasync,
215         yod_inop_ioctl,
216         yod_inop_mknod,
217 #ifdef _HAVE_STATVFS
218         yod_inop_statvfs,
219 #endif
220         yod_inop_gone
221 };
222
223 static int yod_fsswop_mount(const char *source,
224                                unsigned flags,
225                                const void *data,
226                                struct pnode *tocover,
227                                struct mount **mntp);
228
229 static struct fssw_ops yod_fssw_ops = {
230         yod_fsswop_mount
231 };
232
233 static void yod_fsop_gone(struct filesys *fs);
234
235 static struct filesys_ops yod_inodesys_ops = {
236         yod_fsop_gone
237 };
238
239 /* 
240  * Placeholder internal mount as in native driver
241  */
242 static struct mount *yod_internal_mount = NULL;
243
244 /*
245  * Given i-node, return driver private part.
246  */
247 #define I2NI(ino)       ((struct yod_inode *)((ino)->i_private))
248
249 /*
250  * stat -- by path.
251  */
252 static int
253 yod_stat(const char *path, struct intnl_stat *buf)
254 {
255         int     err;
256         struct __yod_stat stbuf;
257         
258         err = stat_yod(path, &stbuf); 
259         if (err)
260                 err = -errno;
261         COPY_STAT(&stbuf, buf);
262
263         return err;
264 }
265
266 /*
267  * stat -- by fildes
268  */
269 static int
270 yod_fstat(int fd, struct intnl_stat *buf)
271 {
272         int     err;
273         struct __yod_stat stbuf;
274
275         err = fstat_yod(fd, &stbuf);
276         if (err)
277                 err = -errno;
278         COPY_STAT(&stbuf, buf);
279
280         return err;
281 }
282
283 /*
284  * Introduce an i-node to the system.
285  */
286 static struct inode *
287 yod_i_new(struct filesys *fs, struct intnl_stat *buf)
288 {
289         struct yod_inode *nino;
290         struct inode *ino;
291
292         nino = malloc(sizeof(struct yod_inode));
293         if (!nino)
294                 return NULL;
295         bzero(&nino->ni_ident, sizeof(nino->ni_ident));
296         nino->ni_seekok = 0;
297         nino->ni_ident.dev = buf->st_dev;
298         nino->ni_ident.ino = buf->st_ino;
299 #ifdef HAVE_GENERATION
300         nino->ni_ident.gen = buf->st_gen;
301 #endif
302         nino->ni_fileid.fid_data = &nino->ni_ident;
303         nino->ni_fileid.fid_len = sizeof(nino->ni_ident);
304         nino->ni_fd = -1;
305         nino->ni_oflags = 0;
306         nino->ni_nopens = 0;
307         nino->ni_fpos = 0;
308         ino =
309             _sysio_i_new(fs,
310                          &nino->ni_fileid,
311                          buf,
312                          0,
313                          &yod_i_ops,
314                          nino);
315         if (!ino)
316                 free(nino);
317         return ino;
318 }
319
320 /*
321  * Initialize this driver.
322  */
323 int
324 _sysio_yod_init()
325 {
326
327         /*
328          * Capture current process umask and reset our process umask to
329          * zero. All permission bits to open/creat/setattr are absolute --
330          * They've already had a umask applied, when appropriate.
331          */
332         _sysio_umask = syscall(SYS_umask, 0);
333
334         return _sysio_fssw_register("yod", &yod_fssw_ops);
335 }
336
337 /*
338  * Create private, internal, view of the hosts name space.
339  */
340 static int
341 create_internal_namespace()
342 {
343         int     err;
344         struct mount *mnt;
345         struct inode *rootino;
346         struct pnode_base *rootpb;
347         static struct qstr noname = { NULL, 0, 0 };
348         struct filesys *fs;
349         struct intnl_stat stbuf;
350
351         if (yod_internal_mount) {
352                 /*
353                  * Reentered!
354                  */
355                 abort();
356         }
357
358         /*
359          * We maintain an artificial, internal, name space in order to
360          * have access to fully qualified path names in the various routines.
361          * Initialize that name space now.
362          */
363         mnt = NULL;
364         rootino = NULL;
365         rootpb = NULL;
366         fs = _sysio_fs_new(&yod_inodesys_ops, 0, NULL);
367         if (!fs) {
368                 err = -ENOMEM;
369                 goto error;
370         }
371
372         /*
373          * Get root i-node.
374          */
375         err = yod_stat("/", &stbuf);
376         if (err)
377                 goto error;
378         rootino = yod_i_new(fs, &stbuf);
379         if (!rootino) {
380                 err = -ENOMEM;
381                 goto error;
382         }
383
384         /*
385          * Generate base path-node for root.
386          */
387         rootpb = _sysio_pb_new(&noname, NULL, rootino);
388         if (!rootpb) {
389                 err = -ENOMEM;
390                 goto error;
391         }
392
393         /*
394          * Mount it. This name space is disconnected from the
395          * rest of the system -- Only available within this driver.
396          */
397         err = _sysio_do_mount(fs, rootpb, 0, NULL, &mnt);
398         if (err)
399                 goto error;
400
401         yod_internal_mount = mnt;
402         return 0;
403 error:
404         if (mnt) {
405                 if (_sysio_do_unmount(mnt) != 0)
406                         abort();
407                 fs = NULL;
408                 rootpb = NULL;
409                 rootino = NULL;
410         }
411         if (rootpb)
412                 _sysio_pb_gone(rootpb);
413         if (fs) {
414                 FS_RELE(fs);
415         }
416
417         return err;
418 }
419
420 static int
421 yod_fsswop_mount(const char *source,
422                  unsigned flags,
423                  const void *data __IS_UNUSED,
424                  struct pnode *tocover,
425                  struct mount **mntp)
426 {
427         int     err;
428         struct nameidata nameidata;
429         struct mount *mnt;
430
431         /*
432          * Caller must use fully qualified path names when specifying
433          * the source.
434          */
435         if (*source != '/')
436                 return -ENOENT;
437
438         if (!yod_internal_mount) {
439                 err = create_internal_namespace();
440                 if (err)
441                         return err;
442         }
443
444         /*
445          * Lookup the source in the internally maintained name space.
446          */
447         ND_INIT(&nameidata, 0, source, yod_internal_mount->mnt_root, NULL);
448         err = _sysio_path_walk(yod_internal_mount->mnt_root, &nameidata);
449         if (err)
450                 return err;
451
452         /*
453          * Have path-node specified by the given source argument. Let the
454          * system finish the job, now.
455          */
456         err =
457             _sysio_do_mount(yod_internal_mount->mnt_fs,
458                             nameidata.nd_pno->p_base,
459                             flags,
460                             tocover,
461                             &mnt);
462         /*
463          * Release the internal name space pnode and clean up any
464          * aliases we might have generated. We really don't need to cache them
465          * as they are only used at mount time..
466          */
467         P_RELE(nameidata.nd_pno);
468         (void )_sysio_p_prune(yod_internal_mount->mnt_root);
469
470         if (!err) {
471                 FS_REF(yod_internal_mount->mnt_fs);
472                 *mntp = mnt;
473         }
474         return err;
475 }
476
477 static int
478 yod_i_invalid(struct inode *inop, struct intnl_stat *stat)
479 {
480         /*
481          * Validate passed in inode against stat struct info
482          */
483         struct yod_inode *nino = I2NI(inop);
484         
485         if ((nino->ni_ident.dev != stat->st_dev ||
486              nino->ni_ident.ino != stat->st_ino ||
487 #ifdef HAVE_GENERATION
488              nino->ni_ident.gen != stat->st_gen ||
489 #endif
490              ((inop)->i_stbuf.st_mode & S_IFMT) != (stat->st_mode & S_IFMT)) ||
491             (((inop)->i_stbuf.st_rdev != stat->st_rdev) &&
492                (S_ISCHR((inop)->i_stbuf.st_mode) ||
493                 S_ISBLK((inop)->i_stbuf.st_mode))))
494                 return 1;
495         
496         return 0;
497 }
498
499 /*
500  * Find, and validate, or create i-node by host-relative path. Returned i-node
501  * is referenced.
502  */
503 static int
504 yod_iget(struct filesys *fs,
505             const char *path,
506             struct inode **inop,
507             int forced)
508 {
509         int     err;
510         struct inode *ino;
511         struct intnl_stat stbuf;
512         struct yod_inode_identifier ident;
513         struct file_identifier fileid;
514
515         /*
516          * Get file status.
517          */
518         err = yod_stat(path, &stbuf);
519         if (err) {
520                 *inop = NULL;
521                 return err;
522         }
523
524         /*
525          * Validate?
526          */
527         if (*inop) {
528                 if (!yod_i_invalid(*inop, &stbuf))
529                         return 0;
530                 /*
531                  * Invalidate.
532                  */
533                 *inop = NULL;
534         }
535
536         /*
537          * I-node is not already known. Find or create it.
538          */
539         bzero(&ident, sizeof(ident)); 
540         ident.dev = stbuf.st_dev;
541         ident.ino = stbuf.st_ino;
542 #ifdef HAVE_GENERATION
543         ident.gen = stbuf.st_gen;
544 #endif
545         fileid.fid_data = &ident;
546         fileid.fid_len = sizeof(ident);
547         ino = _sysio_i_find(fs, &fileid);
548         if (ino && forced) {
549                 /*
550                  * Insertion was forced but it's already present!
551                  */
552                 if (yod_i_invalid(ino, &stbuf)) {
553                         /* 
554                          * Cached inode has stale attrs
555                          * make way for the new one
556                          */
557                         I_RELE(ino);
558                         _sysio_i_undead(ino);
559                         ino = NULL;
560                 } else
561                         /* 
562                          * OK to reuse cached inode
563                          */
564                         goto out;
565         }
566
567         if (!ino) {
568                 ino = yod_i_new(fs, &stbuf);
569                 if (!ino)
570                         err = -ENOMEM;
571         }
572 out:
573         if (!err)
574                 *inop = ino;
575         return err;
576 }
577
578 /*
579  * Look up named object in host's name space by path.
580  */
581 static int
582 yod_path_lookup(struct filesys *fs, const char *path, struct inode **inop)
583 {
584
585         return yod_iget(fs, path, inop, 0);
586 }
587
588 /*
589  * Look up object by it's path node.
590  */
591 static int
592 yod_i_lookup(struct filesys *fs, struct pnode_base *pb, struct inode **inop)
593 {
594         int     err;
595         char    *path;
596
597         path = _sysio_pb_path(pb, '/');
598         if (!path)
599                 return -ENOMEM;
600         err = yod_path_lookup(fs, path, inop);
601         free(path);
602         return err;
603 }
604
605 static int
606 yod_inop_lookup(struct pnode *pno,
607                    struct inode **inop,
608                    struct intent *intnt __IS_UNUSED,
609                    const char *path __IS_UNUSED)
610 {
611         int     err;
612
613         *inop = pno->p_base->pb_ino;
614
615         /*
616          * Don't have an inode yet. Because we translate everything back to
617          * a single name space for the host, we will assume the object the
618          * caller is looking for has no existing alias in our internal
619          * name space. We don't see the same file on different mounts in the
620          * underlying host FS as the same file.
621          *
622          * The file identifier *will* be unique. It's got to have a different
623          * dev.
624          */
625         err = yod_i_lookup(pno->p_mount->mnt_fs, pno->p_base, inop);
626         if (err)
627                 *inop = NULL;
628         return err;
629 }
630
631 static int
632 yod_inop_getattr(struct pnode *pno, struct inode *ino, struct intnl_stat *stbuf)
633 {
634         char    *path;
635         int     err;
636
637         path = NULL;
638         if (!ino || I2NI(ino)->ni_fd < 0) {
639                 path = _sysio_pb_path(pno->p_base, '/');
640                 if (!path)
641                         return -ENOMEM;
642         }
643         err =
644             path
645               ? yod_stat(path, stbuf)
646               : yod_fstat(I2NI(ino)->ni_fd, stbuf);
647         if (path)
648                 free(path);
649         return err;
650 }
651
652 static int
653 yod_inop_setattr(struct pnode *pno,
654                     struct inode *ino,
655                     unsigned mask,
656                     struct intnl_stat *stbuf)
657 {
658         char    *path;
659         int     fd;
660         struct intnl_stat st;
661         int     err;
662
663         path = NULL;
664         fd = ino ? I2NI(ino)->ni_fd : -1;
665         if (fd < 0 || mask & (SETATTR_MTIME|SETATTR_ATIME)) {
666                 if (!pno)
667                         return -EEXIST;
668                 path = _sysio_pb_path(pno->p_base, '/');
669                 if (!path)
670                         return -ENOMEM;
671         }
672
673         /*
674          * Get current status for undo.
675          */
676         err =
677             fd < 0
678               ? yod_stat(path, &st)
679               : yod_fstat(fd, &st);
680         if (err)
681                 goto out;
682
683         if (mask & SETATTR_MODE) {
684                 mode_t  mode;
685
686                 /*
687                  * Alter permissions attribute.
688                  */
689                 mode = stbuf->st_mode & 07777;
690                 err = chmod_yod(path, mode);
691         }
692         if (err)
693                 mask &= ~SETATTR_MODE;
694
695         if (mask & (SETATTR_UID|SETATTR_GID)) {
696
697                 /*
698                  * Alter owner and/or group identifiers.
699                  */
700                 err = chown_yod(path,
701                                 mask & SETATTR_UID
702                                   ? stbuf->st_uid
703                                   : (uid_t )-1,
704                                 mask & SETATTR_GID
705                                   ? stbuf->st_gid
706                                   : (gid_t )-1);
707         }
708         if (err)
709                 mask &= ~(SETATTR_UID|SETATTR_GID);
710         else if (mask & SETATTR_LEN) {
711                 /*
712                  * Do the truncate last. It can't be undone.
713                  */
714                  (void )(fd < 0
715                            ? truncate_yod(path, stbuf->st_size)
716                            : ftruncate_yod(fd, stbuf->st_size));
717         }
718         if (!err)
719                 goto out;
720         /*
721          * Undo after error. Some or all of this might not work... We
722          * can but try.
723          */
724         if (mask & (SETATTR_UID|SETATTR_GID)) {
725                  (void )chown_yod(path,
726                                   mask & SETATTR_UID
727                                     ? st.st_uid
728                                     : (uid_t )-1,
729                                   mask & SETATTR_GID
730                                     ? st.st_gid
731                                     : (gid_t )-1);
732         }
733         if (mask & SETATTR_MODE) {
734                 chmod_yod(path, st.st_mode & 07777);
735         }
736 out:
737         if (path)
738                 free(path);
739         return err;
740 }
741
742 static ssize_t
743 yod_filldirentries(struct inode *ino,
744                    char *buf,
745                    _SYSIO_OFF_T *posp,
746                     size_t nbytes)
747 {
748         struct yod_inode *nino = I2NI(ino);
749         _SYSIO_OFF_T result;
750         ssize_t cc;
751
752         assert(nino->ni_fd >= 0);
753
754         result = *basep;
755         if (*basep != nino->ni_fpos &&
756             (result = lseek_yod(nino->ni_fd,
757                                 *posp,
758                                 SEEK_SET) == -1))
759                 return -errno;
760         nino->ni_fpos = result;
761         memset(buf, 0, nbytes);
762         /*
763          * This is almost certainly broken. The resulting position parameter
764          * points to the block just filled, not the next.
765          */
766         cc = getdirentries_yod(nino->ni_fd, buf, nbytes, &result);
767         if (cc < 0)
768                 return -errno;
769         nino->ni_fpos = *posp = result;
770         return cc;
771 }
772
773 static int
774 yod_inop_mkdir(struct pnode *pno, mode_t mode)
775 {
776         char    *path;
777         int     err;
778
779         path = _sysio_pb_path(pno->p_base, '/');
780         if (!path)
781                 return -ENOMEM;
782
783         err = mkdir_yod(path, mode);
784         free(path);
785         return err;
786 }
787
788 static int
789 yod_inop_rmdir(struct pnode *pno)
790 {
791         char    *path;
792         int     err;
793
794         path = _sysio_pb_path(pno->p_base, '/');
795         if (!path)
796                 return -ENOMEM;
797
798         err = rmdir_yod(path);
799         free(path);
800         return err;
801 }
802
803 static int
804 yod_inop_symlink(struct pnode *pno, const char *data)
805 {
806         char    *path;
807         int     err;
808
809         path = _sysio_pb_path(pno->p_base, '/');
810         if (!path)
811                 return -ENOMEM;
812
813         err = symlink_yod(data, path);
814         free(path);
815         return err;
816 }
817
818 static int
819 yod_inop_readlink(struct pnode *pno __IS_UNUSED, 
820                   char *buf __IS_UNUSED, 
821                   size_t bufsiz __IS_UNUSED)
822 {
823
824         return -ENOSYS;
825 }
826
827 static int
828 yod_inop_open(struct pnode *pno, int flags, mode_t mode)
829 {
830         struct yod_inode *nino;
831         char    *path;
832         int     fd;
833
834         path = _sysio_pb_path(pno->p_base, '/');
835         if (!path)
836                 return -ENOMEM;
837
838         /*
839          * Whether the file is already open, or not, makes no difference.
840          * Want to always give the host OS a chance to authorize in case
841          * something has changed underneath us.
842          */
843         if (flags & O_WRONLY) {
844                 /*
845                  * Promote write-only attempt to RW.
846                  */
847                 flags &= ~O_WRONLY;
848                 flags |= O_RDWR;
849         }
850         fd = open_yod(path, flags, mode);
851         if (!pno->p_base->pb_ino && fd >= 0) {
852                 int     err;
853
854                 /*
855                  * Success but we need to return an i-node.
856                  */
857                 err =
858                     yod_iget(pno->p_mount->mnt_fs,
859                                 path,
860                                 &pno->p_base->pb_ino,
861                                 1);
862                 if (err) {
863                         (void )close_yod(fd);
864                         if (err == -EEXIST)
865                                 abort();
866                         fd = err;
867                 }
868         }
869         free(path);
870         if (fd < 0)
871                 return -errno;
872
873         /*
874          * Remember this new open.
875          */
876         nino = I2NI(pno->p_base->pb_ino);
877         nino->ni_nopens++;
878         assert(nino->ni_nopens);
879
880         if (nino->ni_fd >= 0) {
881                 if ((nino->ni_oflags & O_RDWR) ||
882                     (flags & (O_RDONLY|O_WRONLY|O_RDWR)) == O_RDONLY) {
883                         /*
884                          * Keep existing.
885                          */
886                         (void )close_yod(fd);
887                         return 0;
888                 }
889                 (void )close_yod(nino->ni_fd);
890         }
891         /*
892          * Invariant; First open. Must init.
893          */
894         nino->ni_fpos = 0;
895         nino->ni_fd = fd;
896
897         /*
898          * Need to know whether we can seek on this
899          * descriptor.
900          */
901         nino->ni_seekok =
902             lseek_yod(nino->ni_fd, 0, SEEK_CUR) != 0 ? 0 : 1;
903
904         return 0;
905 }
906
907 static int
908 yod_inop_close(struct inode *ino)
909 {
910         struct yod_inode *nino = I2NI(ino);
911         int     err;
912
913         if (nino->ni_fd < 0)
914                 abort();
915
916         assert(nino->ni_nopens);
917         if (--nino->ni_nopens)
918                 return 0;
919
920         err = close_yod(nino->ni_fd);
921         if (err)
922                 return -errno;
923
924         nino->ni_fd = -1;
925         nino->ni_fpos = 0;
926         return 0;
927 }
928
929 static int
930 yod_inop_link(struct pnode *old, struct pnode *new)
931 {
932         int     err;
933         char    *opath, *npath;
934
935         err = 0;
936
937         opath = _sysio_pb_path(old->p_base, '/');
938         npath = _sysio_pb_path(new->p_base, '/');
939         if (!(opath && npath)) {
940                 err = -ENOMEM;
941                 goto out;
942         }
943
944         err = link_yod(opath, npath);
945
946 out:
947         if (opath)
948                 free(opath);
949         if (npath)
950                 free(npath);
951
952         return err;
953 }
954
955 static int
956 yod_inop_unlink(struct pnode *pno)
957 {
958         char    *path;
959         int     err = 0;
960
961         path = _sysio_pb_path(pno->p_base, '/');
962         if (!path)
963                 return -ENOMEM;
964
965         /*
966          * For this driver, unlink is easy with open files. Since the
967          * file remains open to the system, too, the descriptors are still
968          * valid.
969          *
970          * Other drivers will have some difficulty here as the entry in the
971          * file system name space must be removed without sacrificing access
972          * to the file itself. In NFS this is done with a mechanism referred
973          * to as a `silly delete'. The file is moved to a temporary name
974          * (usually .NFSXXXXXX, where the X's are replaced by the PID and some
975          * unique characters) in order to simulate the proper semantic.
976          */
977         if (unlink_yod(path) != 0)
978                 err = -errno;
979         free(path);
980         return err;
981 }
982
983 /*
984  * A helper function performing the real IO operation work.
985  *
986  * We don't really have async IO. We'll just perform the function
987  * now.
988  */
989 static int
990 doio(ssize_t (*f)(void *, size_t, _SYSIO_OFF_T, struct yod_inode *),
991         struct ioctx *ioctx)
992 {
993         struct yod_inode *nino = I2NI(ioctx->ioctx_ino);
994
995         ioctx->ioctx_cc =
996                 _sysio_doio(ioctx->ioctx_xtv, ioctx->ioctx_xtvlen,
997                             ioctx->ioctx_iov, ioctx->ioctx_iovlen,
998                             (ssize_t (*)(void *, size_t, 
999                                          _SYSIO_OFF_T, void *))f,
1000                             nino);
1001         if (ioctx->ioctx_cc < 0) {
1002                 ioctx->ioctx_errno = -ioctx->ioctx_cc;
1003                 ioctx->ioctx_cc = -1;
1004                 return -1;
1005         }
1006         nino->ni_fpos += ioctx->ioctx_cc;
1007         ioctx->ioctx_done = 1;
1008         return 0;
1009 }       
1010
1011 static ssize_t
1012 yod_read_simple(void *buf,
1013                 size_t nbytes,
1014                 _SYSIO_OFF_T off,
1015                 struct yod_inode *nino)
1016 {
1017         if (off != nino->ni_fpos) {
1018                 _SYSIO_OFF_T rtn;
1019
1020                 rtn = lseek_yod(nino->ni_fd, off, SEEK_SET);
1021                 if (rtn < 0) 
1022                         return -1;
1023                 nino->ni_fpos = rtn;
1024         }
1025         return read_yod(nino->ni_fd, buf, nbytes);
1026 }
1027
1028 static int
1029 yod_inop_read(struct inode *ino __IS_UNUSED, struct ioctx *ioctx)
1030 {
1031
1032         return doio(yod_read_simple, ioctx);
1033 }
1034
1035 static int
1036 yod_inop_rename(struct pnode *old, struct pnode *new)
1037 {
1038         int     err;
1039         char    *opath, *npath;
1040
1041         opath = _sysio_pb_path(old->p_base, '/');
1042         npath = _sysio_pb_path(new->p_base, '/');
1043         if (!(opath && npath)) {
1044                 err = -ENOMEM;
1045                 goto out;
1046         }
1047
1048         err = rename_yod(opath, npath);
1049
1050 out:
1051         if (opath)
1052                 free(opath);
1053         if (npath)
1054                 free(npath);
1055
1056         return err;
1057 }
1058
1059 static ssize_t
1060 yod_write_simple(void *buf,
1061                 size_t nbytes,
1062                 _SYSIO_OFF_T off,
1063                 struct yod_inode *nino)
1064 {
1065
1066         if (off != nino->ni_fpos) {
1067                 _SYSIO_OFF_T rtn;
1068
1069                 rtn = lseek_yod(nino->ni_fd, off, SEEK_SET);
1070                 if (rtn < 0) 
1071                         return -1;
1072                 nino->ni_fpos = rtn;
1073         }
1074         return write_yod(nino->ni_fd, buf, nbytes);
1075 }
1076
1077 static int
1078 yod_inop_write(struct inode *ino __IS_UNUSED, struct ioctx *ioctx)
1079 {
1080
1081         return doio(yod_write_simple, ioctx);
1082 }
1083
1084 static _SYSIO_OFF_T
1085 yod_inop_pos(struct inode *ino, _SYSIO_OFF_T off)
1086 {
1087         struct yod_inode *nino = I2NI(ino);
1088         int     err;
1089
1090         err = lseek_yod(nino->ni_fd, off, SEEK_SET);
1091         return err < 0 ? err : off;
1092 }
1093
1094 static int
1095 yod_inop_iodone(struct ioctx *ioctxp __IS_UNUSED)
1096 {
1097
1098         /*
1099          * It's always done in this driver. It completed when posted.
1100          */
1101         return 1;
1102 }
1103
1104 static int
1105 yod_inop_fcntl(struct inode *ino, int cmd, va_list ap, int *rtn)
1106 {
1107         struct yod_inode *nino = I2NI(ino);
1108         long    arg;
1109         int     err;
1110
1111         if (nino->ni_fd < 0)
1112                 abort();
1113
1114         err = 0;
1115         switch (cmd) {
1116         case F_GETFD:
1117         case F_GETFL:
1118 #ifdef F_GETOWN
1119         case F_GETOWN:
1120 #endif
1121                 *rtn = syscall(SYS_fcntl, nino->ni_fd, cmd);
1122                 if (*rtn == -1)
1123                         err = -errno;
1124                 break;
1125         case F_DUPFD:
1126         case F_SETFD:
1127         case F_SETFL:
1128         case F_GETLK:
1129         case F_SETLK:
1130         case F_SETLKW:
1131 #ifdef F_SETOWN
1132         case F_SETOWN:
1133 #endif
1134                 arg = va_arg(ap, long);
1135                 *rtn = syscall(SYS_fcntl, nino->ni_fd, cmd, arg);
1136                 if (*rtn == -1)
1137                         err = -errno;
1138                 break;
1139         default:
1140                 *rtn = -1;
1141                 err = -EINVAL;
1142         }
1143         return err;
1144 }
1145
1146 static int
1147 yod_inop_mknod(struct pnode *pno __IS_UNUSED,
1148                   mode_t mode __IS_UNUSED,
1149                   dev_t dev __IS_UNUSED)
1150 {
1151
1152         return -ENOSYS;
1153 }
1154
1155 #ifdef _HAVE_STATVFS
1156 static int
1157 yod_inop_statvfs(struct pnode *pno,
1158                     struct inode *ino,
1159                     struct intnl_statvfs *buf)
1160 {
1161         char    *path;
1162         int    rc;
1163         struct statfs fs;
1164
1165         path = NULL;
1166         if (!ino || I2NI(ino)->ni_fd < 0) {
1167                 path = _sysio_pb_path(pno->p_base, '/');
1168                 if (!path)
1169                         return -ENOMEM;
1170         }
1171
1172         /*
1173          * The syscall interface does not support SYS_fstatvfs.
1174          * Should possibly return ENOSYS, but thought it
1175          * better to use SYS_fstatfs and fill in as much of
1176          * the statvfs structure as possible.  This allows
1177          * for more of a test of the sysio user interface.
1178          */
1179         rc =
1180             path
1181               ? statfs_yod(path, &fs)
1182               : fstatfs_yod(I2NI(ino)->ni_fd, &fs);
1183         if (path)
1184                 free(path);
1185         if (rc < 0)
1186                 return -errno;
1187
1188         buf->f_bsize = fs.f_bsize;  /* file system block size */
1189         buf->f_frsize = fs.f_bsize; /* file system fundamental block size */
1190         buf->f_blocks = fs.f_blocks;
1191         buf->f_bfree = fs.f_bfree;
1192         buf->f_bavail = fs.f_bavail;
1193         buf->f_files = fs.f_files;  /* Total number serial numbers */
1194         buf->f_ffree = fs.f_ffree;  /* Number free serial numbers */
1195         buf->f_favail = fs.f_ffree; /* Number free ser num for non-privileged*/
1196         buf->f_fsid = fs.f_fsid.__val[1];
1197         buf->f_flag = 0;            /* No equiv in statfs; maybe use type? */
1198         buf->f_namemax = fs.f_namelen;
1199         return 0;
1200 }
1201 #endif
1202
1203 static int
1204 yod_inop_sync(struct inode *ino)
1205 {
1206
1207         assert(I2NI(ino)->ni_fd >= 0);
1208
1209         return fsync_yod(I2NI(ino)->ni_fd);
1210 }
1211
1212 static int
1213 yod_inop_datasync(struct inode *ino)
1214 {
1215
1216         assert(I2NI(ino)->ni_fd >= 0);
1217
1218         return fsync_yod(I2NI(ino)->ni_fd);
1219 }
1220
1221 static int
1222 yod_inop_ioctl(struct inode *ino __IS_UNUSED,
1223                   unsigned long int request __IS_UNUSED,
1224                   va_list ap __IS_UNUSED)
1225 {
1226
1227         /*
1228          * I'm lazy. Maybe implemented later.
1229          */
1230         errno = ENOTTY;
1231         return -1;
1232 }
1233
1234 static void
1235 yod_inop_gone(struct inode *ino)
1236 {
1237         struct yod_inode *nino = I2NI(ino);
1238
1239         if (nino->ni_fd)
1240                 (void )close(nino->ni_fd);
1241         free(ino->i_private);
1242 }
1243
1244 static void
1245 yod_fsop_gone(struct filesys *fs __IS_UNUSED)
1246 {
1247
1248         /*
1249          * Do nothing. There is no private part maintained for the
1250          * yod file interface. 
1251          */
1252 }