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