Whamcloud - gitweb
import libsysio for b_newsysio
[fs/lustre-release.git] / libsysio / src / inode.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 #include <stdlib.h>
45 #include <string.h>
46 #include <errno.h>
47 #include <assert.h>
48 #include <sys/types.h>
49 #include <sys/stat.h>
50 #include <sys/queue.h>
51
52 #include "sysio.h"
53 #include "fs.h"
54 #include "mount.h"
55 #include "inode.h"
56 #include "dev.h"
57
58 /*
59  * Support for path and index nodes.
60  */
61
62 /*
63  * Size of all names bucket-hash table.
64  */
65 #ifndef NAMES_TABLE_LEN
66 #define NAMES_TABLE_LEN         251
67 #endif
68
69 /*
70  * Desired i-nodes cache size is MAX_INODES_MULTIPLIER times the number
71  * of slots in the names hash table.
72  */
73 #define MAX_INODES_MULTIPLIER   3
74
75 /*
76  * Active i-nodes in the system and the number of same.
77  */
78 struct inodes_head _sysio_inodes;
79 static size_t n_inodes = 0;
80 /*
81  * Desired number of active i-nodes.
82  */
83 static size_t max_inodes = (MAX_INODES_MULTIPLIER * NAMES_TABLE_LEN);
84
85 /*
86  * System table for rapid access to component names.
87  */
88 static LIST_HEAD(, pnode_base) names[NAMES_TABLE_LEN];
89 /*
90  * Number of names tracked by the system.
91  */
92 static size_t n_names = 0;
93 /*
94  * Desired number of base path nodes to maintain.
95  */
96 static size_t max_names = (2 * NAMES_TABLE_LEN);
97
98 /*
99  * Number of pnodes to grab per memory allocation when filling the
100  * free list.
101  */
102 #define PNODES_PER_CHUNK ((8 * 1024) / sizeof(struct pnode) - 2)
103
104 #if ZERO_SUM_MEMORY
105 /*
106  * Allocation information for pnodes bulk allocation.
107  */
108 struct pnodes_block {
109         LIST_ENTRY(pnodes_block) pnblk_links;
110         struct pnode pnblk_nodes[PNODES_PER_CHUNK];
111 };
112
113 static LIST_HEAD( ,pnodes_block) pnblocks;
114 #endif
115
116 /*
117  * List of all path-nodes (aliases) referenced by any tree.
118  */
119 struct pnodes_head _sysio_pnodes;
120
121 /*
122  * Free path-nodes -- Not referenced by any tree for fas reuse.
123  */
124 static LIST_HEAD( ,pnode) free_pnodes;
125
126 /*
127  * The system root -- Aka `/'.
128  */
129 struct pnode *_sysio_root = NULL;
130
131 /*
132  * Initialize path and i-node support. Must be called before any other
133  * routine in this module.
134  */
135 int
136 _sysio_i_init()
137 {
138         unsigned i;
139
140         TAILQ_INIT(&_sysio_inodes);
141
142         for (i = 0; i < NAMES_TABLE_LEN; i++)
143                 LIST_INIT(&names[i]);
144
145 #if ZERO_SUM_MEMORY
146         LIST_INIT(&pnblocks);
147 #endif
148         TAILQ_INIT(&_sysio_pnodes);
149         LIST_INIT(&free_pnodes);
150
151         return 0;
152 }
153
154 /*
155  * Garbage-collect idle i-nodes. We try to keep resource use limited to
156  * MAX_INODES_MULTIPLIER * max_names.
157  */
158 static void
159 i_reclaim()
160 {
161         struct inode *next, *ino;
162         size_t  t;
163
164         /*
165          * I just can't figure out a good way to reclaim these well without
166          * getting really fancy and using complex algorithms. The
167          * base nodes hold references on them for a long time and then
168          * release them. Those will age to the front of the queue and
169          * we have to skip over them. Oh well...
170          */
171         t = MAX_INODES_MULTIPLIER * max_names;
172         if (max_inodes < t) {
173                 /*
174                  * Oops. Nope. We want more inodes than names entries.
175                  */
176                 max_inodes = t;
177                 return;
178         }
179         next = _sysio_inodes.tqh_first;
180         if (!next)
181                 return;
182         t = max_inodes / 2;
183         do {
184                 ino = next;
185                 next = ino->i_nodes.tqe_next;
186                 if (ino->i_ref || ino->i_immune)
187                         continue;
188                 _sysio_i_gone(ino);
189         } while (next && n_inodes > t);
190
191         if (n_inodes > t)
192                 max_inodes += t;
193 }
194
195 static unsigned
196 hash(struct file_identifier *fid)
197 {
198         size_t  n;
199         unsigned char *ucp;
200         unsigned hkey;
201
202         n = fid->fid_len;
203         ucp = fid->fid_data;
204         hkey = 0;
205         do {
206                 hkey <<= 1;
207                 hkey += *ucp++;
208         } while (--n);
209         return hkey;
210 }
211
212 /*
213  * Allocate and initialize a new i-node. Returned i-node is referenced.
214  *
215  * NB: The passed file identifier is not copied. It is, therefor, up to the
216  * caller to assure that the value is static until the inode is destroyed.
217  */
218 struct inode *
219 _sysio_i_new(struct filesys *fs,
220              struct file_identifier *fid,
221              mode_t type,
222              dev_t rdev,
223              unsigned immunity,
224              struct inode_ops *ops,
225              void *private)
226 {
227         struct inode *ino;
228         struct itable_entry *head;
229         struct inode_ops operations;
230
231         if (n_inodes > max_inodes) {
232                 /*
233                  * Try to limit growth.
234                  */
235                 i_reclaim();
236         }
237
238         ino = malloc(sizeof(struct inode));
239         if (!ino)
240                 return NULL;
241         ino->i_ops = *ops;
242         operations = *ops;
243         if (S_ISBLK(type) || S_ISCHR(type) || S_ISFIFO(type)) {
244                 struct inode_ops *o;
245
246                 /*
247                  * Replace some operations sent with
248                  * those from the device table.
249                  */
250                 o = _sysio_dev_lookup(type, rdev);
251                 operations.inop_open = o->inop_open;
252                 operations.inop_close = o->inop_close;
253                 operations.inop_read = o->inop_read;
254                 operations.inop_write = o->inop_write;
255                 operations.inop_pos = o->inop_pos;
256                 operations.inop_iodone = o->inop_iodone;
257                 operations.inop_datasync = o->inop_datasync;
258                 operations.inop_ioctl = o->inop_ioctl;
259         }
260         I_INIT(ino, fs, type, rdev, &operations, fid, immunity, private);
261         ino->i_ref = 1;
262         TAILQ_INSERT_TAIL(&_sysio_inodes, ino, i_nodes);
263         head = &fs->fs_itbl[hash(fid) % FS_ITBLSIZ];
264         LIST_INSERT_HEAD(head, ino, i_link);
265
266         n_inodes++;
267         assert(n_inodes);
268
269         return ino;
270 }
271
272 /*
273  * Find existing i-node given i-number and pointers to FS record
274  * and identifier.
275  */
276 struct inode *
277 _sysio_i_find(struct filesys *fs, struct file_identifier *fid)
278 {
279         struct inode *ino;
280         struct itable_entry *head;
281
282         head = &fs->fs_itbl[hash(fid) % FS_ITBLSIZ];
283         /*
284          * Look for existing.
285          */
286         for (ino = head->lh_first; ino; ino = ino->i_link.le_next)
287                 if (ino->i_fid->fid_len == fid->fid_len &&
288                     memcmp(ino->i_fid->fid_data,
289                            fid->fid_data,
290                            fid->fid_len) == 0) {
291                         I_REF(ino);
292                         break;
293                 }
294
295         return ino;
296 }
297
298 /*
299  * Force reclaim of idle i-node.
300  */
301 void
302 _sysio_i_gone(struct inode *ino)
303 {
304
305         if (ino->i_ref)
306                 abort();
307         if (!ino->i_zombie) 
308                 LIST_REMOVE(ino, i_link);
309         TAILQ_REMOVE(&_sysio_inodes, ino, i_nodes);
310         (*ino->i_ops.inop_gone)(ino);
311         free(ino);
312
313         assert(n_inodes);
314         n_inodes--;
315 }
316
317 /*
318  * Stale inode, zombie it and move it out of the way 
319  */
320 void
321 _sysio_i_undead(struct inode *ino)
322 {
323         
324         LIST_REMOVE(ino, i_link);
325         ino->i_zombie = 1;
326 }
327
328 /*
329  * Garbage collect idle path (and base path) nodes tracked by the system.
330  */
331 static void
332 p_reclaim()
333 {
334         struct pnode *next, *pno;
335         size_t  t;
336
337         next = _sysio_pnodes.tqh_first;
338         if (!next)
339                 return;
340         t = max_names / 2;
341         do {
342                 pno = next;
343                 if (pno->p_ref) {
344                         next = pno->p_nodes.tqe_next;
345                         continue;
346                 }
347                 pno->p_ref++;
348                 assert(pno->p_ref);
349                 (void )_sysio_p_prune(pno);
350                 next = pno->p_nodes.tqe_next;
351                 assert(pno->p_ref);
352                 pno->p_ref--;
353                 if (pno->p_ref)
354                         continue;
355                 (void )_sysio_p_prune(pno);
356         } while (n_names > t && next);
357
358         if (n_names > t)
359                 max_names += t;
360 }
361
362 /*
363  * Allocate and initialize a new base path node.
364  */
365 struct pnode_base *
366 _sysio_pb_new(struct qstr *name, struct pnode_base *parent, struct inode *ino)
367 {
368         struct pnode_base *pb;
369
370         if (n_names > max_names) {
371                 /*
372                  * Try to limit growth.
373                  */
374                 p_reclaim();
375         }
376
377         pb = malloc(sizeof(struct pnode_base) + name->len);
378         if (!pb)
379                 return NULL;
380
381         pb->pb_name.name = NULL;
382         pb->pb_name.len = name->len;
383         if (pb->pb_name.len) {
384                 char    *cp;
385
386                 /*
387                  * Copy the passed name.
388                  *
389                  * We have put the space for the name immediately behind
390                  * the record in order to maximize spatial locality.
391                  */
392                 cp = (char *)pb + sizeof(struct pnode_base);
393                 (void )strncpy(cp, name->name, name->len);
394                 pb->pb_name.name = cp;
395                 assert(name->hashval);
396                 pb->pb_name.hashval = name->hashval;
397                 LIST_INSERT_HEAD(&names[name->hashval % NAMES_TABLE_LEN],
398                                  pb,
399                                  pb_names);
400         }
401         pb->pb_ino = ino;
402         LIST_INIT(&pb->pb_children);
403         LIST_INIT(&pb->pb_aliases);
404         if (parent)
405                 LIST_INSERT_HEAD(&parent->pb_children, pb, pb_sibs);
406         pb->pb_parent = parent;
407
408         n_names++;
409         assert(n_names);
410
411         return pb;
412 }
413
414 /*
415  * Destroy base path node, releasing resources back to the system.
416  *
417  * NB: Caller must release the inode referenced by the record.
418  */
419 static void
420 pb_destroy(struct pnode_base *pb)
421 {
422
423         assert(n_names);
424         n_names--;
425
426         assert(!pb->pb_aliases.lh_first);
427         assert(!pb->pb_children.lh_first);
428         assert(!pb->pb_ino);
429         if (pb->pb_name.len)
430                 LIST_REMOVE(pb, pb_names);
431         if (pb->pb_parent)
432                 LIST_REMOVE(pb, pb_sibs);
433
434 #ifndef NDEBUG
435         /*
436          * This can help us catch pb-nodes that are free'd redundantly.
437          */
438         pb->pb_name.hashval = 0;
439 #endif
440         free(pb);
441 }
442
443 /*
444  * Force reclaim of idle base path node.
445  */
446 void
447 _sysio_pb_gone(struct pnode_base *pb)
448 {
449
450         if (pb->pb_ino)
451                 I_RELE(pb->pb_ino);
452         pb->pb_ino = NULL;
453
454         pb_destroy(pb);
455 }
456
457 /*
458  * Generate more path (alias) nodes for the fast allocator.
459  */
460 static void
461 more_pnodes()
462 {
463         size_t  n;
464 #if ZERO_SUM_MEMORY
465         struct pnodes_block *pnblk;
466 #endif
467         struct pnode *pno;
468
469 #if ZERO_SUM_MEMORY
470         pnblk = malloc(sizeof(struct pnodes_block));
471         pno = NULL;
472         if (pnblk) {
473                 LIST_INSERT_HEAD(&pnblocks, pnblk, pnblk_links);
474                 pno = pnblk->pnblk_nodes;
475         }
476 #else
477         pno = malloc(PNODES_PER_CHUNK * sizeof(struct pnode));
478 #endif
479         if (!pno)
480                 return;
481         n = PNODES_PER_CHUNK;
482         do {
483                 LIST_INSERT_HEAD(&free_pnodes, pno, p_links);
484                 pno++;
485         } while (--n);
486 }
487
488 #if ZERO_SUM_MEMORY
489 /*
490  * Shutdown
491  */
492 void
493 _sysio_i_shutdown()
494 {
495         struct pnodes_block *pnblk;
496
497         while ((pnblk = pnblocks.lh_first)) {
498                 LIST_REMOVE(pnblk, pnblk_links);
499                 free(pnblk);
500         }
501 }
502 #endif
503
504 /*
505  * Allocate, initialize and establish appropriate links for new path (alias)
506  * node.
507  */
508 struct pnode *
509 _sysio_p_new_alias(struct pnode *parent,
510                    struct pnode_base *pb,
511                    struct mount *mnt)
512 {
513         struct pnode *pno;
514
515         assert(!pb->pb_name.name || pb->pb_name.hashval);
516
517         pno = free_pnodes.lh_first;
518         if (!pno) {
519                 more_pnodes();
520                 pno = free_pnodes.lh_first;
521         }
522         if (!pno)
523                 return NULL;
524         LIST_REMOVE(pno, p_links);
525
526         pno->p_ref = 1;
527         pno->p_parent = parent;
528         if (!pno->p_parent)
529                 pno->p_parent = pno;
530         pno->p_base = pb;
531         pno->p_mount = mnt;
532         pno->p_cover = NULL;
533         LIST_INSERT_HEAD(&pb->pb_aliases, pno, p_links);
534         TAILQ_INSERT_TAIL(&_sysio_pnodes, pno, p_nodes);
535
536         return pno;
537 }
538
539 /*
540  * For reclamation of idle path (alias) node.
541  */
542 void
543 _sysio_p_gone(struct pnode *pno)
544 {
545         struct pnode_base *pb;
546
547         assert(!pno->p_ref);
548         assert(!pno->p_cover);
549
550         TAILQ_REMOVE(&_sysio_pnodes, pno, p_nodes);
551         LIST_REMOVE(pno, p_links);
552
553         pb = pno->p_base;
554         if (!(pb->pb_aliases.lh_first || pb->pb_children.lh_first))
555                 _sysio_pb_gone(pb);
556
557         LIST_INSERT_HEAD(&free_pnodes, pno, p_links);
558 }
559
560 /*
561  * (Re)Validate passed path node.
562  */
563 int
564 _sysio_p_validate(struct pnode *pno, struct intent *intnt, const char *path)
565 {
566         struct inode *ino;
567         struct pnode_base *rootpb;
568         int     err;
569
570         ino = pno->p_base->pb_ino;
571         /*
572          * An invalid pnode will not have an associated inode. We'll use
573          * the FS root inode, then -- It *must* be valid.
574          */
575         rootpb = pno->p_mount->mnt_root->p_base;
576         assert(rootpb->pb_ino);
577         err =
578             rootpb->pb_ino->i_ops.inop_lookup(pno,
579                                               &ino,
580                                               intnt,
581                                               path);
582         /*
583          * If the inode lookup returns a different inode, release the old if
584          * present and point to the new.
585          */
586         if (err || pno->p_base->pb_ino != ino) {
587                 if (pno->p_base->pb_ino)
588                         I_RELE(pno->p_base->pb_ino);
589                 pno->p_base->pb_ino = ino;
590         }
591         return err;
592 }
593
594 /*
595  * Find (or create!) an alias for the given parent and name. A misnomer,
596  * really -- This is a "get". Returned path node is referenced.
597  */
598 int
599 _sysio_p_find_alias(struct pnode *parent,
600                     struct qstr *name,
601                     struct pnode **pnop)
602 {
603         struct pnode_base *pb;
604         int     err;
605         struct pnode *pno;
606
607         /*
608          * Find the named child.
609          */
610         if (name->len) {
611                 /*
612                  * Try the names table.
613                  */
614                 pb = names[name->hashval % NAMES_TABLE_LEN].lh_first;
615                 while (pb) {
616                         if (pb->pb_parent == parent->p_base &&
617                             pb->pb_name.len == name->len &&
618                             strncmp(pb->pb_name.name,
619                                     name->name,
620                                     name->len) == 0)
621                                 break;
622                         pb = pb->pb_names.le_next;
623                 }
624         } else {
625                 /*
626                  * Brute force through the parent's list of children.
627                  */
628                 pb = parent->p_base->pb_children.lh_first;
629                 while (pb) {
630                         if (pb->pb_parent == parent->p_base &&
631                             pb->pb_name.len == name->len &&
632                             strncmp(pb->pb_name.name,
633                                     name->name,
634                                     name->len) == 0)
635                                 break;
636                         pb = pb->pb_sibs.le_next;
637                 }
638         }
639         if (!pb) {
640                 /*
641                  * None found, create new child.
642                  */
643                 pb = _sysio_pb_new(name, parent->p_base, NULL);
644                 if (!pb)
645                         return -ENOMEM;
646         }
647         /*
648          * Now find the proper alias. It's the one with the passed
649          * parent.
650          */
651         err = 0;
652         pno = pb->pb_aliases.lh_first;
653         while (pno) {
654                 if (pno->p_parent == parent) {
655                         P_REF(pno);
656                         break;
657                 }
658                 pno = pno->p_links.le_next;
659         }
660         if (!pno) {
661                 /*
662                  * Hmm. No alias. Just create an invalid one, to be
663                  * validated later.
664                  */
665                 pno = _sysio_p_new_alias(parent, pb, parent->p_mount);
666                 if (!pno)
667                         err = -ENOMEM;
668         }
669         if (!err)
670                 *pnop = pno;
671         return err;
672 }
673
674 /*
675  * Prune idle path base nodes freom the passed sub-tree, including the root.
676  */
677 static void
678 _sysio_prune(struct pnode_base *rpb)
679 {
680         struct pnode_base *nxtpb, *pb;
681
682         nxtpb = rpb->pb_children.lh_first;
683         while ((pb = nxtpb)) {
684                 nxtpb = pb->pb_sibs.le_next;
685                 if (pb->pb_aliases.lh_first)
686                         continue;
687                 if (pb->pb_children.lh_first) {
688                         _sysio_prune(pb);
689                         continue;
690                 }
691                 _sysio_pb_gone(pb);
692         }
693         if (rpb->pb_children.lh_first)
694                 return;
695         _sysio_pb_gone(rpb);
696 }
697
698 /*
699  * Prune idle nodes from the passed sub-tree, including the root.
700  *
701  * Returns the number of aliases on the same mount that could not be pruned.
702  * i.e. a zero return means the entire sub-tree is gone.
703  */
704 size_t
705 _sysio_p_prune(struct pnode *root)
706 {
707         size_t  count;
708         struct pnode_base *nxtpb, *pb;
709         struct pnode *nxtpno, *pno;
710
711         count = 0;
712         nxtpb = root->p_base->pb_children.lh_first;
713         while ((pb = nxtpb)) {
714                 nxtpb = pb->pb_sibs.le_next;
715                 nxtpno = pb->pb_aliases.lh_first;
716                 if (!nxtpno) {
717                         _sysio_prune(pb);
718                         continue;
719                 }
720                 while ((pno = nxtpno)) {
721                         nxtpno = pno->p_links.le_next;
722                         if (pno->p_mount != root->p_mount) {
723                                 /*
724                                  * Not the alias we were looking for.
725                                  */
726                                 continue;
727                         }
728                         if (pno->p_base->pb_children.lh_first) {
729                                 /*
730                                  * Node is interior. Recurse.
731                                  */
732                                 count += _sysio_p_prune(pno);
733                                 continue;
734                         }
735                         if (pno->p_ref) {
736                                 /*
737                                  * Can't prune; It's active.
738                                  */
739                                 count++;
740                                 continue;
741                         }
742                         assert(!pno->p_cover);          /* covered => ref'd! */
743                         assert(!pno->p_base->pb_name.name ||
744                                pno->p_base->pb_name.hashval);
745                         /*
746                          * Ok to prune.
747                          */
748                         if (pno->p_mount->mnt_root == pno) {
749 #ifndef AUTOMOUNT_FILE_NAME
750                                 count++;
751                                 continue;
752 #else
753                                 /*
754                                  * This is an automount-point. Must
755                                  * unmount before relcaim.
756                                  */
757                                 P_REF(pno);
758                                 if (_sysio_do_unmount(pno->p_mount) != 0) {
759                                         P_RELE(pno);
760                                         count++;
761                                 }
762                                 continue;
763 #endif
764                         }
765                         _sysio_p_gone(pno);
766                 }
767         }
768
769         if (count) {
770                 /*
771                  * Can't get the root or we disconnect the sub-trees.
772                  */
773                 return count + (root->p_ref ? 1 : 0);
774         }
775
776         /*
777          * All that is left is the root. Try for it too.
778          */
779         if (root->p_ref) {
780                 count++;
781         } else if (root->p_mount->mnt_root == root) {
782 #ifndef AUTOMOUNT_FILE_NAME
783                 count++;
784 #else
785                 /*
786                  * This is an automount-point. Must
787                  * unmount before relcaim.
788                  */
789                 P_REF(root);
790                 if (_sysio_do_unmount(root->p_mount) != 0) {
791                         P_RELE(root);
792                         count++;
793                 }
794 #endif
795         } else
796                 _sysio_p_gone(root);
797
798         return count;
799 }
800
801 /*
802  * Return path tracked by the base path node ancestor chain.
803  *
804  * Remember, base path nodes track the path relative to the file system and
805  * path (alias) nodes track path relative to our name space -- They cross
806  * mount points.
807  */
808 char *
809 _sysio_pb_path(struct pnode_base *pb, const char separator)
810 {
811         char    *buf;
812         size_t  len, n;
813         struct pnode_base *tmp;
814         char    *cp;
815
816         /*
817          * First pass: Traverse to the root of the sub-tree, remembering
818          * lengths.
819          */
820         len = 0;
821         tmp = pb;
822         do {
823                 n = tmp->pb_name.len;
824                 len += tmp->pb_name.len;
825                 if (n)
826                         len++;
827                 tmp = tmp->pb_parent;
828         } while (tmp);
829         if (!len)
830                 len++;
831         /*
832          * Alloc space.
833          */
834         buf = malloc(len + 1);
835         if (!buf)
836                 return NULL;
837         /*
838          * Fill in the path buffer -- Backwards, since we're starting
839          * from the end.
840          */
841         cp = buf;
842         *cp = separator;
843         cp += len;
844         *cp = '\0';                                     /* NUL term */
845         tmp = pb;
846         do {
847                 cp -= tmp->pb_name.len;
848                 n = tmp->pb_name.len;
849                 if (n) {
850                         (void )strncpy(cp, tmp->pb_name.name, n);
851                         *--cp = separator;
852                 }
853                 tmp = tmp->pb_parent;
854         } while (tmp);
855
856         return buf;
857 }
858
859 /*
860  * Common set attributes routine.
861  */
862 int
863 _sysio_setattr(struct pnode *pno,
864                struct inode *ino,
865                unsigned mask,
866                struct intnl_stat *stbuf)
867 {
868         /* It is possible that pno is null (for ftruncate call). */
869
870         if (pno) {
871         assert(!(pno->p_base->pb_ino && ino) || pno->p_base->pb_ino == ino);
872         if (IS_RDONLY(pno, ino))
873                 return -EROFS;
874         }
875         if (!ino && pno->p_base->pb_ino)
876                 ino = pno->p_base->pb_ino;
877         return (*ino->i_ops.inop_setattr)(pno, ino, mask, stbuf);
878 }
879
880 /*
881  * Do nothing.
882  */
883 void
884 _sysio_do_noop()
885 {
886
887         return;
888 }
889
890 /*
891  * Abort.
892  */
893 void
894 _sysio_do_illop()
895 {
896
897         abort();
898 }
899
900 /*
901  * Return -EBADF
902  */
903 int
904 _sysio_do_ebadf()
905 {
906
907         return -EBADF;
908 }
909
910 /*
911  * Return -EINVAL
912  */
913 int
914 _sysio_do_einval()
915 {
916
917         return -EINVAL;
918 }
919
920 /*
921  * Return -ENOENT
922  */
923 int
924 _sysio_do_enoent()
925 {
926
927         return -ENOENT;
928 }
929
930 /*
931  * Return -ESPIPE
932  */
933 int
934 _sysio_do_espipe()
935 {
936
937         return -ESPIPE;
938 }
939
940 /*
941  * Return -EISDIR
942  */
943 int
944 _sysio_do_eisdir()
945 {
946
947         return -EISDIR;
948 }
949
950 /*
951  * Return -ENOSYS
952  */
953 int
954 _sysio_do_enosys()
955 {
956
957         return -ENOSYS;
958 }
959
960
961 /*
962  * Return -ENODEV
963  */
964 int
965 _sysio_do_enodev()
966 {
967
968         return -ENODEV;
969 }