Whamcloud - gitweb
c9bfcf32ca958f58c39ee6676741a6c9607c7eea
[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_getdirentries(struct inode *ino,
164                                     char *buf,
165                                     size_t nbytes,
166                                     off64_t *basep);
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_getdirentries,
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                 _sysio_fs_gone(fs);
416         }
417
418         return err;
419 }
420
421 static int
422 yod_fsswop_mount(const char *source,
423                  unsigned flags,
424                  const void *data __IS_UNUSED,
425                  struct pnode *tocover,
426                  struct mount **mntp)
427 {
428         int     err;
429         struct nameidata nameidata;
430         struct mount *mnt;
431
432         /*
433          * Caller must use fully qualified path names when specifying
434          * the source.
435          */
436         if (*source != '/')
437                 return -ENOENT;
438
439         if (!yod_internal_mount) {
440                 err = create_internal_namespace();
441                 if (err)
442                         return err;
443         }
444
445         /*
446          * Lookup the source in the internally maintained name space.
447          */
448         ND_INIT(&nameidata, 0, source, yod_internal_mount->mnt_root, NULL);
449         err = _sysio_path_walk(yod_internal_mount->mnt_root, &nameidata);
450         if (err)
451                 return err;
452
453         /*
454          * Have path-node specified by the given source argument. Let the
455          * system finish the job, now.
456          */
457         err =
458             _sysio_do_mount(yod_internal_mount->mnt_fs,
459                             nameidata.nd_pno->p_base,
460                             flags,
461                             tocover,
462                             &mnt);
463         /*
464          * Release the internal name space pnode and clean up any
465          * aliases we might have generated. We really don't need to cache them
466          * as they are only used at mount time..
467          */
468         P_RELE(nameidata.nd_pno);
469         (void )_sysio_p_prune(yod_internal_mount->mnt_root);
470
471         if (!err) {
472                 FS_REF(yod_internal_mount->mnt_fs);
473                 *mntp = mnt;
474         }
475         return err;
476 }
477
478 static int
479 yod_i_invalid(struct inode *inop, struct intnl_stat *stat)
480 {
481         /*
482          * Validate passed in inode against stat struct info
483          */
484         struct yod_inode *nino = I2NI(inop);
485         
486         if ((nino->ni_ident.dev != stat->st_dev ||
487              nino->ni_ident.ino != stat->st_ino ||
488 #ifdef HAVE_GENERATION
489              nino->ni_ident.gen != stat->st_gen ||
490 #endif
491              ((inop)->i_stbuf.st_mode & S_IFMT) != (stat->st_mode & S_IFMT)) ||
492             (((inop)->i_stbuf.st_rdev != stat->st_rdev) &&
493                (S_ISCHR((inop)->i_stbuf.st_mode) ||
494                 S_ISBLK((inop)->i_stbuf.st_mode))))
495                 return 1;
496         
497         return 0;
498 }
499
500 /*
501  * Find, and validate, or create i-node by host-relative path. Returned i-node
502  * is referenced.
503  */
504 static int
505 yod_iget(struct filesys *fs,
506             const char *path,
507             struct inode **inop,
508             int forced)
509 {
510         int     err;
511         struct inode *ino;
512         struct intnl_stat stbuf;
513         struct yod_inode_identifier ident;
514         struct file_identifier fileid;
515
516         /*
517          * Get file status.
518          */
519         err = yod_stat(path, &stbuf);
520         if (err) {
521                 *inop = NULL;
522                 return err;
523         }
524
525         /*
526          * Validate?
527          */
528         if (*inop) {
529                 if (!yod_i_invalid(*inop, &stbuf))
530                         return 0;
531                 /*
532                  * Invalidate.
533                  */
534                 *inop = NULL;
535         }
536
537         /*
538          * I-node is not already known. Find or create it.
539          */
540         bzero(&ident, sizeof(ident)); 
541         ident.dev = stbuf.st_dev;
542         ident.ino = stbuf.st_ino;
543 #ifdef HAVE_GENERATION
544         ident.gen = stbuf.st_gen;
545 #endif
546         fileid.fid_data = &ident;
547         fileid.fid_len = sizeof(ident);
548         ino = _sysio_i_find(fs, &fileid);
549         if (ino && forced) {
550                 /*
551                  * Insertion was forced but it's already present!
552                  */
553                 if (yod_i_invalid(ino, &stbuf)) {
554                         /* 
555                          * Cached inode has stale attrs
556                          * make way for the new one
557                          */
558                         I_RELE(ino);
559                         _sysio_i_undead(ino);
560                         ino = NULL;
561                 } else
562                         /* 
563                          * OK to reuse cached inode
564                          */
565                         goto out;
566         }
567
568         if (!ino) {
569                 ino = yod_i_new(fs, &stbuf);
570                 if (!ino)
571                         err = -ENOMEM;
572         }
573 out:
574         if (!err)
575                 *inop = ino;
576         return err;
577 }
578
579 /*
580  * Look up named object in host's name space by path.
581  */
582 static int
583 yod_path_lookup(struct filesys *fs, const char *path, struct inode **inop)
584 {
585
586         return yod_iget(fs, path, inop, 0);
587 }
588
589 /*
590  * Look up object by it's path node.
591  */
592 static int
593 yod_i_lookup(struct filesys *fs, struct pnode_base *pb, struct inode **inop)
594 {
595         int     err;
596         char    *path;
597
598         path = _sysio_pb_path(pb, '/');
599         if (!path)
600                 return -ENOMEM;
601         err = yod_path_lookup(fs, path, inop);
602         free(path);
603         return err;
604 }
605
606 static int
607 yod_inop_lookup(struct pnode *pno,
608                    struct inode **inop,
609                    struct intent *intnt __IS_UNUSED,
610                    const char *path __IS_UNUSED)
611 {
612         int     err;
613
614         *inop = pno->p_base->pb_ino;
615
616         /*
617          * Don't have an inode yet. Because we translate everything back to
618          * a single name space for the host, we will assume the object the
619          * caller is looking for has no existing alias in our internal
620          * name space. We don't see the same file on different mounts in the
621          * underlying host FS as the same file.
622          *
623          * The file identifier *will* be unique. It's got to have a different
624          * dev.
625          */
626         err = yod_i_lookup(pno->p_mount->mnt_fs, pno->p_base, inop);
627         if (err)
628                 *inop = NULL;
629         return err;
630 }
631
632 static int
633 yod_inop_getattr(struct pnode *pno, struct inode *ino, struct intnl_stat *stbuf)
634 {
635         char    *path;
636         int     err;
637
638         path = NULL;
639         if (!ino || I2NI(ino)->ni_fd < 0) {
640                 path = _sysio_pb_path(pno->p_base, '/');
641                 if (!path)
642                         return -ENOMEM;
643         }
644         err =
645             path
646               ? yod_stat(path, stbuf)
647               : yod_fstat(I2NI(ino)->ni_fd, stbuf);
648         if (path)
649                 free(path);
650         return err;
651 }
652
653 static int
654 yod_inop_setattr(struct pnode *pno,
655                     struct inode *ino,
656                     unsigned mask,
657                     struct intnl_stat *stbuf)
658 {
659         char    *path;
660         int     fd;
661         struct intnl_stat st;
662         int     err;
663
664         path = NULL;
665         fd = ino ? I2NI(ino)->ni_fd : -1;
666         if (fd < 0 || mask & (SETATTR_MTIME|SETATTR_ATIME)) {
667                 if (!pno)
668                         return -EEXIST;
669                 path = _sysio_pb_path(pno->p_base, '/');
670                 if (!path)
671                         return -ENOMEM;
672         }
673
674         /*
675          * Get current status for undo.
676          */
677         err =
678             fd < 0
679               ? yod_stat(path, &st)
680               : yod_fstat(fd, &st);
681         if (err)
682                 goto out;
683
684         if (mask & SETATTR_MODE) {
685                 mode_t  mode;
686
687                 /*
688                  * Alter permissions attribute.
689                  */
690                 mode = stbuf->st_mode & 07777;
691                 err = chmod_yod(path, mode);
692         }
693         if (err)
694                 mask &= ~SETATTR_MODE;
695
696         if (mask & (SETATTR_UID|SETATTR_GID)) {
697
698                 /*
699                  * Alter owner and/or group identifiers.
700                  */
701                 err = chown_yod(path,
702                                 mask & SETATTR_UID
703                                   ? stbuf->st_uid
704                                   : (uid_t )-1,
705                                 mask & SETATTR_GID
706                                   ? stbuf->st_gid
707                                   : (gid_t )-1);
708         }
709         if (err)
710                 mask &= ~(SETATTR_UID|SETATTR_GID);
711         else if (mask & SETATTR_LEN) {
712                 /*
713                  * Do the truncate last. It can't be undone.
714                  */
715                  (void )(fd < 0
716                            ? truncate_yod(path, stbuf->st_size)
717                            : ftruncate_yod(fd, stbuf->st_size));
718         }
719         if (!err)
720                 goto out;
721         /*
722          * Undo after error. Some or all of this might not work... We
723          * can but try.
724          */
725         if (mask & (SETATTR_UID|SETATTR_GID)) {
726                  (void )chown_yod(path,
727                                   mask & SETATTR_UID
728                                     ? st.st_uid
729                                     : (uid_t )-1,
730                                   mask & SETATTR_GID
731                                     ? st.st_gid
732                                     : (gid_t )-1);
733         }
734         if (mask & SETATTR_MODE) {
735                 chmod_yod(path, st.st_mode & 07777);
736         }
737 out:
738         if (path)
739                 free(path);
740         return err;
741 }
742
743 static ssize_t
744 yod_getdirentries(struct inode *ino,
745                      char *buf,
746                      size_t nbytes,
747                      _SYSIO_OFF_T *basep)
748 {
749         struct yod_inode *nino = I2NI(ino);
750         _SYSIO_OFF_T result;
751         ssize_t cc;
752
753         assert(nino->ni_fd >= 0);
754
755         result = *basep;
756         if (*basep != nino->ni_fpos &&
757             (result = lseek_yod(nino->ni_fd,
758                                 *basep,
759                                 SEEK_SET) == -1))
760                 return -errno;
761         nino->ni_fpos = result;
762         memset(buf, 0, nbytes);
763         cc = getdirentries_yod(nino->ni_fd, buf, nbytes, &result);
764         if (cc < 0)
765                 return -errno;
766         nino->ni_fpos = *basep = result;
767         return cc;
768 }
769
770 static int
771 yod_inop_mkdir(struct pnode *pno, mode_t mode)
772 {
773         char    *path;
774         int     err;
775
776         path = _sysio_pb_path(pno->p_base, '/');
777         if (!path)
778                 return -ENOMEM;
779
780         err = mkdir_yod(path, mode);
781         free(path);
782         return err;
783 }
784
785 static int
786 yod_inop_rmdir(struct pnode *pno)
787 {
788         char    *path;
789         int     err;
790
791         path = _sysio_pb_path(pno->p_base, '/');
792         if (!path)
793                 return -ENOMEM;
794
795         err = rmdir_yod(path);
796         free(path);
797         return err;
798 }
799
800 static int
801 yod_inop_symlink(struct pnode *pno, const char *data)
802 {
803         char    *path;
804         int     err;
805
806         path = _sysio_pb_path(pno->p_base, '/');
807         if (!path)
808                 return -ENOMEM;
809
810         err = symlink_yod(data, path);
811         free(path);
812         return err;
813 }
814
815 static int
816 yod_inop_readlink(struct pnode *pno __IS_UNUSED, 
817                   char *buf __IS_UNUSED, 
818                   size_t bufsiz __IS_UNUSED)
819 {
820
821         return -ENOSYS;
822 }
823
824 static int
825 yod_inop_open(struct pnode *pno, int flags, mode_t mode)
826 {
827         struct yod_inode *nino;
828         char    *path;
829         int     fd;
830
831         path = _sysio_pb_path(pno->p_base, '/');
832         if (!path)
833                 return -ENOMEM;
834
835         /*
836          * Whether the file is already open, or not, makes no difference.
837          * Want to always give the host OS a chance to authorize in case
838          * something has changed underneath us.
839          */
840         if (flags & O_WRONLY) {
841                 /*
842                  * Promote write-only attempt to RW.
843                  */
844                 flags &= ~O_WRONLY;
845                 flags |= O_RDWR;
846         }
847         fd = open_yod(path, flags, mode);
848         if (!pno->p_base->pb_ino && fd >= 0) {
849                 int     err;
850
851                 /*
852                  * Success but we need to return an i-node.
853                  */
854                 err =
855                     yod_iget(pno->p_mount->mnt_fs,
856                                 path,
857                                 &pno->p_base->pb_ino,
858                                 1);
859                 if (err) {
860                         (void )close_yod(fd);
861                         if (err == -EEXIST)
862                                 abort();
863                         fd = err;
864                 }
865         }
866         free(path);
867         if (fd < 0)
868                 return -errno;
869
870         /*
871          * Remember this new open.
872          */
873         nino = I2NI(pno->p_base->pb_ino);
874         nino->ni_nopens++;
875         assert(nino->ni_nopens);
876
877         if (nino->ni_fd >= 0) {
878                 if ((nino->ni_oflags & O_RDWR) ||
879                     (flags & (O_RDONLY|O_WRONLY|O_RDWR)) == O_RDONLY) {
880                         /*
881                          * Keep existing.
882                          */
883                         (void )close_yod(fd);
884                         return 0;
885                 }
886                 (void )close_yod(nino->ni_fd);
887         }
888         /*
889          * Invariant; First open. Must init.
890          */
891         nino->ni_fpos = 0;
892         nino->ni_fd = fd;
893
894         /*
895          * Need to know whether we can seek on this
896          * descriptor.
897          */
898         nino->ni_seekok =
899             lseek_yod(nino->ni_fd, 0, SEEK_CUR) != 0 ? 0 : 1;
900
901         return 0;
902 }
903
904 static int
905 yod_inop_close(struct inode *ino)
906 {
907         struct yod_inode *nino = I2NI(ino);
908         int     err;
909
910         if (nino->ni_fd < 0)
911                 abort();
912
913         assert(nino->ni_nopens);
914         if (--nino->ni_nopens)
915                 return 0;
916
917         err = close_yod(nino->ni_fd);
918         if (err)
919                 return -errno;
920
921         nino->ni_fd = -1;
922         nino->ni_fpos = 0;
923         return 0;
924 }
925
926 static int
927 yod_inop_link(struct pnode *old, struct pnode *new)
928 {
929         int     err;
930         char    *opath, *npath;
931
932         err = 0;
933
934         opath = _sysio_pb_path(old->p_base, '/');
935         npath = _sysio_pb_path(new->p_base, '/');
936         if (!(opath && npath)) {
937                 err = -ENOMEM;
938                 goto out;
939         }
940
941         err = link_yod(opath, npath);
942
943 out:
944         if (opath)
945                 free(opath);
946         if (npath)
947                 free(npath);
948
949         return err;
950 }
951
952 static int
953 yod_inop_unlink(struct pnode *pno)
954 {
955         char    *path;
956         int     err = 0;
957
958         path = _sysio_pb_path(pno->p_base, '/');
959         if (!path)
960                 return -ENOMEM;
961
962         /*
963          * For this driver, unlink is easy with open files. Since the
964          * file remains open to the system, too, the descriptors are still
965          * valid.
966          *
967          * Other drivers will have some difficulty here as the entry in the
968          * file system name space must be removed without sacrificing access
969          * to the file itself. In NFS this is done with a mechanism referred
970          * to as a `silly delete'. The file is moved to a temporary name
971          * (usually .NFSXXXXXX, where the X's are replaced by the PID and some
972          * unique characters) in order to simulate the proper semantic.
973          */
974         if (unlink_yod(path) != 0)
975                 err = -errno;
976         free(path);
977         return err;
978 }
979
980 /*
981  * A helper function performing the real IO operation work.
982  *
983  * We don't really have async IO. We'll just perform the function
984  * now.
985  */
986 static int
987 doio(ssize_t (*f)(void *, size_t, _SYSIO_OFF_T, struct yod_inode *),
988         struct ioctx *ioctx)
989 {
990         struct yod_inode *nino = I2NI(ioctx->ioctx_ino);
991
992         ioctx->ioctx_cc =
993                 _sysio_doio(ioctx->ioctx_xtv, ioctx->ioctx_xtvlen,
994                             ioctx->ioctx_iov, ioctx->ioctx_iovlen,
995                             (ssize_t (*)(void *, size_t, 
996                                          _SYSIO_OFF_T, void *))f,
997                             nino);
998         if (ioctx->ioctx_cc < 0) {
999                 ioctx->ioctx_errno = -ioctx->ioctx_cc;
1000                 ioctx->ioctx_cc = -1;
1001                 return -1;
1002         }
1003         nino->ni_fpos += ioctx->ioctx_cc;
1004         ioctx->ioctx_done = 1;
1005         return 0;
1006 }       
1007
1008 static ssize_t
1009 yod_read_simple(void *buf,
1010                 size_t nbytes,
1011                 _SYSIO_OFF_T off,
1012                 struct yod_inode *nino)
1013 {
1014         if (off != nino->ni_fpos) {
1015                 _SYSIO_OFF_T rtn;
1016
1017                 rtn = lseek_yod(nino->ni_fd, off, SEEK_SET);
1018                 if (rtn < 0) 
1019                         return -1;
1020                 nino->ni_fpos = rtn;
1021         }
1022         return read_yod(nino->ni_fd, buf, nbytes);
1023 }
1024
1025 static int
1026 yod_inop_read(struct inode *ino __IS_UNUSED, struct ioctx *ioctx)
1027 {
1028
1029         return doio(yod_read_simple, ioctx);
1030 }
1031
1032 static int
1033 yod_inop_rename(struct pnode *old, struct pnode *new)
1034 {
1035         int     err;
1036         char    *opath, *npath;
1037
1038         opath = _sysio_pb_path(old->p_base, '/');
1039         npath = _sysio_pb_path(new->p_base, '/');
1040         if (!(opath && npath)) {
1041                 err = -ENOMEM;
1042                 goto out;
1043         }
1044
1045         err = rename_yod(opath, npath);
1046
1047 out:
1048         if (opath)
1049                 free(opath);
1050         if (npath)
1051                 free(npath);
1052
1053         return err;
1054 }
1055
1056 static ssize_t
1057 yod_write_simple(void *buf,
1058                 size_t nbytes,
1059                 _SYSIO_OFF_T off,
1060                 struct yod_inode *nino)
1061 {
1062
1063         if (off != nino->ni_fpos) {
1064                 _SYSIO_OFF_T rtn;
1065
1066                 rtn = lseek_yod(nino->ni_fd, off, SEEK_SET);
1067                 if (rtn < 0) 
1068                         return -1;
1069                 nino->ni_fpos = rtn;
1070         }
1071         return write_yod(nino->ni_fd, buf, nbytes);
1072 }
1073
1074 static int
1075 yod_inop_write(struct inode *ino __IS_UNUSED, struct ioctx *ioctx)
1076 {
1077
1078         return doio(yod_write_simple, ioctx);
1079 }
1080
1081 static _SYSIO_OFF_T
1082 yod_inop_pos(struct inode *ino, _SYSIO_OFF_T off)
1083 {
1084         struct yod_inode *nino = I2NI(ino);
1085         int     err;
1086
1087         err = lseek_yod(nino->ni_fd, off, SEEK_SET);
1088         return err < 0 ? err : off;
1089 }
1090
1091 static int
1092 yod_inop_iodone(struct ioctx *ioctxp __IS_UNUSED)
1093 {
1094
1095         /*
1096          * It's always done in this driver. It completed when posted.
1097          */
1098         return 1;
1099 }
1100
1101 static int
1102 yod_inop_fcntl(struct inode *ino, int cmd, va_list ap, int *rtn)
1103 {
1104         struct yod_inode *nino = I2NI(ino);
1105         long    arg;
1106         int     err;
1107
1108         if (nino->ni_fd < 0)
1109                 abort();
1110
1111         err = 0;
1112         switch (cmd) {
1113         case F_GETFD:
1114         case F_GETFL:
1115 #ifdef F_GETOWN
1116         case F_GETOWN:
1117 #endif
1118                 *rtn = syscall(SYS_fcntl, nino->ni_fd, cmd);
1119                 if (*rtn == -1)
1120                         err = -errno;
1121                 break;
1122         case F_DUPFD:
1123         case F_SETFD:
1124         case F_SETFL:
1125         case F_GETLK:
1126         case F_SETLK:
1127         case F_SETLKW:
1128 #ifdef F_SETOWN
1129         case F_SETOWN:
1130 #endif
1131                 arg = va_arg(ap, long);
1132                 *rtn = syscall(SYS_fcntl, nino->ni_fd, cmd, arg);
1133                 if (*rtn == -1)
1134                         err = -errno;
1135                 break;
1136         default:
1137                 *rtn = -1;
1138                 err = -EINVAL;
1139         }
1140         return err;
1141 }
1142
1143 static int
1144 yod_inop_mknod(struct pnode *pno __IS_UNUSED,
1145                   mode_t mode __IS_UNUSED,
1146                   dev_t dev __IS_UNUSED)
1147 {
1148
1149         return -ENOSYS;
1150 }
1151
1152 #ifdef _HAVE_STATVFS
1153 static int
1154 yod_inop_statvfs(struct pnode *pno,
1155                     struct inode *ino,
1156                     struct intnl_statvfs *buf)
1157 {
1158         char    *path;
1159         int    rc;
1160         struct statfs fs;
1161
1162         path = NULL;
1163         if (!ino || I2NI(ino)->ni_fd < 0) {
1164                 path = _sysio_pb_path(pno->p_base, '/');
1165                 if (!path)
1166                         return -ENOMEM;
1167         }
1168
1169         /*
1170          * The syscall interface does not support SYS_fstatvfs.
1171          * Should possibly return ENOSYS, but thought it
1172          * better to use SYS_fstatfs and fill in as much of
1173          * the statvfs structure as possible.  This allows
1174          * for more of a test of the sysio user interface.
1175          */
1176         rc =
1177             path
1178               ? statfs_yod(path, &fs)
1179               : fstatfs_yod(I2NI(ino)->ni_fd, &fs);
1180         if (path)
1181                 free(path);
1182         if (rc < 0)
1183                 return -errno;
1184
1185         buf->f_bsize = fs.f_bsize;  /* file system block size */
1186         buf->f_frsize = fs.f_bsize; /* file system fundamental block size */
1187         buf->f_blocks = fs.f_blocks;
1188         buf->f_bfree = fs.f_bfree;
1189         buf->f_bavail = fs.f_bavail;
1190         buf->f_files = fs.f_files;  /* Total number serial numbers */
1191         buf->f_ffree = fs.f_ffree;  /* Number free serial numbers */
1192         buf->f_favail = fs.f_ffree; /* Number free ser num for non-privileged*/
1193         buf->f_fsid = fs.f_fsid.__val[1];
1194         buf->f_flag = 0;            /* No equiv in statfs; maybe use type? */
1195         buf->f_namemax = fs.f_namelen;
1196         return 0;
1197 }
1198 #endif
1199
1200 static int
1201 yod_inop_sync(struct inode *ino)
1202 {
1203
1204         assert(I2NI(ino)->ni_fd >= 0);
1205
1206         return fsync_yod(I2NI(ino)->ni_fd);
1207 }
1208
1209 static int
1210 yod_inop_datasync(struct inode *ino)
1211 {
1212
1213         assert(I2NI(ino)->ni_fd >= 0);
1214
1215         return fsync_yod(I2NI(ino)->ni_fd);
1216 }
1217
1218 static int
1219 yod_inop_ioctl(struct inode *ino __IS_UNUSED,
1220                   unsigned long int request __IS_UNUSED,
1221                   va_list ap __IS_UNUSED)
1222 {
1223
1224         /*
1225          * I'm lazy. Maybe implemented later.
1226          */
1227         errno = ENOTTY;
1228         return -1;
1229 }
1230
1231 static void
1232 yod_inop_gone(struct inode *ino)
1233 {
1234         struct yod_inode *nino = I2NI(ino);
1235
1236         if (nino->ni_fd)
1237                 (void )close(nino->ni_fd);
1238         free(ino->i_private);
1239 }
1240
1241 static void
1242 yod_fsop_gone(struct filesys *fs __IS_UNUSED)
1243 {
1244
1245         /*
1246          * Do nothing. There is no private part maintained for the
1247          * yod file interface. 
1248          */
1249 }