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