Whamcloud - gitweb
Branch: HEAD
[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              struct intnl_stat *stat,
222              unsigned immunity,
223              struct inode_ops *ops,
224              void *private)
225 {
226         struct inode *ino;
227         struct itable_entry *head;
228         struct inode_ops operations;
229
230         if (n_inodes > max_inodes) {
231                 /*
232                  * Try to limit growth.
233                  */
234                 i_reclaim();
235         }
236
237         ino = malloc(sizeof(struct inode));
238         if (!ino)
239                 return NULL;
240         ino->i_ops = *ops;
241         operations = *ops;
242         if (S_ISBLK(stat->st_mode) ||
243             S_ISCHR(stat->st_mode) ||
244             S_ISFIFO(stat->st_mode)) {
245                 struct inode_ops *o;
246
247                 /*
248                  * Replace some operations sent with
249                  * those from the device table.
250                  */
251                 o = _sysio_dev_lookup(stat->st_mode, stat->st_rdev);
252                 operations.inop_open = o->inop_open;
253                 operations.inop_close = o->inop_close;
254                 operations.inop_read = o->inop_read;
255                 operations.inop_write = o->inop_write;
256                 operations.inop_pos = o->inop_pos;
257                 operations.inop_iodone = o->inop_iodone;
258                 operations.inop_fcntl = o->inop_fcntl;
259                 operations.inop_datasync = o->inop_datasync;
260                 operations.inop_ioctl = o->inop_ioctl;
261         }
262         I_INIT(ino, fs, stat, &operations, fid, immunity, private);
263         ino->i_ref = 1;
264         TAILQ_INSERT_TAIL(&_sysio_inodes, ino, i_nodes);
265         head = &fs->fs_itbl[hash(fid) % FS_ITBLSIZ];
266         LIST_INSERT_HEAD(head, ino, i_link);
267
268         n_inodes++;
269         assert(n_inodes);
270
271         return ino;
272 }
273
274 /*
275  * Find existing i-node given i-number and pointers to FS record
276  * and identifier.
277  */
278 struct inode *
279 _sysio_i_find(struct filesys *fs, struct file_identifier *fid)
280 {
281         struct inode *ino;
282         struct itable_entry *head;
283
284         head = &fs->fs_itbl[hash(fid) % FS_ITBLSIZ];
285         /*
286          * Look for existing.
287          */
288         for (ino = head->lh_first; ino; ino = ino->i_link.le_next)
289                 if (ino->i_fid->fid_len == fid->fid_len &&
290                     memcmp(ino->i_fid->fid_data,
291                            fid->fid_data,
292                            fid->fid_len) == 0) {
293                         I_REF(ino);
294                         break;
295                 }
296
297         return ino;
298 }
299
300 /*
301  * Force reclaim of idle i-node.
302  */
303 void
304 _sysio_i_gone(struct inode *ino)
305 {
306
307         if (ino->i_ref)
308                 abort();
309         if (!ino->i_zombie) 
310                 LIST_REMOVE(ino, i_link);
311         TAILQ_REMOVE(&_sysio_inodes, ino, i_nodes);
312         (*ino->i_ops.inop_gone)(ino);
313         free(ino);
314
315         assert(n_inodes);
316         n_inodes--;
317 }
318
319 /*
320  * Stale inode, zombie it and move it out of the way 
321  */
322 void
323 _sysio_i_undead(struct inode *ino)
324 {
325         
326         if (ino->i_zombie)
327                 return;
328         LIST_REMOVE(ino, i_link);
329         ino->i_zombie = 1;
330 }
331
332 /*
333  * Garbage collect idle path (and base path) nodes tracked by the system.
334  */
335 static void
336 p_reclaim()
337 {
338         struct pnode *next, *pno;
339         size_t  t;
340
341         next = _sysio_pnodes.tqh_first;
342         if (!next)
343                 return;
344         t = max_names / 2;
345         do {
346                 pno = next;
347                 if (pno->p_ref) {
348                         next = pno->p_nodes.tqe_next;
349                         continue;
350                 }
351                 pno->p_ref++;
352                 assert(pno->p_ref);
353                 (void )_sysio_p_prune(pno);
354                 next = pno->p_nodes.tqe_next;
355                 assert(pno->p_ref);
356                 pno->p_ref--;
357                 if (pno->p_ref)
358                         continue;
359                 (void )_sysio_p_prune(pno);
360         } while (n_names > t && next);
361
362         if (n_names > t)
363                 max_names += t;
364 }
365
366 /*
367  * Allocate and initialize a new base path node.
368  */
369 struct pnode_base *
370 _sysio_pb_new(struct qstr *name, struct pnode_base *parent, struct inode *ino)
371 {
372         struct pnode_base *pb;
373
374         if (n_names > max_names) {
375                 /*
376                  * Try to limit growth.
377                  */
378                 p_reclaim();
379         }
380
381         pb = malloc(sizeof(struct pnode_base) + name->len);
382         if (!pb)
383                 return NULL;
384
385         pb->pb_name.name = NULL;
386         pb->pb_name.len = name->len;
387         if (pb->pb_name.len) {
388                 char    *cp;
389
390                 /*
391                  * Copy the passed name.
392                  *
393                  * We have put the space for the name immediately behind
394                  * the record in order to maximize spatial locality.
395                  */
396                 cp = (char *)pb + sizeof(struct pnode_base);
397                 (void )strncpy(cp, name->name, name->len);
398                 pb->pb_name.name = cp;
399                 assert(name->hashval);
400                 pb->pb_name.hashval = name->hashval;
401                 LIST_INSERT_HEAD(&names[name->hashval % NAMES_TABLE_LEN],
402                                  pb,
403                                  pb_names);
404         }
405         pb->pb_ino = ino;
406         LIST_INIT(&pb->pb_children);
407         LIST_INIT(&pb->pb_aliases);
408         if (parent)
409                 LIST_INSERT_HEAD(&parent->pb_children, pb, pb_sibs);
410         pb->pb_parent = parent;
411
412         n_names++;
413         assert(n_names);
414
415         return pb;
416 }
417
418 /*
419  * Destroy base path node, releasing resources back to the system.
420  *
421  * NB: Caller must release the inode referenced by the record.
422  */
423 static void
424 pb_destroy(struct pnode_base *pb)
425 {
426
427         assert(n_names);
428         n_names--;
429
430         assert(!pb->pb_aliases.lh_first);
431         assert(!pb->pb_children.lh_first);
432         assert(!pb->pb_ino);
433         if (pb->pb_name.len)
434                 LIST_REMOVE(pb, pb_names);
435         if (pb->pb_parent)
436                 LIST_REMOVE(pb, pb_sibs);
437
438 #ifndef NDEBUG
439         /*
440          * This can help us catch pb-nodes that are free'd redundantly.
441          */
442         pb->pb_name.hashval = 0;
443 #endif
444         free(pb);
445 }
446
447 /*
448  * Force reclaim of idle base path node.
449  */
450 void
451 _sysio_pb_gone(struct pnode_base *pb)
452 {
453
454         if (pb->pb_ino)
455                 I_RELE(pb->pb_ino);
456         pb->pb_ino = NULL;
457
458         pb_destroy(pb);
459 }
460
461 /*
462  * Generate more path (alias) nodes for the fast allocator.
463  */
464 static void
465 more_pnodes()
466 {
467         size_t  n;
468 #if ZERO_SUM_MEMORY
469         struct pnodes_block *pnblk;
470 #endif
471         struct pnode *pno;
472
473 #if ZERO_SUM_MEMORY
474         pnblk = malloc(sizeof(struct pnodes_block));
475         pno = NULL;
476         if (pnblk) {
477                 LIST_INSERT_HEAD(&pnblocks, pnblk, pnblk_links);
478                 pno = pnblk->pnblk_nodes;
479         }
480 #else
481         pno = malloc(PNODES_PER_CHUNK * sizeof(struct pnode));
482 #endif
483         if (!pno)
484                 return;
485         n = PNODES_PER_CHUNK;
486         do {
487                 LIST_INSERT_HEAD(&free_pnodes, pno, p_links);
488                 pno++;
489         } while (--n);
490 }
491
492 #if ZERO_SUM_MEMORY
493 /*
494  * Shutdown
495  */
496 void
497 _sysio_i_shutdown()
498 {
499         struct pnodes_block *pnblk;
500
501         while ((pnblk = pnblocks.lh_first)) {
502                 LIST_REMOVE(pnblk, pnblk_links);
503                 free(pnblk);
504         }
505 }
506 #endif
507
508 /*
509  * Allocate, initialize and establish appropriate links for new path (alias)
510  * node.
511  */
512 struct pnode *
513 _sysio_p_new_alias(struct pnode *parent,
514                    struct pnode_base *pb,
515                    struct mount *mnt)
516 {
517         struct pnode *pno;
518
519         assert(!pb->pb_name.name || pb->pb_name.hashval);
520
521         pno = free_pnodes.lh_first;
522         if (!pno) {
523                 more_pnodes();
524                 pno = free_pnodes.lh_first;
525         }
526         if (!pno)
527                 return NULL;
528         LIST_REMOVE(pno, p_links);
529
530         pno->p_ref = 1;
531         pno->p_parent = parent;
532         if (!pno->p_parent)
533                 pno->p_parent = pno;
534         pno->p_base = pb;
535         pno->p_mount = mnt;
536         pno->p_cover = NULL;
537         LIST_INSERT_HEAD(&pb->pb_aliases, pno, p_links);
538         TAILQ_INSERT_TAIL(&_sysio_pnodes, pno, p_nodes);
539
540         return pno;
541 }
542
543 /*
544  * For reclamation of idle path (alias) node.
545  */
546 void
547 _sysio_p_gone(struct pnode *pno)
548 {
549         struct pnode_base *pb;
550
551         assert(!pno->p_ref);
552         assert(!pno->p_cover);
553
554         TAILQ_REMOVE(&_sysio_pnodes, pno, p_nodes);
555         LIST_REMOVE(pno, p_links);
556
557         pb = pno->p_base;
558         if (!(pb->pb_aliases.lh_first || pb->pb_children.lh_first))
559                 _sysio_pb_gone(pb);
560
561         LIST_INSERT_HEAD(&free_pnodes, pno, p_links);
562 }
563
564 /*
565  * (Re)Validate passed path node.
566  */
567 int
568 _sysio_p_validate(struct pnode *pno, struct intent *intnt, const char *path)
569 {
570         struct inode *ino;
571         struct pnode_base *rootpb;
572         int     err;
573
574         ino = pno->p_base->pb_ino;
575         /*
576          * An invalid pnode will not have an associated inode. We'll use
577          * the FS root inode, then -- It *must* be valid.
578          */
579         rootpb = pno->p_mount->mnt_root->p_base;
580         assert(rootpb->pb_ino);
581         err =
582             rootpb->pb_ino->i_ops.inop_lookup(pno,
583                                               &ino,
584                                               intnt,
585                                               path);
586         /*
587          * If the inode lookup returns a different inode, release the old if
588          * present and point to the new.
589          */
590         if (err || pno->p_base->pb_ino != ino) {
591                 if (pno->p_base->pb_ino)
592                         I_RELE(pno->p_base->pb_ino);
593                 pno->p_base->pb_ino = ino;
594         }
595         return err;
596 }
597
598 /*
599  * Find (or create!) an alias for the given parent and name. A misnomer,
600  * really -- This is a "get". Returned path node is referenced.
601  */
602 int
603 _sysio_p_find_alias(struct pnode *parent,
604                     struct qstr *name,
605                     struct pnode **pnop)
606 {
607         struct pnode_base *pb;
608         int     err;
609         struct pnode *pno;
610
611         /*
612          * Find the named child.
613          */
614         if (name->len) {
615                 /*
616                  * Try the names table.
617                  */
618                 pb = names[name->hashval % NAMES_TABLE_LEN].lh_first;
619                 while (pb) {
620                         if (pb->pb_parent == parent->p_base &&
621                             pb->pb_name.len == name->len &&
622                             strncmp(pb->pb_name.name,
623                                     name->name,
624                                     name->len) == 0)
625                                 break;
626                         pb = pb->pb_names.le_next;
627                 }
628         } else {
629                 /*
630                  * Brute force through the parent's list of children.
631                  */
632                 pb = parent->p_base->pb_children.lh_first;
633                 while (pb) {
634                         if (pb->pb_parent == parent->p_base &&
635                             pb->pb_name.len == name->len &&
636                             strncmp(pb->pb_name.name,
637                                     name->name,
638                                     name->len) == 0)
639                                 break;
640                         pb = pb->pb_sibs.le_next;
641                 }
642         }
643         if (!pb) {
644                 /*
645                  * None found, create new child.
646                  */
647                 pb = _sysio_pb_new(name, parent->p_base, NULL);
648                 if (!pb)
649                         return -ENOMEM;
650         }
651         /*
652          * Now find the proper alias. It's the one with the passed
653          * parent.
654          */
655         err = 0;
656         pno = pb->pb_aliases.lh_first;
657         while (pno) {
658                 if (pno->p_parent == parent) {
659                         P_REF(pno);
660                         break;
661                 }
662                 pno = pno->p_links.le_next;
663         }
664         if (!pno) {
665                 /*
666                  * Hmm. No alias. Just create an invalid one, to be
667                  * validated later.
668                  */
669                 pno = _sysio_p_new_alias(parent, pb, parent->p_mount);
670                 if (!pno)
671                         err = -ENOMEM;
672         }
673         if (!err)
674                 *pnop = pno;
675         return err;
676 }
677
678 /*
679  * Prune idle path base nodes freom the passed sub-tree, including the root.
680  */
681 static void
682 _sysio_prune(struct pnode_base *rpb)
683 {
684         struct pnode_base *nxtpb, *pb;
685
686         nxtpb = rpb->pb_children.lh_first;
687         while ((pb = nxtpb)) {
688                 nxtpb = pb->pb_sibs.le_next;
689                 if (pb->pb_aliases.lh_first)
690                         continue;
691                 if (pb->pb_children.lh_first) {
692                         _sysio_prune(pb);
693                         continue;
694                 }
695                 _sysio_pb_gone(pb);
696         }
697         if (rpb->pb_children.lh_first)
698                 return;
699         _sysio_pb_gone(rpb);
700 }
701
702 /*
703  * Prune idle nodes from the passed sub-tree, including the root.
704  *
705  * Returns the number of aliases on the same mount that could not be pruned.
706  * i.e. a zero return means the entire sub-tree is gone.
707  */
708 size_t
709 _sysio_p_prune(struct pnode *root)
710 {
711         size_t  count;
712         struct pnode_base *nxtpb, *pb;
713         struct pnode *nxtpno, *pno;
714
715         count = 0;
716         nxtpb = root->p_base->pb_children.lh_first;
717         while ((pb = nxtpb)) {
718                 nxtpb = pb->pb_sibs.le_next;
719                 nxtpno = pb->pb_aliases.lh_first;
720                 if (!nxtpno) {
721                         _sysio_prune(pb);
722                         continue;
723                 }
724                 while ((pno = nxtpno)) {
725                         nxtpno = pno->p_links.le_next;
726                         if (pno->p_mount != root->p_mount) {
727                                 /*
728                                  * Not the alias we were looking for.
729                                  */
730                                 continue;
731                         }
732                         if (pno->p_base->pb_children.lh_first) {
733                                 /*
734                                  * Node is interior. Recurse.
735                                  */
736                                 count += _sysio_p_prune(pno);
737                                 continue;
738                         }
739                         if (pno->p_ref) {
740                                 /*
741                                  * Can't prune; It's active.
742                                  */
743                                 count++;
744                                 continue;
745                         }
746                         assert(!pno->p_cover);          /* covered => ref'd! */
747                         assert(!pno->p_base->pb_name.name ||
748                                pno->p_base->pb_name.hashval);
749                         /*
750                          * Ok to prune.
751                          */
752                         if (pno->p_mount->mnt_root == pno) {
753 #ifndef AUTOMOUNT_FILE_NAME
754                                 count++;
755                                 continue;
756 #else
757                                 /*
758                                  * This is an automount-point. Must
759                                  * unmount before relcaim.
760                                  */
761                                 P_REF(pno);
762                                 if (_sysio_do_unmount(pno->p_mount) != 0) {
763                                         P_RELE(pno);
764                                         count++;
765                                 }
766                                 continue;
767 #endif
768                         }
769                         _sysio_p_gone(pno);
770                 }
771         }
772
773         if (count) {
774                 /*
775                  * Can't get the root or we disconnect the sub-trees.
776                  */
777                 return count + (root->p_ref ? 1 : 0);
778         }
779
780         /*
781          * All that is left is the root. Try for it too.
782          */
783         if (root->p_ref) {
784                 count++;
785         } else if (root->p_mount->mnt_root == root) {
786 #ifndef AUTOMOUNT_FILE_NAME
787                 count++;
788 #else
789                 /*
790                  * This is an automount-point. Must
791                  * unmount before relcaim.
792                  */
793                 P_REF(root);
794                 if (_sysio_do_unmount(root->p_mount) != 0) {
795                         P_RELE(root);
796                         count++;
797                 }
798 #endif
799         } else
800                 _sysio_p_gone(root);
801
802         return count;
803 }
804
805 /*
806  * Return path tracked by the base path node ancestor chain.
807  *
808  * Remember, base path nodes track the path relative to the file system and
809  * path (alias) nodes track path relative to our name space -- They cross
810  * mount points.
811  */
812 char *
813 _sysio_pb_path(struct pnode_base *pb, const char separator)
814 {
815         char    *buf;
816         size_t  len, n;
817         struct pnode_base *tmp;
818         char    *cp;
819
820         /*
821          * First pass: Traverse to the root of the sub-tree, remembering
822          * lengths.
823          */
824         len = 0;
825         tmp = pb;
826         do {
827                 n = tmp->pb_name.len;
828                 len += tmp->pb_name.len;
829                 if (n)
830                         len++;
831                 tmp = tmp->pb_parent;
832         } while (tmp);
833         if (!len)
834                 len++;
835         /*
836          * Alloc space.
837          */
838         buf = malloc(len + 1);
839         if (!buf)
840                 return NULL;
841         /*
842          * Fill in the path buffer -- Backwards, since we're starting
843          * from the end.
844          */
845         cp = buf;
846         *cp = separator;
847         cp += len;
848         *cp = '\0';                                     /* NUL term */
849         tmp = pb;
850         do {
851                 cp -= tmp->pb_name.len;
852                 n = tmp->pb_name.len;
853                 if (n) {
854                         (void )strncpy(cp, tmp->pb_name.name, n);
855                         *--cp = separator;
856                 }
857                 tmp = tmp->pb_parent;
858         } while (tmp);
859
860         return buf;
861 }
862
863 /*
864  * Common set attributes routine.
865  */
866 int
867 _sysio_setattr(struct pnode *pno,
868                struct inode *ino,
869                unsigned mask,
870                struct intnl_stat *stbuf)
871 {
872         /* It is possible that pno is null (for ftruncate call). */
873
874         if (pno) {
875         assert(!(pno->p_base->pb_ino && ino) || pno->p_base->pb_ino == ino);
876         if (IS_RDONLY(pno, ino))
877                 return -EROFS;
878         }
879         if (!ino && pno->p_base->pb_ino)
880                 ino = pno->p_base->pb_ino;
881         return (*ino->i_ops.inop_setattr)(pno, ino, mask, stbuf);
882 }
883
884 /*
885  * Do nothing.
886  */
887 void
888 _sysio_do_noop()
889 {
890
891         return;
892 }
893
894 /*
895  * Abort.
896  */
897 void
898 _sysio_do_illop()
899 {
900
901         abort();
902 }
903
904 /*
905  * Return -EBADF
906  */
907 int
908 _sysio_do_ebadf()
909 {
910
911         return -EBADF;
912 }
913
914 /*
915  * Return -EINVAL
916  */
917 int
918 _sysio_do_einval()
919 {
920
921         return -EINVAL;
922 }
923
924 /*
925  * Return -ENOENT
926  */
927 int
928 _sysio_do_enoent()
929 {
930
931         return -ENOENT;
932 }
933
934 /*
935  * Return -ESPIPE
936  */
937 int
938 _sysio_do_espipe()
939 {
940
941         return -ESPIPE;
942 }
943
944 /*
945  * Return -EISDIR
946  */
947 int
948 _sysio_do_eisdir()
949 {
950
951         return -EISDIR;
952 }
953
954 /*
955  * Return -ENOSYS
956  */
957 int
958 _sysio_do_enosys()
959 {
960
961         return -ENOSYS;
962 }
963
964
965 /*
966  * Return -ENODEV
967  */
968 int
969 _sysio_do_enodev()
970 {
971
972         return -ENODEV;
973 }