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