Whamcloud - gitweb
import libsysio for b_newsysio
[fs/lustre-release.git] / libsysio / drivers / incore / fs_incore.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 <stdlib.h>
49 #include <string.h>
50 #include <unistd.h>
51 #include <time.h>
52 #include <limits.h>
53 #include <errno.h>
54 #include <assert.h>
55 #include <sys/uio.h>
56 #include <sys/types.h>
57 #include <dirent.h>
58 #include <sys/stat.h>
59 #ifdef _HAVE_STATVFS
60 #include <sys/statvfs.h>
61 #endif
62 #include <sys/queue.h>
63
64 #include "xtio.h"
65 #include "sysio.h"
66 #include "fs.h"
67 #include "mount.h"
68 #include "inode.h"
69 #include "dev.h"
70
71 #include "fs_incore.h"
72
73
74 /*
75  * In-core file system pseudo-driver.
76  */
77
78 /*
79  * Pseudo-blocksize.
80  */
81 #define INCORE_BLKSIZE          (8192)
82
83 /*
84  * Format of an incore inode.
85  */
86 struct incore_inode {
87         LIST_ENTRY(incore_inode) ici_link;              /* i-nodes list link */
88         unsigned ici_revalidate                 : 1;    /* synch sys inode? */
89         struct intnl_stat ici_st;                       /* attrs */
90         struct file_identifier ici_fileid;              /* file ID */
91         void    *ici_data;                              /* file data */
92 };
93
94 /*
95  * Given pointer to inode, return pointer to incore-inode.
96  */
97 #define I2IC(ino)       ((struct incore_inode *)(ino)->i_private)
98
99 struct incore_filesys {
100         LIST_HEAD(, incore_inode) icfs_icinodes;        /* all i-nodes list */
101 };
102
103 /*
104  * Given pointer to filesys, return pointer to incore-filesys.
105  */
106 #define FS2ICFS(fs)     ((struct incore_filesys *)(fs)->fs_private)
107
108 static int _sysio_incore_fsswop_mount(const char *source,
109                                       unsigned flags,
110                                       const void *data,
111                                       struct pnode *tocover,
112                                       struct mount **mntp);
113
114 static struct fssw_ops incore_fssw_ops = {
115                 _sysio_incore_fsswop_mount
116 };
117
118 static void _sysio_incore_fsop_gone(struct filesys *fs);
119
120 static struct filesys_ops incore_fs_ops = {
121                 _sysio_incore_fsop_gone,
122 };
123
124 static int _sysio_incore_dirop_lookup(struct pnode *pno,
125                                       struct inode **inop,
126                                       struct intent *intnt,
127                                       const char *path);
128 static int _sysio_incore_inop_getattr(struct pnode *pno,
129                                       struct inode *ino,
130                                       struct intnl_stat *stbuf);
131 static int _sysio_incore_inop_setattr(struct pnode *pno,
132                                       struct inode *ino,
133                                       unsigned mask,
134                                       struct intnl_stat *stbuf);
135 static ssize_t _sysio_incore_dirop_getdirentries(struct inode *ino,
136                                            char *buf,
137                                            size_t nbytes,
138                                            _SYSIO_OFF_T *basep);
139 static int _sysio_incore_dirop_mkdir(struct pnode *pno, mode_t mode);
140 static int _sysio_incore_dirop_rmdir(struct pnode *pno);
141 static int _sysio_incore_inop_open(struct pnode *pno, int flags, mode_t mode);
142 static int _sysio_incore_inop_close(struct inode *ino);
143 static int _sysio_incore_dirop_link(struct pnode *old, struct pnode *new);
144 static int _sysio_incore_dirop_unlink(struct pnode *pno);
145 static int _sysio_incore_dirop_rename(struct pnode *old, struct pnode *new);
146 static int _sysio_incore_filop_read(struct inode *ino, struct ioctx *ioctx);
147 static int _sysio_incore_filop_write(struct inode *ino, struct ioctx *ioctx);
148 static _SYSIO_OFF_T _sysio_incore_filop_pos(struct inode *ino,
149                                             _SYSIO_OFF_T off);
150 static int _sysio_incore_filop_iodone(struct ioctx *ioctx);
151 static int _sysio_incore_filop_fcntl(struct inode *ino, 
152                                      int cmd, va_list ap, int *rtn);
153 static int _sysio_incore_inop_sync(struct inode *ino);
154 static int _sysio_incore_filop_ioctl(struct inode *ino,
155                                     unsigned long int request,
156                                     va_list ap);
157 static int _sysio_incore_dirop_mknod(struct pnode *pno, mode_t mode, dev_t dev);
158 #ifdef _HAVE_STATVFS
159 static int _sysio_incore_inop_statvfs(struct pnode *pno,
160                                       struct inode *ino,
161                                       struct intnl_statvfs *buf);
162 #endif
163 static void _sysio_incore_inop_gone(struct inode *ino);
164
165 #define _sysio_incore_dirop_symlink \
166         (int (*)(struct pnode *, const char *))_sysio_do_enosys
167 #define _sysio_incore_dirop_readlink \
168         (int (*)(struct pnode *, char *, size_t))_sysio_do_einval
169 #define _sysio_incore_dirop_read \
170         (int (*)(struct inode *, \
171                  struct ioctx *))_sysio_do_eisdir
172 #define _sysio_incore_dirop_write \
173         (int (*)(struct inode *, \
174                  struct ioctx *))_sysio_do_eisdir
175 #define _sysio_incore_dirop_pos \
176         (_SYSIO_OFF_T (*)(struct inode *, \
177                           _SYSIO_OFF_T))_sysio_do_eisdir
178 #define _sysio_incore_dirop_iodone \
179         (int (*)(struct ioctx *))_sysio_do_illop
180 #define _sysio_incore_dirop_fcntl \
181         (int (*)(struct inode *, int, va_list, int *))_sysio_do_eisdir
182 #define _sysio_incore_dirop_ioctl \
183         (int (*)(struct inode *, \
184                  unsigned long int, \
185                  va_list))_sysio_do_eisdir
186
187 static struct inode_ops _sysio_incore_dir_ops = {
188         _sysio_incore_dirop_lookup,
189         _sysio_incore_inop_getattr,
190         _sysio_incore_inop_setattr,
191         _sysio_incore_dirop_getdirentries,
192         _sysio_incore_dirop_mkdir,
193         _sysio_incore_dirop_rmdir,
194         _sysio_incore_dirop_symlink,
195         _sysio_incore_dirop_readlink,
196         _sysio_incore_inop_open,
197         _sysio_incore_inop_close,
198         _sysio_incore_dirop_link,
199         _sysio_incore_dirop_unlink,
200         _sysio_incore_dirop_rename,
201         _sysio_incore_dirop_read,
202         _sysio_incore_dirop_write,
203         _sysio_incore_dirop_pos,
204         _sysio_incore_dirop_iodone,
205         _sysio_incore_dirop_fcntl,
206         _sysio_incore_inop_sync,
207         _sysio_incore_inop_sync,
208         _sysio_incore_dirop_ioctl,
209         _sysio_incore_dirop_mknod,
210 #ifdef _HAVE_STATVFS
211         _sysio_incore_inop_statvfs,
212 #endif
213         _sysio_incore_inop_gone
214 };
215
216 #define _sysio_incore_filop_lookup \
217         (int (*)(struct pnode *, \
218                  struct inode **, \
219                  struct intent *, \
220                  const char *))_sysio_do_illop
221 #define _sysio_incore_filop_getdirentries \
222         (ssize_t (*)(struct inode *, \
223                  char *, \
224                  size_t, \
225                  _SYSIO_OFF_T *))_sysio_do_illop
226 #define _sysio_incore_filop_mkdir \
227         (int (*)(struct pnode *, mode_t))_sysio_do_illop
228 #define _sysio_incore_filop_rmdir \
229         (int (*)(struct pnode *))_sysio_do_illop
230 #define _sysio_incore_filop_symlink \
231         (int (*)(struct pnode *, const char *))_sysio_do_illop
232 #define _sysio_incore_symlinkop_readlink \
233         (int (*)(struct pnode *, char *, size_t))_sysio_do_illop
234 #define _sysio_incore_filop_link \
235         (int (*)(struct pnode *old, struct pnode *new))_sysio_do_illop
236 #define _sysio_incore_filop_unlink \
237         (int (*)(struct pnode *pno))_sysio_do_illop
238 #define _sysio_incore_filop_rename \
239         (int (*)(struct pnode *old, struct pnode *new))_sysio_do_illop
240 #define _sysio_incore_filop_mknod \
241         (int (*)(struct pnode *pno, mode_t, dev_t))_sysio_do_illop
242
243 static struct inode_ops _sysio_incore_file_ops = {
244         _sysio_incore_filop_lookup,
245         _sysio_incore_inop_getattr,
246         _sysio_incore_inop_setattr,
247         _sysio_incore_filop_getdirentries,
248         _sysio_incore_filop_mkdir,
249         _sysio_incore_filop_rmdir,
250         _sysio_incore_filop_symlink,
251         _sysio_incore_symlinkop_readlink,
252         _sysio_incore_inop_open,
253         _sysio_incore_inop_close,
254         _sysio_incore_filop_link,
255         _sysio_incore_filop_unlink,
256         _sysio_incore_filop_rename,
257         _sysio_incore_filop_read,
258         _sysio_incore_filop_write,
259         _sysio_incore_filop_pos,
260         _sysio_incore_filop_iodone,
261         _sysio_incore_filop_fcntl,
262         _sysio_incore_inop_sync,
263         _sysio_incore_inop_sync,
264         _sysio_incore_filop_ioctl,
265         _sysio_incore_filop_mknod,
266 #ifdef _HAVE_STATVFS
267         _sysio_incore_inop_statvfs,
268 #endif
269         _sysio_incore_inop_gone
270 };
271
272 static struct inode_ops _sysio_incore_dev_ops = {
273         _sysio_incore_filop_lookup,
274         _sysio_incore_inop_getattr,
275         _sysio_incore_inop_setattr,
276         _sysio_incore_filop_getdirentries,
277         _sysio_incore_filop_mkdir,
278         _sysio_incore_filop_rmdir,
279         _sysio_incore_filop_symlink,
280         _sysio_incore_symlinkop_readlink,
281         _sysio_nodev_inop_open,
282         _sysio_nodev_inop_close,
283         _sysio_incore_filop_link,
284         _sysio_incore_filop_unlink,
285         _sysio_incore_filop_rename,
286         _sysio_nodev_inop_read,
287         _sysio_nodev_inop_write,
288         _sysio_nodev_inop_pos,
289         _sysio_nodev_inop_iodone,
290         _sysio_incore_filop_fcntl,
291         _sysio_incore_inop_sync,
292         _sysio_nodev_inop_sync,
293         _sysio_nodev_inop_ioctl,
294         _sysio_incore_filop_mknod,
295 #ifdef _HAVE_STATVFS
296         _sysio_incore_inop_statvfs,
297 #endif
298         _sysio_incore_inop_gone
299 };
300
301 typedef void *(*probe_ty)(void *data, size_t len, void *arg);
302
303 /*
304  * Lookup data argument bundle record.
305  */
306 struct lookup_data {
307         struct qstr *name;                              /* desired entry name */
308         struct intnl_dirent *de;                        /* last dirent */
309         size_t  minsiz;                                 /* min hole needed */
310         struct {
311                 void    *p;                             /* best hole */
312                 size_t  len;                            /* best hole len */
313         } hole;
314 };
315
316 /*
317  * Initialize lookup data argument bundle.
318  */
319 #define INCORE_LD_INIT(ld, minsz, qs) \
320         do { \
321                 (ld)->name = (qs); \
322                 (ld)->de = NULL; \
323                 (ld)->minsiz = (minsz); \
324                 (ld)->hole.p = NULL; \
325                 (ld)->hole.len = 0; \
326         } while (0)
327
328 /*
329  * Calculate size of a directory entry given length of the entry name.
330  */
331 #define INCORE_D_RECLEN(namlen) \
332         (((size_t )&((struct intnl_dirent *)0)->d_name + \
333           (namlen) + 1 + sizeof(void *)) & \
334          ~(sizeof(void *) - 1))
335
336 /*
337  * Given mode bits, return directory entry type code.
338  */
339 #define INCORE_D_TYPEOF(m)      (((m) & S_IFMT) >> 12)
340
341 static char incore_dir_template[INCORE_D_RECLEN(1) + INCORE_D_RECLEN(2)];
342 #if 0
343 static struct intnl_dirent incore_dir_template[] = {
344         {
345                 0,
346                 INCORE_D_RECLEN(1),
347                 INCORE_D_RECLEN(1),
348                 INCORE_D_TYPEOF(S_IFDIR),
349                 { '.', '\0' }
350         },
351         {
352                 0,
353                 INCORE_D_RECLEN(1) + INCORE_D_RECLEN(2),
354                 INCORE_D_RECLEN(2),
355                 INCORE_D_TYPEOF(S_IFDIR),
356                 { '.', '.', '\0' }
357         }
358 };
359 #endif
360
361 /*
362  * Initialize this driver.
363  */
364 int
365 _sysio_incore_init()
366 {
367         struct intnl_dirent *de;
368         off_t   off;
369
370         /*
371          * Fill in the directory template.
372          */
373         de = (struct intnl_dirent *)incore_dir_template;
374 #ifdef _DIRENT_HAVE_D_OFF
375         de->d_off =
376 #endif
377             off = de->d_reclen = INCORE_D_RECLEN(1);
378         de->d_type = INCORE_D_TYPEOF(S_IFDIR);
379         de->d_name[0] = '.';
380 #ifdef _DIRENT_HAVE_D_NAMLEN
381         de->d_namlen = 1;
382 #endif
383         /*
384          * Move to entry for `..'
385          */
386         de = (struct intnl_dirent *)((char *)de + off);
387         de->d_reclen = INCORE_D_RECLEN(2);
388 #ifdef _DIRENT_HAVE_D_NAMLEN
389         de->d_namlen = 2;
390 #endif
391 #ifdef _DIRENT_HAVE_D_OFF
392         de->d_off =
393 #endif
394             off += de->d_reclen;
395         de->d_type = INCORE_D_TYPEOF(S_IFDIR);
396         de->d_name[0] = de->d_name[1] = '.';
397         de->d_name[2] = ' ';
398
399         return _sysio_fssw_register("incore", &incore_fssw_ops);
400 }
401
402 static ino_t
403 incore_inum_alloc()
404 {
405         static ino_t nxtnum = 1;
406
407         assert(nxtnum);
408         return nxtnum++;
409 }
410
411 static struct incore_inode *
412 incore_i_alloc(struct incore_filesys *icfs, struct intnl_stat *st)
413 {
414         struct incore_inode *icino;
415
416         assert(st->st_ino);
417         assert(!st->st_size);
418
419         icino = malloc(sizeof(struct incore_inode));
420         if (!icino)
421                 return NULL;
422         icino->ici_revalidate = 0;
423         icino->ici_st = *st;
424         icino->ici_fileid.fid_data = &icino->ici_st.st_ino;
425         icino->ici_fileid.fid_len = sizeof(icino->ici_st.st_ino);
426         icino->ici_data = NULL;
427
428         LIST_INSERT_HEAD(&icfs->icfs_icinodes, icino, ici_link);
429
430         return icino;
431 }
432
433 static int
434 incore_trunc(struct incore_inode *icino, _SYSIO_OFF_T size, int clear)
435 {
436         _SYSIO_OFF_T n;
437         void    *p;
438
439         if (size < 0) 
440                 return -EINVAL;
441         n = size;
442         if (!size) {
443                 if (icino->ici_data) {
444                         free(icino->ici_data);
445                         icino->ici_data = NULL;
446                 }
447                 n = 0;
448                 goto out;
449         }
450         p = realloc(icino->ici_data, (size_t )n);
451         if (!p)
452                 return -ENOSPC;
453         icino->ici_data = p;
454         if (clear && n > icino->ici_st.st_size)
455                 (void )memset((char *)icino->ici_data + icino->ici_st.st_size,
456                               0,
457                               (size_t )(n - icino->ici_st.st_size));
458 out:
459         icino->ici_st.st_size = n;
460         icino->ici_st.st_blocks =
461             (n + icino->ici_st.st_blksize - 1) / icino->ici_st.st_blksize;
462         icino->ici_st.st_mtime = time(NULL);
463         return 0;
464 }
465
466 static void
467 incore_i_destroy(struct incore_inode *icino)
468 {
469
470         LIST_REMOVE(icino, ici_link);
471         (void )incore_trunc(icino, 0, 0);
472         free(icino);
473 }
474
475 static struct incore_inode *
476 incore_directory_new(struct incore_filesys *icfs,
477                      struct incore_inode *parent,
478                      struct intnl_stat *st)
479 {
480         struct incore_inode *icino;
481         int     err;
482         struct intnl_dirent *de;
483
484         icino = incore_i_alloc(icfs, st);
485         if (!icino)
486                 return NULL;
487
488         if (!parent)
489                 parent = icino;                         /* root */
490
491         /*
492          * Allocate and init directory data.
493          */
494         err = incore_trunc(icino, sizeof(incore_dir_template), 1);
495         if (err) {
496                 incore_i_destroy(icino);
497                 return NULL;
498         }
499         (void )memcpy(icino->ici_data,
500                       &incore_dir_template,
501                       sizeof(incore_dir_template));
502         de = icino->ici_data;
503         de->d_ino = st->st_ino;
504         de =
505             (struct intnl_dirent *)((char *)de +
506 #ifdef _DIRENT_HAVE_D_OFF
507                                     de->d_off
508 #else
509                                     de->d_reclen
510 #endif
511                                     );
512         de->d_ino = parent->ici_st.st_ino;
513
514         /*
515          * Set creation time to modify time set by truncate.
516          */
517         st->st_ctime = st->st_mtime;
518
519         return icino;
520 }
521
522 static int
523 _sysio_incore_fsswop_mount(const char *source,
524                            unsigned flags,
525                            const void *data __IS_UNUSED,
526                            struct pnode *tocover,
527                            struct mount **mntp)
528 {
529         char    *cp;
530         unsigned long ul;
531         long    l;
532         mode_t  mode;
533         uid_t   uid;
534         gid_t   gid;
535         int     err;
536         dev_t   dev;
537         struct intnl_stat stat;
538         struct incore_filesys *icfs;
539         ino_t   inum;
540         struct incore_inode *icino;
541         struct filesys *fs;
542         struct inode *rooti;
543         struct pnode_base *rootpb;
544         struct mount *mnt;
545         static struct qstr noname = { NULL, 0, 0 };
546
547         /*
548          * Source is a specification for the root attributes of this
549          * new file system in the format:
550          *
551          * <permissions>+<owner>+<group>
552          */
553         ul = strtoul(source, &cp, 0);
554         mode = (mode_t )ul & 07777;
555         if (*cp != '+' ||
556             (ul == ULONG_MAX && errno == ERANGE) ||
557             (unsigned long)mode != ul ||
558             mode > 07777)
559                 return -EINVAL;
560         source = cp;
561         l = strtol(source, &cp, 0);
562         uid = (uid_t )l;
563         if (*cp != '+' ||
564             ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) ||
565             (long )uid != l)
566                 return -EINVAL;
567         source = cp;
568         l = strtol(source, &cp, 0);
569         gid = (gid_t )l;
570         if (*cp ||
571             ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) ||
572             (long )gid != l)
573                 return -EINVAL;
574
575         err = 0;
576
577         dev = _sysio_dev_alloc();
578
579         mnt = NULL;
580         rootpb = NULL;
581         rooti = NULL;
582         fs = NULL;
583         icino = NULL;
584         icfs = NULL;
585
586         /*
587          * Create new FS.
588          */
589         icfs = malloc(sizeof(struct incore_filesys));
590         if (!icfs) {
591                 err = -ENOMEM;
592                 goto error;
593         }
594         (void )memset(icfs, 0, sizeof(struct incore_filesys));
595         LIST_INIT(&icfs->icfs_icinodes);
596
597         /*
598          * Create root i-node.
599          */
600         (void )memset(&stat, 0, sizeof(stat));
601         stat.st_dev = dev;
602         inum = incore_inum_alloc();
603 #ifdef HAVE__ST_INO
604         stat.__st_ino = inum; 
605 #endif
606         stat.st_mode = S_IFDIR | (mode & 07777);
607         stat.st_nlink = 2;
608         stat.st_uid = uid;
609         stat.st_gid = gid;
610         stat.st_size = 0;
611         stat.st_blksize = INCORE_BLKSIZE;
612         stat.st_blocks = 0;
613         stat.st_ctime = stat.st_mtime = stat.st_atime = 0;
614         stat.st_ino = inum;
615         icino = incore_directory_new(icfs, NULL, &stat);
616         if (!icino)
617                 return -ENOSPC;
618         icino->ici_st.st_atime = icino->ici_st.st_mtime;
619
620         fs =
621             _sysio_fs_new(&incore_fs_ops,
622                           (flags & MOUNT_F_RO) ? FS_F_RO : 0,
623                           icfs);
624         if (!fs) {
625                 err = -ENOMEM;
626                 goto error;
627         }
628
629         /*
630          * Create root for system.
631          *
632          * Persistent across remounts because we ask for immunity.
633          */
634         rooti =
635             _sysio_i_new(fs,
636                          &icino->ici_fileid,
637                          icino->ici_st.st_mode,
638                          0,
639                          1,
640                          &_sysio_incore_dir_ops,
641                          icino);
642         if (!rooti) {
643                 err = -ENOMEM;
644                 goto error;
645         }
646         rootpb = _sysio_pb_new(&noname, NULL, rooti);
647         if (!rootpb) {
648                 err = -ENOMEM;
649                 goto error;
650         }
651
652         /*
653          * Have path-node specified by the given source argument. Let the
654          * system finish the job, now.
655          */
656         mnt = NULL;
657         err =
658             _sysio_do_mount(fs,
659                             rootpb,
660                             flags,
661                             tocover,
662                             &mnt);
663         if (err)
664                 goto error;
665
666         *mntp = mnt;
667
668         goto out;
669
670 error:
671         if (mnt && _sysio_do_unmount(mnt) != 0)
672                         abort();
673         if (rootpb) {
674                 _sysio_pb_gone(rootpb);
675                 rooti = NULL;
676         }
677         if (rooti)
678                 I_RELE(rooti);
679         if (fs) {
680                 FS_RELE(fs);
681                 goto out;
682         }
683         if (icino) {
684                 incore_i_destroy(icino);
685                 goto out;
686         }
687         if (icfs) {
688                 free(icfs);
689                 goto out;
690         }
691
692 out:
693         return err;
694 }
695
696 static void
697 _sysio_incore_fsop_gone(struct filesys *fs)
698 {
699         struct incore_filesys *icfs;
700         struct incore_inode *icino, *oicino;
701
702         icfs = FS2ICFS(fs);
703
704         /*
705          * Free up i-node resource associated with this file system.
706          */
707         icino = icfs->icfs_icinodes.lh_first;
708         while (icino) {
709                 oicino = icino;
710                 icino = icino->ici_link.le_next;
711                 incore_i_destroy(oicino);
712         }
713
714         /*
715          * Free the FS record.
716          */
717         free(icfs);
718 }
719
720 /*
721  * A directory search engine. Various functions are carried out by
722  * supplying appropriate callback functions.
723  *
724  * The two arguments, entry and hole, are called, if not null, for each
725  * directory entry and hole, respectively.
726  */
727 static void *
728 incore_directory_probe(void *data,
729                        size_t siz,
730                        _SYSIO_OFF_T origin
731 #ifndef _DIRENT_HAVE_D_OFF
732                                 __IS_UNUSED
733 #endif
734                        ,
735                        probe_ty entry,
736                        probe_ty hole,
737                        void *arg)
738 {
739         struct intnl_dirent *de;
740         void    *p;
741         size_t  n;
742
743         de = data;
744         for (;;) {
745 #ifdef _DIRENT_HAVE_D_OFF
746                 assert(de->d_off);
747 #else
748                 assert(de->d_reclen);
749 #endif
750                 if (entry && (p = (*entry)(de, de->d_reclen, arg)))
751                         return p;
752                 n =
753 #ifdef _DIRENT_HAVE_D_OFF
754                     de->d_off - origin;
755 #else
756                     ((void *)de - data) + de->d_reclen;
757 #endif
758                 if (hole) {
759                         p = (*hole)((void *)de, de->d_reclen, arg);
760                         if (p)
761                                 return p;
762                 }
763                 if (n >= siz)
764                         break;
765                 de = (struct intnl_dirent *)((char *)data + n);
766         }
767
768         return NULL;
769 }
770
771 static struct intnl_dirent *
772 incore_directory_match(struct intnl_dirent *de,
773                        size_t reclen __IS_UNUSED,
774                        struct lookup_data *ld)
775 {
776
777 #if defined(BSD) || defined(REDSTORM)
778         if (IFTODT(de->d_type) == DT_WHT)
779                 return NULL;
780 #endif
781         if (
782 #ifdef _DIRENT_HAVE_D_NAMLEN
783             ld->name->len == de->d_namlen &&
784 #endif
785             strncmp(de->d_name, ld->name->name, ld->name->len) == 0)
786                 return de;
787         ld->de = de;
788         return NULL;
789 }
790
791 static int
792 _sysio_incore_dirop_lookup(struct pnode *pno,
793                            struct inode **inop,
794                            struct intent *intnt __IS_UNUSED,
795                            const char *path __IS_UNUSED)
796 {
797         struct inode *ino;
798         struct intnl_dirent *de;
799         struct incore_inode *icino;
800         struct lookup_data lookup_data;
801         struct file_identifier fileid;
802 #ifdef notdef
803         struct inode_ops *ops;
804 #endif
805
806         /*
807          * Revalidate?
808          */
809         if (*inop) {
810                 icino = I2IC(*inop);
811                 assert(icino);
812                 if (icino->ici_revalidate) {
813                         (*inop)->i_mode = icino->ici_st.st_mode;
814                         icino->ici_revalidate = 0;
815                 }
816                 return 0;
817         }
818
819         ino = pno->p_parent->p_base->pb_ino;
820         icino = I2IC(ino);
821         INCORE_LD_INIT(&lookup_data,
822                        ULONG_MAX,
823                        &pno->p_base->pb_name);
824         de =
825             incore_directory_probe(icino->ici_data,
826                                    icino->ici_st.st_size,
827                                    0,
828                                    (probe_ty )incore_directory_match,
829                                    NULL,
830                                    &lookup_data);
831         if (!de)
832                 return -ENOENT;
833
834         fileid.fid_data = &de->d_ino;
835         fileid.fid_len = sizeof(de->d_ino);
836         ino =
837             _sysio_i_find(ino->i_fs, &fileid);
838 #ifdef notdef
839         if (ino)
840                 goto out;
841         icino->ici_fileid.fid_data = &icino->ici_st.st_ino;
842         icino->ici_fileid.fid_len = sizeof(icino->ici_st.st_ino);
843         ops = NULL;
844         switch (icino->ici_st.st_mode & S_IFMT) {
845         case S_IFDIR:
846                 ops = &_sysio_incore_dir_ops;
847                 break;
848         case S_IFREG:
849                 ops = &_sysio_incore_file_ops;
850                 break;
851         default:
852                 break;
853         }
854         if (!ops)
855                 abort();
856         ino =
857             _sysio_i_new(ino->i_fs,
858                          &icino->ici_fileid,
859                          icino->ici_st.st_mode,
860                          0,
861                          1,
862                          ops,
863                          icino);
864 #endif
865         if (!ino)
866                 return -ENOMEM;
867
868 #ifdef notdef
869 out:
870 #endif
871         *inop = ino;
872         return 0;
873 }
874
875 static int
876 _sysio_incore_inop_getattr(struct pnode *pno,
877                            struct inode *ino,
878                            struct intnl_stat *stbuf)
879 {
880         struct incore_inode *icino;
881
882         if (!ino)
883                 ino = pno->p_base->pb_ino;
884         icino = I2IC(ino);
885         *stbuf = icino->ici_st;
886         return 0;
887 }
888
889 static int
890 _sysio_incore_inop_setattr(struct pnode *pno,
891                            struct inode *ino,
892                            unsigned mask,
893                            struct intnl_stat *stbuf)
894 {
895         struct incore_inode *icino;
896         int     err;
897
898         if (!ino)
899                 ino = pno->p_base->pb_ino;
900         if (!ino)
901                 return -EBADF;
902         icino = I2IC(ino);
903
904         err = 0;
905         if (mask & SETATTR_LEN) {
906                 err = incore_trunc(icino, stbuf->st_size, 1);
907                 if (err)
908                         goto out;
909                 mask &= ~SETATTR_LEN;
910         }
911         if (mask & SETATTR_MODE) {
912                 icino->ici_st.st_mode =
913                     (icino->ici_st.st_mode & S_IFMT) | (stbuf->st_mode & 07777);
914                 icino->ici_revalidate = 1;
915         }
916         if (mask & SETATTR_MTIME)
917                 icino->ici_st.st_mtime = stbuf->st_mtime;
918         if (mask & SETATTR_ATIME)
919                 icino->ici_st.st_atime = stbuf->st_atime;
920         if (mask & SETATTR_UID)
921                 icino->ici_st.st_uid = stbuf->st_uid;
922         if (mask & SETATTR_GID)
923                 icino->ici_st.st_gid = stbuf->st_gid;
924         icino->ici_st.st_ctime = time(NULL);
925
926 out:
927         return err;
928 }
929
930 static void *
931 incore_directory_position(struct intnl_dirent *de,
932                           size_t reclen __IS_UNUSED,
933                           void *p)
934 {
935
936         return (void *)de >= p ? de : NULL;
937 }
938
939 struct copy_info {
940         void    *data;
941         size_t  nbytes;
942 };
943
944 /*
945  * Eumeration callback.
946  *
947  * Note:
948  * On those systems supporting white-out entries, they are returned. On
949  * systems without, they are not.
950  */
951 static void *
952 incore_directory_enumerate(struct intnl_dirent *de,
953                            size_t reclen,
954                            struct copy_info *cinfo) {
955
956         if (reclen > cinfo->nbytes)
957                 return de;
958         (void *)memcpy(cinfo->data, de, reclen);
959         cinfo->data = (char *)cinfo->data + reclen;
960         cinfo->nbytes -= reclen;
961         return NULL;
962 }
963
964 static ssize_t
965 _sysio_incore_dirop_getdirentries(struct inode *ino,
966                                  char *buf,
967                                  size_t nbytes,
968                                  _SYSIO_OFF_T *basep)
969 {
970         struct incore_inode *icino = I2IC(ino);
971         off_t   off;
972         struct intnl_dirent *de;
973         struct copy_info copy_info;
974
975         if (*basep > icino->ici_st.st_size)
976                 return 0;
977
978         de =
979             incore_directory_probe(icino->ici_data,
980                                    icino->ici_st.st_size,
981                                    *basep,
982                                    (probe_ty )incore_directory_position,
983                                    NULL,
984                                    (char *)icino->ici_data + *basep);
985         if (!de) {
986                 /*
987                  * Past EOF.
988                  */
989                 *basep = 0;
990                 return 0;
991         }
992
993         copy_info.data = buf;
994         copy_info.nbytes = nbytes;
995         off = (char *)de - (char *)icino->ici_data;
996         de =
997             incore_directory_probe(de,
998                                    icino->ici_st.st_size - off,
999                                    off,
1000                                    (probe_ty )incore_directory_enumerate,
1001                                    NULL,
1002                                    &copy_info);
1003         nbytes -= copy_info.nbytes;
1004         icino->ici_st.st_atime = time(NULL);
1005         if (!nbytes)
1006                 return -EOVERFLOW;
1007         *basep = nbytes;
1008         return (ssize_t )nbytes;
1009 }
1010
1011 static struct intnl_dirent *
1012 incore_directory_best_fit(void *data, size_t len, struct lookup_data *ld)
1013 {
1014
1015         if (!ld->hole.len || len < ld->hole.len) {
1016                 ld->hole.p = data;
1017                 ld->hole.len = len;
1018         }
1019
1020         return NULL;
1021 }
1022
1023 static int
1024 incore_directory_insert(struct incore_inode *parent,
1025                         struct qstr *name,
1026                         ino_t inum,
1027                         unsigned char type)
1028 {
1029         size_t  reclen;
1030         struct lookup_data lookup_data;
1031         struct intnl_dirent *de;
1032         size_t  xt;
1033         size_t  n;
1034         size_t  r;
1035
1036         reclen = INCORE_D_RECLEN(name->len);
1037         INCORE_LD_INIT(&lookup_data, reclen, name);
1038         de =
1039             incore_directory_probe(parent->ici_data,
1040                                    parent->ici_st.st_size,
1041                                    0,
1042                                    (probe_ty )incore_directory_match,
1043                                    (probe_ty )incore_directory_best_fit,
1044                                    &lookup_data);
1045         if (de)
1046                 return -EEXIST;
1047         de = lookup_data.de;
1048         xt = (char *)lookup_data.de - (char *)parent->ici_data;
1049         n =
1050 #ifdef _DIRENT_HAVE_D_OFF
1051             de->d_off;
1052 #else
1053             xt + de->d_reclen;
1054 #endif
1055         r =
1056 #ifdef _DIRENT_HAVE_D_OFF
1057             de->d_reclen;
1058 #else
1059             INCORE_D_RECLEN(de->d_namlen);
1060 #endif
1061         if (!parent->ici_st.st_size ||
1062             xt + r + reclen > (size_t )parent->ici_st.st_size) {
1063                 int     err;
1064
1065                 err = incore_trunc(parent, xt + r + reclen, 1);
1066                 if (err)
1067                         return err;
1068                 de = (struct intnl_dirent *)((char *)parent->ici_data + xt);
1069                 n = parent->ici_st.st_size;
1070         }
1071
1072 #ifdef _DIRENT_HAVE_D_OFF
1073         de->d_off = xt + r;                             /* trim */
1074 #else
1075         de->d_reclen = r;
1076 #endif
1077         de = (struct intnl_dirent *)((char *)de + r);                           /* reposition */
1078         xt += r;
1079
1080 #ifndef _DIRENT_HAVE_D_OFF
1081         /*
1082          * Will we split this hole or use all of it?
1083          */
1084         if (lookup_data.hole.len - reclen &&
1085             lookup_data.hole.len - reclen <= INCORE_D_RECLEN(1))
1086                 reclen = lookup_data.hole.len;
1087 #endif
1088
1089         /*
1090          * Insert new.
1091          */
1092         de->d_ino = inum;
1093 #ifdef _DIRENT_HAVE_D_OFF
1094         de->d_off = n;
1095 #endif
1096         de->d_reclen = reclen;
1097         de->d_type = type;
1098         (void )memcpy(de->d_name, name->name, name->len);
1099 #ifdef _DIRENT_HAVE_D_NAMLEN
1100         de->d_namlen = name->len;
1101 #endif
1102
1103 #ifndef _DIRENT_HAVE_D_OFF
1104         xt += reclen;
1105         if (n - xt) {
1106                 /*
1107                  * White-out remaining part of the hole.
1108                  */
1109                 (void *)de += reclen;
1110                 de->d_ino = 0;
1111                 de->d_reclen = n - xt;
1112                 de->d_type = DT_WHT;
1113                 de->d_namlen = 0;
1114         }
1115 #endif
1116
1117         /*
1118          * Update attributes to reflect the new entry.
1119          */
1120         parent->ici_st.st_nlink++;
1121         assert(parent->ici_st.st_nlink);
1122         parent->ici_st.st_atime = parent->ici_st.st_mtime = time(NULL);
1123
1124         return 0;
1125 }
1126
1127 static int
1128 _sysio_incore_dirop_mkdir(struct pnode *pno, mode_t mode)
1129 {
1130         struct intnl_stat stat;
1131         struct incore_inode *icino, *parent;
1132         ino_t   inum;
1133         int     err;
1134         struct intnl_dirent *de = NULL;
1135         struct inode *ino;
1136
1137         ino = pno->p_parent->p_base->pb_ino;
1138         parent = I2IC(ino);
1139
1140         if (!S_ISDIR(parent->ici_st.st_mode))
1141                 return -ENOTDIR;
1142
1143         (void )memset(&stat, 0, sizeof(stat));
1144         stat.st_dev = pno->p_parent->p_base->pb_ino->i_fs->fs_dev;
1145         inum = incore_inum_alloc();
1146 #ifdef HAVE__ST_INO
1147         stat.__st_ino = inum;
1148 #endif
1149         stat.st_mode = S_IFDIR | (mode & 07777);
1150         stat.st_nlink = 2;
1151         stat.st_uid = getuid();
1152         stat.st_gid = getgid();
1153         stat.st_size = 0;
1154         stat.st_blksize = 4096;
1155         stat.st_blocks = 0;
1156         stat.st_ctime = stat.st_mtime = stat.st_atime = 0;
1157         stat.st_ino = inum;
1158         icino = incore_directory_new(FS2ICFS(ino->i_fs), parent, &stat);
1159         if (!icino)
1160                 return -ENOSPC;
1161
1162         /*
1163          * Tell the system about the new inode.
1164          *
1165          * Persistent across remounts because we ask for immunity.
1166          */
1167         ino =
1168             _sysio_i_new(pno->p_parent->p_base->pb_ino->i_fs,
1169                          &icino->ici_fileid,
1170                          stat.st_mode,
1171                          0,
1172                          1,
1173                          &_sysio_incore_dir_ops,
1174                          icino);
1175         if (!ino) {
1176                 incore_i_destroy(icino);
1177                 return -ENOMEM;
1178         }
1179
1180         /*
1181          * Insert into parent.
1182          */
1183         err =
1184             incore_directory_insert(parent,
1185                                     &pno->p_base->pb_name,
1186                                     stat.st_ino,
1187                                     INCORE_D_TYPEOF(S_IFDIR));
1188
1189         if (err) {
1190                 de->d_ino = 0;                          /* bad parent */
1191                 I_RELE(ino);
1192                 _sysio_i_gone(ino);
1193                 return err;
1194         }
1195
1196         pno->p_base->pb_ino = ino;
1197         return 0;
1198 }
1199
1200 static int
1201 incore_unlink_entry(struct incore_inode *icino,
1202                     struct qstr *name)
1203 {
1204         struct lookup_data lookup_data;
1205         struct intnl_dirent *de;
1206         size_t  reclen;
1207 #ifdef _DIRENT_HAVE_D_OFF
1208         size_t  off;
1209 #endif
1210
1211         if (!S_ISDIR(icino->ici_st.st_mode))
1212                 return -ENOTDIR;
1213
1214         INCORE_LD_INIT(&lookup_data, 0, name);
1215         de =
1216             incore_directory_probe(icino->ici_data,
1217                                    icino->ici_st.st_size,
1218                                    0,
1219                                    (probe_ty )incore_directory_match,
1220                                    NULL,
1221                                    &lookup_data);
1222         if (!de)
1223                 return -ENOENT;
1224         assert((size_t )((char *)de - (char *)icino->ici_data) >=
1225                sizeof(incore_dir_template));
1226 #ifndef _DIRENT_HAVE_D_OFF
1227         reclen = de->d_reclen;
1228 #else
1229         off = de->d_off;
1230         reclen = off - ((char *)de - (char *)icino->ici_data);
1231 #endif
1232         (void )memset(de, 0, reclen);
1233 #ifndef _DIRENT_HAVE_D_OFF
1234         de->d_type = (__uint8_t )DTTOIF(DT_WHT);
1235         de->d_reclen = reclen;
1236 #else
1237         lookup_data.de->d_off = off;
1238 #endif
1239
1240         /*
1241          * Adjust link count.
1242          */
1243         assert(icino->ici_st.st_nlink > 2);
1244         icino->ici_st.st_nlink--;
1245
1246         return 0;
1247 }
1248
1249 static int
1250 _sysio_incore_dirop_rmdir(struct pnode *pno)
1251 {
1252         struct inode *ino = pno->p_base->pb_ino;
1253         struct incore_inode *icino = I2IC(ino);
1254         int     err;
1255
1256         if (!pno->p_base->pb_name.len ||
1257             (pno->p_base->pb_name.name[0] == '.' &&
1258              (pno->p_base->pb_name.len == 1 ||
1259               (pno->p_base->pb_name.len == 2 &&
1260                pno->p_base->pb_name.name[1] == '.'))))
1261                 return -EINVAL;
1262
1263         if (!S_ISDIR(icino->ici_st.st_mode))
1264                 return -ENOTDIR;
1265
1266         if (icino->ici_st.st_nlink > 2)
1267                 return -ENOTEMPTY;
1268
1269         pno->p_base->pb_ino = NULL;
1270         err =
1271             incore_unlink_entry(I2IC(pno->p_parent->p_base->pb_ino),
1272                                 &pno->p_base->pb_name);
1273         return err;
1274 }
1275
1276 static int
1277 incore_create(struct pnode *pno, struct intnl_stat *st)
1278 {
1279         struct inode *dino, *ino;
1280         struct incore_inode *icino;
1281         int     err;
1282
1283         dino = pno->p_parent->p_base->pb_ino;
1284         assert(dino);
1285
1286         icino = incore_i_alloc(FS2ICFS(dino->i_fs), st);
1287         if (!icino)
1288                 return -ENOSPC;
1289
1290         /*
1291          * Tell the system about the new inode.
1292          */
1293         ino =
1294             _sysio_i_new(dino->i_fs,
1295                          &icino->ici_fileid,
1296                          st->st_mode,
1297                          st->st_rdev,
1298                          1,
1299                          S_ISREG(st->st_mode)
1300                            ? &_sysio_incore_file_ops
1301                            : &_sysio_incore_dev_ops,
1302                          icino);
1303         if (!ino) {
1304                 incore_i_destroy(icino);
1305                 return -ENOMEM;
1306         }
1307
1308         /*
1309          * Insert into parent.
1310          */
1311         err =
1312             incore_directory_insert(I2IC(dino),
1313                                     &pno->p_base->pb_name,
1314                                     st->st_ino,
1315                                     INCORE_D_TYPEOF(icino->ici_st.st_mode));
1316         if (err) {
1317                 I_RELE(ino);
1318                 _sysio_i_gone(ino);
1319                 return err;
1320         }
1321
1322         pno->p_base->pb_ino = ino;
1323         return 0;
1324 }
1325
1326 static int
1327 _sysio_incore_inop_open(struct pnode *pno, int flags __IS_UNUSED, mode_t mode)
1328 {
1329         struct intnl_stat stat;
1330         ino_t   inum;
1331
1332         /*
1333          * File exists. Nothing to do.
1334          */
1335         if (pno->p_base->pb_ino)
1336                 return 0;
1337
1338         /*
1339          * Must create a new, regular, file.
1340          */
1341         (void )memset(&stat, 0, sizeof(stat));
1342         stat.st_dev = pno->p_parent->p_base->pb_ino->i_fs->fs_dev;
1343         inum = incore_inum_alloc();
1344 #ifdef HAVE__ST_INO
1345         stat.__st_ino = inum;
1346 #endif
1347         stat.st_mode = S_IFREG | (mode & 07777);
1348         stat.st_nlink = 1;
1349         stat.st_uid = getuid();
1350         stat.st_gid = getgid();
1351         stat.st_rdev = 0;
1352         stat.st_size = 0;
1353         stat.st_blksize = 4096;
1354         stat.st_blocks = 0;
1355         stat.st_ctime = stat.st_mtime = stat.st_atime = 0;
1356         stat.st_ino = inum;
1357
1358         return incore_create(pno, &stat);
1359 }
1360
1361 static int
1362 _sysio_incore_inop_close(struct inode *ino __IS_UNUSED)
1363 {
1364
1365         return 0;
1366 }
1367
1368 static int
1369 _sysio_incore_dirop_link(struct pnode *old, struct pnode *new)
1370 {
1371         struct incore_inode *icino = I2IC(old->p_base->pb_ino);
1372         int     err;
1373
1374         assert(!new->p_base->pb_ino);
1375         assert(!S_ISDIR(old->p_base->pb_ino->i_mode));
1376
1377         /*
1378          * Can bump the link count?
1379          */
1380         if (!(icino->ici_st.st_nlink + 1))
1381                 return -EMLINK;
1382         /*
1383          * Insert into parent.
1384          */
1385         err =
1386             incore_directory_insert(I2IC(new->p_parent->p_base->pb_ino),
1387                                     &new->p_base->pb_name,
1388                                     icino->ici_st.st_ino,
1389                                     INCORE_D_TYPEOF(icino->ici_st.st_mode));
1390         if (err)
1391                 return err;
1392         /*
1393          * Bump the link count.
1394          */
1395         icino->ici_st.st_nlink++;
1396
1397         return 0;
1398 }
1399
1400 static int
1401 _sysio_incore_dirop_rename(struct pnode *old, struct pnode *new)
1402 {
1403         int     err;
1404         struct incore_inode *icino = I2IC(old->p_base->pb_ino);
1405
1406         if (new->p_base->pb_ino) {
1407                 /*
1408                  * Have to kill off the target first.
1409                  */
1410                 if (S_ISDIR(I2IC(new->p_base->pb_ino)->ici_st.st_mode) &&
1411                     I2IC(new->p_base->pb_ino)->ici_st.st_nlink > 2)
1412                         return -ENOTEMPTY;
1413                 err =
1414                     incore_unlink_entry(I2IC(new->p_parent->p_base->pb_ino),
1415                                         &new->p_base->pb_name);
1416                 if (err)
1417                         return err;
1418         }
1419
1420         /*
1421          * Insert into new parent.
1422          */
1423         err =
1424             incore_directory_insert(I2IC(new->p_parent->p_base->pb_ino),
1425                                     &new->p_base->pb_name,
1426                                     icino->ici_st.st_ino,
1427                                     INCORE_D_TYPEOF(icino->ici_st.st_mode));
1428         if (err)
1429                 abort();
1430         /*
1431          * Remove from the old parent.
1432          */
1433         err =
1434             incore_unlink_entry(I2IC(old->p_parent->p_base->pb_ino),
1435                                 &old->p_base->pb_name);
1436         if (err)
1437                 abort();
1438
1439         if (S_ISDIR(icino->ici_st.st_mode)) {
1440                 struct intnl_dirent *de;
1441
1442                 /*
1443                  * We moved a directory. The entry for `..' must be corrected.
1444                  */
1445                 de = icino->ici_data;
1446                 de++;
1447                 assert(strcmp(de->d_name, "..") == 0);
1448                 de->d_ino = I2IC(new->p_parent->p_base->pb_ino)->ici_st.st_ino;
1449         }
1450         return 0;
1451 }
1452
1453 static int
1454 _sysio_incore_dirop_unlink(struct pnode *pno)
1455 {
1456         struct inode *ino = pno->p_base->pb_ino;
1457         struct incore_inode *icino = I2IC(ino);
1458         int     err;
1459
1460         if (S_ISDIR(icino->ici_st.st_mode))
1461                 return -EISDIR;
1462
1463         err =
1464             incore_unlink_entry(I2IC(pno->p_parent->p_base->pb_ino),
1465                                 &pno->p_base->pb_name);
1466         return err;
1467 }
1468
1469 static int
1470 doio(ssize_t (*f)(void *, size_t, _SYSIO_OFF_T, struct incore_inode *),
1471      struct inode *ino,
1472      struct ioctx *ioctx)
1473 {
1474
1475         ioctx->ioctx_cc =
1476             _sysio_doio(ioctx->ioctx_xtv, ioctx->ioctx_xtvlen,
1477                         ioctx->ioctx_iov, ioctx->ioctx_iovlen,
1478                         (ssize_t (*)(void *, size_t, _SYSIO_OFF_T, void *))f,
1479                         I2IC(ino));
1480         if (ioctx->ioctx_cc  < 0) {
1481                 ioctx->ioctx_errno = -ioctx->ioctx_cc;
1482                 ioctx->ioctx_cc = -1;
1483         }
1484         ioctx->ioctx_done = 1;
1485
1486         return 0;
1487 }
1488
1489 static ssize_t
1490 incore_read(void *buf, size_t nbytes,
1491             _SYSIO_OFF_T off,
1492             struct incore_inode *icino)
1493 {
1494         size_t  n;
1495
1496         if (off < 0)
1497                 return -EINVAL;
1498         if (!nbytes || off > icino->ici_st.st_size)
1499                 return 0;
1500         n = icino->ici_st.st_size - (size_t )off;
1501         if (n > nbytes)
1502                 n = nbytes;
1503         (void )memcpy(buf, (char *)icino->ici_data + off, (size_t )n);
1504
1505         return (ssize_t )n;
1506 }
1507
1508 static int
1509 _sysio_incore_filop_read(struct inode *ino, struct ioctx *ioctx)
1510 {
1511         
1512
1513         return doio(incore_read, ino, ioctx);
1514 }
1515
1516 static ssize_t
1517 incore_write(const void *buf, size_t nbytes,
1518              _SYSIO_OFF_T off,
1519              struct incore_inode *icino)
1520 {
1521         _SYSIO_OFF_T pos;
1522
1523         if (off < 0)
1524                 return -EINVAL;
1525         if (!nbytes || off > icino->ici_st.st_size)
1526                 return 0;
1527         pos = off + nbytes;
1528         if (off && pos <= off) {
1529                 /*
1530                  * It's all or nothing. We won't write just part of
1531                  * the buffer.
1532                  */
1533                 return -EFBIG;
1534         }
1535         if (pos > icino->ici_st.st_size) {
1536                 int     err;
1537
1538                 err = incore_trunc(icino, (size_t )pos, 0);
1539                 if (err)
1540                         return err;
1541         }
1542         (void )memcpy((char *)icino->ici_data + off, buf, nbytes);
1543
1544         return (ssize_t )nbytes;
1545 }
1546
1547 static int
1548 _sysio_incore_filop_write(struct inode *ino, struct ioctx *ioctx)
1549 {
1550
1551         return doio((ssize_t (*)(void *, size_t,
1552                                  _SYSIO_OFF_T,
1553                                  struct incore_inode *))incore_write,
1554                     ino,
1555                     ioctx);
1556 }
1557
1558 static _SYSIO_OFF_T
1559 _sysio_incore_filop_pos(struct inode *ino __IS_UNUSED, _SYSIO_OFF_T off)
1560 {
1561
1562         return off;
1563 }
1564
1565 static int
1566 _sysio_incore_filop_iodone(struct ioctx *iocp __IS_UNUSED)
1567 {
1568
1569         /*
1570          * It's always done in this driver. It completed when posted.
1571          */
1572         return 1;
1573 }
1574
1575 static int
1576 _sysio_incore_filop_fcntl(struct inode *ino __IS_UNUSED,
1577                           int cmd __IS_UNUSED,
1578                           va_list ap __IS_UNUSED,
1579                           int *rtn)
1580 {
1581
1582         /*
1583          * No fcntl's supported.
1584          */
1585         *rtn = -1;
1586         return -ENOTTY;
1587 }
1588
1589 static int
1590 _sysio_incore_inop_sync(struct inode *ino __IS_UNUSED)
1591 {
1592
1593         /*
1594          * With what?
1595          */
1596         return 0;
1597 }
1598
1599 static int
1600 _sysio_incore_filop_ioctl(struct inode *ino __IS_UNUSED,
1601                           unsigned long int request __IS_UNUSED,
1602                           va_list ap __IS_UNUSED)
1603 {
1604
1605         /*
1606          * No ioctl's supported.
1607          */
1608         return -ENOTTY;
1609 }
1610
1611 static int
1612 _sysio_incore_dirop_mknod(struct pnode *pno, mode_t mode, dev_t dev)
1613 {
1614         mode_t  m;
1615         struct intnl_stat stat;
1616         ino_t   inum;
1617
1618         assert(!pno->p_base->pb_ino);
1619
1620         m = mode & S_IFMT;
1621         if (S_ISCHR(m))
1622                 m &= ~S_IFCHR;
1623         else if (S_ISFIFO(m))
1624                 m &= ~S_IFIFO;
1625         else if (S_ISBLK(m))
1626                 m &= ~S_IFCHR;
1627         else
1628                 return -EINVAL;
1629         if (m)
1630                 return -EINVAL;
1631
1632         /*
1633          * Initialize attributes.
1634          */
1635         (void )memset(&stat, 0, sizeof(stat));
1636         stat.st_dev = pno->p_parent->p_base->pb_ino->i_fs->fs_dev;
1637         inum = incore_inum_alloc();
1638 #ifdef HAVE__ST_INO
1639         stat.__st_ino = inum;
1640 #endif
1641         stat.st_mode = mode;
1642         stat.st_nlink = 1;
1643         stat.st_uid = getuid();
1644         stat.st_gid = getgid();
1645         stat.st_rdev = dev;
1646         stat.st_size = 0;
1647         stat.st_blksize = 4096;
1648         stat.st_blocks = 0;
1649         stat.st_ctime = stat.st_mtime = stat.st_atime = 0;
1650         stat.st_ino = inum;
1651
1652         return incore_create(pno, &stat);
1653 }
1654
1655 #ifdef _HAVE_STATVFS
1656 static int
1657 _sysio_incore_inop_statvfs(struct pnode *pno,
1658                            struct inode *ino,
1659                            struct intnl_statvfs *buf)
1660 {
1661         struct filesys *fs;
1662
1663         if (!ino)
1664                 ino = pno->p_base->pb_ino;
1665         assert(ino);
1666
1667         fs = pno->p_base->pb_ino->i_fs;
1668
1669         (void )memset(buf, 0, sizeof(struct intnl_statvfs));
1670
1671         /*
1672          * Mostly, we lie.
1673          */
1674         buf->f_bsize = fs->fs_bsize;
1675         buf->f_frsize = buf->f_bsize;
1676         buf->f_blocks = ~0;
1677         buf->f_blocks /= buf->f_bsize;
1678         buf->f_bfree = buf->f_blocks - 1;
1679         buf->f_bavail = buf->f_bfree;
1680         buf->f_files = buf->f_blocks;
1681         buf->f_ffree = buf->f_files - 1;
1682         buf->f_favail = buf->f_ffree;
1683         buf->f_fsid = fs->fs_id;
1684         buf->f_flag = 0;
1685         buf->f_namemax = ULONG_MAX;
1686
1687         return 0;
1688 }
1689 #endif
1690
1691 void
1692 _sysio_incore_inop_gone(struct inode *ino)
1693 {
1694         struct incore_inode *icino = I2IC(ino);
1695
1696         incore_i_destroy(icino);
1697 }