1 /* @(#)lpath_unix.c	1.15 20/07/01 Copyright 2018-2020 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static	UConst char sccsid[] =
5 	"@(#)lpath_unix.c	1.15 20/07/01 Copyright 2018-2020 J. Schilling";
6 #endif
7 /*
8  *	Routines for long path names on unix like operating systems
9  *
10  *	Copyright (c) 2018-2020 J. Schilling
11  */
12 /*
13  * The contents of this file are subject to the terms of the
14  * Common Development and Distribution License, Version 1.0 only
15  * (the "License").  You may not use this file except in compliance
16  * with the License.
17  *
18  * See the file CDDL.Schily.txt in this distribution for details.
19  * A copy of the CDDL is also available via the Internet at
20  * http://www.opensource.org/licenses/cddl1.txt
21  *
22  * When distributing Covered Code, include this CDDL HEADER in each
23  * file and include the License file CDDL.Schily.txt from this distribution.
24  */
25 
26 #include <schily/mconfig.h>
27 #include <schily/stdio.h>
28 #include <schily/errno.h>
29 #include "star.h"
30 #include <schily/standard.h>
31 #include <schily/unistd.h>
32 #include <schily/dirent.h>
33 #include <schily/fcntl.h>	/* For AT_FDCWD */
34 #include <schily/stat.h>
35 #include <schily/param.h>	/* For DEV_BSIZE */
36 #include <schily/string.h>
37 #define	GT_COMERR		/* #define comerr gtcomerr */
38 #define	GT_ERROR		/* #define error gterror   */
39 #include <schily/schily.h>
40 #include "starsubs.h"
41 
42 #ifndef	ENAMETOOLONG
43 #define	ENAMETOOLONG	EINVAL
44 #endif
45 
46 EXPORT	int	lchdir		__PR((char *name));
47 #if	defined(IS_SUN) && defined(__SVR4) && defined(DO_CHDIR_LONG)
48 LOCAL	void	sunos5_cwdfix	__PR((void));
49 #endif
50 EXPORT	char	*lgetcwd	__PR((void));
51 EXPORT	int	lmkdir		__PR((char *name, mode_t mode));
52 EXPORT	int	laccess		__PR((char *name, int amode));
53 EXPORT	int	lstatat		__PR((char *name, struct stat *buf, int flag));
54 EXPORT	int	lchmodat	__PR((char *name, mode_t mode, int flag));
55 EXPORT	int	lutimensat	__PR((char *name, struct timespec *ts, int flag));
56 EXPORT	int	lreadlink	__PR((char *name, char *buf, size_t bfsize));
57 EXPORT	int	lsymlink	__PR((char *name, char *name2));
58 EXPORT	int	llink		__PR((char *name, char *name2));
59 EXPORT	int	lrename		__PR((char *name, char *name2));
60 EXPORT	int	lmknod		__PR((char *name, mode_t mode, dev_t dev));
61 EXPORT	int	lmkfifo		__PR((char *name, mode_t mode));
62 EXPORT	int	lchownat	__PR((char *name, uid_t uid, gid_t gid, int flag));
63 EXPORT	int	lunlinkat	__PR((char *name, int flag));
64 EXPORT	char	*lmktemp	__PR((char *name));
65 EXPORT	FILE	*lfilemopen	__PR((char *name, char *mode, mode_t cmode));
66 LOCAL	int	_fileopenat	__PR((int fd, char *name, char *mode));
67 EXPORT	int	_lfileopen	__PR((char *name, char *mode));
68 EXPORT	DIR	*lopendir	__PR((char *name));
69 EXPORT	int	hop_dirs	__PR((char *name, char **np));
70 
71 /*
72  * A chdir() implementation that is able to deal with ENAMETOOLONG.
73  */
74 EXPORT int
lchdir(path)75 lchdir(path)
76 	char	*path;
77 {
78 	int	ret = chdir(path);
79 	char	*p;
80 	char	*p2;
81 
82 	if (ret >= 0 || geterrno() != ENAMETOOLONG)
83 		return (ret);
84 
85 	p = path;
86 	if (*p == '/') {
87 		if ((ret = chdir("/")) < 0)
88 			return (ret);
89 		while (*p == '/')
90 			p++;
91 	}
92 	while (*p) {
93 		if ((p2 =  strchr(p, '/')) != NULL)
94 			*p2 = '\0';
95 		if ((ret = chdir(p)) < 0) {
96 			if (p2)
97 				*p2 = '/';
98 			break;
99 		}
100 		if (p2 == NULL)
101 			break;
102 		*p2++ = '/';
103 		while (*p2 == '/')
104 			p2++;
105 		p = p2;
106 	}
107 	return (ret);
108 }
109 
110 #if	defined(IS_SUN) && defined(__SVR4) && defined(DO_CHDIR_LONG)
111 /*
112  * Workaround a Solaris getcwd() bug that hits on newer Solaris releases with
113  * getcwd() being a system call. Once a successful getcwd() call that returns
114  * more than PATH_MAX did succeed, future calls to getcwd() always fail with
115  * ERANGE until a successfull new chdir() call has been issued.
116  */
117 LOCAL void
sunos5_cwdfix()118 sunos5_cwdfix()
119 {
120 	int	f = open(".", O_SEARCH|O_DIRECTORY|O_NDELAY);
121 
122 	if (f >= 0) {
123 		chdir("/");
124 		fchdir(f);
125 		close(f);
126 	}
127 }
128 #endif
129 
130 /*
131  * A getcwd() implementation that is able to deal with more than PATH_MAX.
132  */
133 EXPORT char *
lgetcwd()134 lgetcwd()
135 {
136 	char	*dir = NULL;
137 	char	*r;
138 	size_t	len = PATH_MAX;
139 #define	LWD_RETRY_MAX	8			/* stops at 128 * PATH_MAX */
140 	int	i = 0;
141 
142 #if	defined(IS_SUN) && defined(__SVR4) && defined(DO_CHDIR_LONG)
143 retry:
144 #endif
145 	do {
146 		dir = ___realloc(dir, len, "working dir");
147 		*dir = '\0';
148 
149 		r = getcwd(dir, len);
150 		if (++i >= LWD_RETRY_MAX)	/* stops at 128 * PATH_MAX */
151 			break;
152 		len *= 2;
153 	} while (r == NULL && errno == ERANGE);
154 
155 #if	defined(IS_SUN) && defined(__SVR4) && defined(DO_CHDIR_LONG)
156 	if (r == NULL && errno == ERANGE && i == LWD_RETRY_MAX) {
157 		/*
158 		 * Work around a Solaris kernel bug.
159 		 */
160 		sunos5_cwdfix();
161 		goto retry;
162 	}
163 #endif
164 	return (r);
165 }
166 
167 EXPORT int
168 #ifdef	PROTOTYPES
lmkdir(char * name,mode_t mode)169 lmkdir(char *name, mode_t mode)
170 #else
171 lmkdir(name, mode)
172 	char		*name;
173 	mode_t		mode;
174 #endif
175 {
176 #ifdef	HAVE_FCHDIR
177 	char	*p;
178 	int	fd;
179 	int	err;
180 #endif
181 	int	ret;
182 
183 	if ((ret = mkdir(name, mode)) < 0 &&
184 	    geterrno() != ENAMETOOLONG) {
185 		return (ret);
186 	}
187 
188 #ifdef	HAVE_FCHDIR
189 	if (ret >= 0)
190 		return (ret);
191 
192 	fd = hop_dirs(name, &p);
193 	ret = mkdirat(fd, p, mode);
194 	err = geterrno();
195 	close(fd);
196 	seterrno(err);
197 #endif
198 	return (ret);
199 }
200 
201 EXPORT int
laccess(name,amode)202 laccess(name, amode)
203 	char		*name;
204 	int		amode;
205 {
206 #ifdef	HAVE_FCHDIR
207 	char	*p;
208 	int	fd;
209 	int	err;
210 #endif
211 	int	ret;
212 
213 	if ((ret = access(name, amode)) < 0 &&
214 	    geterrno() != ENAMETOOLONG) {
215 		return (ret);
216 	}
217 
218 #ifdef	HAVE_FCHDIR
219 	if (ret >= 0)
220 		return (ret);
221 
222 	fd = hop_dirs(name, &p);
223 	ret = faccessat(fd, p, amode, 0);
224 	err = geterrno();
225 	close(fd);
226 	seterrno(err);
227 #endif
228 	return (ret);
229 }
230 
231 EXPORT int
lstatat(name,buf,flag)232 lstatat(name, buf, flag)
233 	char		*name;
234 	struct stat	*buf;
235 	int		flag;
236 {
237 #ifdef	HAVE_FCHDIR
238 	char	*p;
239 	int	fd;
240 	int	err;
241 #endif
242 	int	ret;
243 
244 	if ((ret = fstatat(AT_FDCWD, name, buf, flag)) < 0 &&
245 	    geterrno() != ENAMETOOLONG) {
246 		return (ret);
247 	}
248 
249 #ifdef	HAVE_FCHDIR
250 	if (ret >= 0)
251 		return (ret);
252 
253 	fd = hop_dirs(name, &p);
254 	ret = fstatat(fd, p, buf, flag);
255 	err = geterrno();
256 	close(fd);
257 	seterrno(err);
258 #endif
259 	return (ret);
260 }
261 
262 
263 EXPORT int
264 #ifdef	PROTOTYPES
lchmodat(char * name,mode_t mode,int flag)265 lchmodat(char *name, mode_t mode, int flag)
266 #else
267 lchmodat(name, mode, flag)
268 	char		*name;
269 	mode_t		mode;
270 	int		flag;
271 #endif
272 {
273 #ifdef	HAVE_FCHDIR
274 	char	*p;
275 	int	fd;
276 	int	err;
277 #endif
278 	int	ret;
279 
280 	if ((ret = fchmodat(AT_FDCWD, name, mode, flag)) < 0 &&
281 	    geterrno() != ENAMETOOLONG) {
282 		return (ret);
283 	}
284 
285 #ifdef	HAVE_FCHDIR
286 	if (ret >= 0)
287 		return (ret);
288 
289 	fd = hop_dirs(name, &p);
290 	ret = fchmodat(fd, p, mode, flag);
291 	err = geterrno();
292 	close(fd);
293 	seterrno(err);
294 #endif
295 	return (ret);
296 }
297 
298 
299 EXPORT int
lutimensat(name,ts,flag)300 lutimensat(name, ts, flag)
301 	char		*name;
302 	struct timespec	*ts;
303 	int		flag;
304 {
305 #ifdef	HAVE_FCHDIR
306 	char	*p;
307 	int	fd;
308 	int	err;
309 #endif
310 	int	ret;
311 
312 	if ((ret = utimensat(AT_FDCWD, name, ts, flag)) < 0 &&
313 	    geterrno() != ENAMETOOLONG) {
314 		return (ret);
315 	}
316 
317 #ifdef	HAVE_FCHDIR
318 	if (ret >= 0)
319 		return (ret);
320 
321 	fd = hop_dirs(name, &p);
322 	ret = utimensat(fd, p, ts, flag);
323 	err = geterrno();
324 	close(fd);
325 	seterrno(err);
326 #endif
327 	return (ret);
328 }
329 
330 #ifdef	HAVE_READLINK
331 EXPORT int
lreadlink(name,buf,bfsize)332 lreadlink(name, buf, bfsize)
333 	char		*name;
334 	char		*buf;
335 	size_t		bfsize;
336 {
337 #ifdef	HAVE_FCHDIR
338 	char	*p;
339 	int	fd;
340 	int	err;
341 #endif
342 	int	ret;
343 
344 	if ((ret = readlink(name, buf, bfsize)) < 0 &&
345 	    geterrno() != ENAMETOOLONG) {
346 		return (ret);
347 	}
348 
349 #ifdef	HAVE_FCHDIR
350 	if (ret >= 0)
351 		return (ret);
352 
353 	fd = hop_dirs(name, &p);
354 	ret = readlinkat(fd, p, buf, bfsize);
355 	err = geterrno();
356 	close(fd);
357 	seterrno(err);
358 #endif
359 	return (ret);
360 }
361 
362 EXPORT int
lsymlink(name,name2)363 lsymlink(name, name2)
364 	char		*name;
365 	char		*name2;
366 {
367 #ifdef	HAVE_FCHDIR
368 	char	*p;
369 	int	fd;
370 	int	err;
371 #endif
372 	int	ret;
373 
374 	if ((ret = symlink(name, name2)) < 0 &&
375 	    geterrno() != ENAMETOOLONG) {
376 		return (ret);
377 	}
378 
379 #ifdef	HAVE_FCHDIR
380 	if (ret >= 0)
381 		return (ret);
382 
383 	fd = hop_dirs(name2, &p);
384 	ret = symlinkat(name, fd, p);
385 	err = geterrno();
386 	close(fd);
387 	seterrno(err);
388 #endif
389 	return (ret);
390 }
391 #endif
392 
393 #ifdef	HAVE_LINK
394 EXPORT int
llink(name,name2)395 llink(name, name2)
396 	char		*name;
397 	char		*name2;
398 {
399 #ifdef	HAVE_FCHDIR
400 	char	*p;
401 	char	*p2;
402 	int	fd;
403 	int	fd2;
404 	int	err;
405 #endif
406 	int	ret;
407 
408 	if ((ret = link(name, name2)) < 0 &&
409 	    geterrno() != ENAMETOOLONG) {
410 		return (ret);
411 	}
412 
413 #ifdef	HAVE_FCHDIR
414 	if (ret >= 0)
415 		return (ret);
416 
417 	fd = hop_dirs(name, &p);
418 	fd2 = hop_dirs(name2, &p2);
419 	ret = linkat(fd, p, fd2, p2, 0); /* SYMLINK_FOLLOW not in emulation */
420 	err = geterrno();
421 	close(fd);
422 	close(fd2);
423 	seterrno(err);
424 #endif
425 	return (ret);
426 }
427 #endif
428 
429 EXPORT int
lrename(name,name2)430 lrename(name, name2)
431 	char		*name;
432 	char		*name2;
433 {
434 #ifdef	HAVE_FCHDIR
435 	char	*p;
436 	char	*p2;
437 	int	fd;
438 	int	fd2;
439 	int	err;
440 #endif
441 	int	ret;
442 
443 	if ((ret = rename(name, name2)) < 0 &&
444 	    geterrno() != ENAMETOOLONG) {
445 		return (ret);
446 	}
447 
448 #ifdef	HAVE_FCHDIR
449 	if (ret >= 0)
450 		return (ret);
451 
452 	fd = hop_dirs(name, &p);
453 	fd2 = hop_dirs(name2, &p2);
454 	ret = renameat(fd, p, fd2, p2);
455 	err = geterrno();
456 	close(fd);
457 	close(fd2);
458 	seterrno(err);
459 #endif
460 	return (ret);
461 }
462 
463 #ifdef	HAVE_MKNOD
464 EXPORT int
465 #ifdef	PROTOTYPES
lmknod(char * name,mode_t mode,dev_t dev)466 lmknod(char *name, mode_t mode, dev_t dev)
467 #else
468 lmknod(name, mode, dev)
469 	char		*name;
470 	mode_t		mode;
471 	dev_t		dev;
472 #endif
473 {
474 #ifdef	HAVE_FCHDIR
475 	char	*p;
476 	int	fd;
477 	int	err;
478 #endif
479 	int	ret;
480 
481 	if ((ret = mknod(name, mode, dev)) < 0 &&
482 	    geterrno() != ENAMETOOLONG) {
483 		return (ret);
484 	}
485 
486 #ifdef	HAVE_FCHDIR
487 	if (ret >= 0)
488 		return (ret);
489 
490 	fd = hop_dirs(name, &p);
491 	ret = mknodat(fd, p, mode, dev);
492 	err = geterrno();
493 	close(fd);
494 	seterrno(err);
495 #endif
496 	return (ret);
497 }
498 #endif
499 
500 #ifdef	HAVE_MKFIFO
501 EXPORT int
502 #ifdef	PROTOTYPES
lmkfifo(char * name,mode_t mode)503 lmkfifo(char *name, mode_t mode)
504 #else
505 lmkfifo(name, mode)
506 	char		*name;
507 	mode_t		mode;
508 #endif
509 {
510 #ifdef	HAVE_FCHDIR
511 	char	*p;
512 	int	fd;
513 	int	err;
514 #endif
515 	int	ret;
516 
517 	if ((ret = mkfifo(name, mode)) < 0 &&
518 	    geterrno() != ENAMETOOLONG) {
519 		return (ret);
520 	}
521 
522 #ifdef	HAVE_FCHDIR
523 	if (ret >= 0)
524 		return (ret);
525 
526 	fd = hop_dirs(name, &p);
527 	ret = mkfifoat(fd, p, mode);
528 	err = geterrno();
529 	close(fd);
530 	seterrno(err);
531 #endif
532 	return (ret);
533 }
534 #endif
535 
536 EXPORT int
537 #ifdef	PROTOTYPES
lchownat(char * name,uid_t uid,gid_t gid,int flag)538 lchownat(char *name, uid_t uid, gid_t gid, int flag)
539 #else
540 lchownat(name, uid, gid, flag)
541 	char		*name;
542 	uid_t		uid;
543 	gid_t		gid;
544 	int		flag;
545 #endif
546 {
547 #ifdef	HAVE_FCHDIR
548 	char	*p;
549 	int	fd;
550 	int	err;
551 #endif
552 	int	ret;
553 
554 	if ((ret = fchownat(AT_FDCWD, name, uid, gid, flag)) < 0 &&
555 	    geterrno() != ENAMETOOLONG) {
556 		return (ret);
557 	}
558 
559 #ifdef	HAVE_FCHDIR
560 	if (ret >= 0)
561 		return (ret);
562 
563 	fd = hop_dirs(name, &p);
564 	ret = fchownat(fd, p, uid, gid, flag);
565 	err = geterrno();
566 	close(fd);
567 	seterrno(err);
568 #endif
569 	return (ret);
570 }
571 
572 EXPORT int
lunlinkat(name,flag)573 lunlinkat(name, flag)
574 	char		*name;
575 	int		flag;
576 {
577 #ifdef	HAVE_FCHDIR
578 	char	*p;
579 	int	fd;
580 	int	err;
581 #endif
582 	int	ret;
583 
584 	if ((ret = unlinkat(AT_FDCWD, name, flag)) < 0 &&
585 	    geterrno() != ENAMETOOLONG) {
586 		return (ret);
587 	}
588 
589 #ifdef	HAVE_FCHDIR
590 	if (ret >= 0)
591 		return (ret);
592 
593 	fd = hop_dirs(name, &p);
594 	ret = unlinkat(fd, p, flag);
595 	err = geterrno();
596 	close(fd);
597 	seterrno(err);
598 #endif
599 	return (ret);
600 }
601 
602 EXPORT char *
lmktemp(name)603 lmktemp(name)
604 	char		*name;
605 {
606 #ifdef	HAVE_FCHDIR
607 	char	*p;
608 	int	fd;
609 	int	fdh;
610 	int	err = 0;
611 	char	spath[PATH_MAX+1];
612 	char	*oname = spath;
613 #endif
614 	char	*ret;
615 
616 	if (name == NULL)
617 		return (name);
618 
619 #ifdef	HAVE_FCHDIR
620 	/*
621 	 * mktemp() is destructive, so we need to save the name before
622 	 * as we always fail first with long path names.
623 	 */
624 	if (strlen(name) < PATH_MAX) {
625 		strcpy(oname, name);
626 	} else {
627 		oname = strdup(name);
628 		if (oname == NULL) {
629 			name[0] = '\0';
630 			return (name);
631 		}
632 	}
633 #endif
634 	ret = mktemp(name);
635 	if (ret[0] == '\0' &&
636 	    geterrno() != ENAMETOOLONG) {
637 #ifdef	HAVE_FCHDIR
638 		if (oname != spath)
639 			free(oname);
640 #endif
641 		return (ret);
642 	}
643 
644 #ifdef	HAVE_FCHDIR
645 	if (ret[0] != '\0') {
646 		if (oname != spath)
647 			free(oname);
648 		return (ret);
649 	}
650 
651 	strcpy(name, oname);
652 	if (oname != spath)
653 		free(oname);
654 	fd = hop_dirs(name, &p);
655 	if (fd >= 0) {
656 		fdh = open(".", O_SEARCH|O_DIRECTORY|O_NDELAY);
657 		if (fdh >= 0) {
658 			fchdir(fd);
659 			ret = mktemp(p);
660 			err = geterrno();
661 			fchdir(fdh);
662 			close(fdh);
663 		}
664 		close(fd);
665 	}
666 	if (err)
667 		seterrno(err);
668 #endif
669 	return (ret);
670 }
671 
672 EXPORT FILE *
673 #ifdef	PROTOTYPES
lfilemopen(char * name,char * mode,mode_t cmode)674 lfilemopen(char *name, char *mode, mode_t cmode)
675 #else
676 lfilemopen(name, mode, cmode)
677 	char		*name;
678 	char		*mode;
679 	mode_t		cmode;
680 #endif
681 {
682 #ifdef	HAVE_FCHDIR
683 	char	*p;
684 	char	*p2;
685 	int	fd;
686 	int	fdf;
687 	int	err;
688 	char	lmode[10];
689 #endif
690 	FILE	*fp;
691 	int	omode = 0;
692 	int	flag = 0;
693 
694 	if ((fp = filemopen(name, mode, cmode)) == NULL &&
695 	    geterrno() != ENAMETOOLONG) {
696 		return (fp);
697 	}
698 
699 #ifdef	HAVE_FCHDIR
700 	if (fp != NULL)
701 		return (fp);
702 
703 	fd = hop_dirs(name, &p);
704 	_cvmod(mode, &omode, &flag);
705 	fdf = openat(fd, p, omode, cmode);
706 	p = mode;
707 	p2 = lmode;
708 	while (*p) {
709 		if (*p == 'c' || *p == 't' || *p == 'e') {
710 			p++;
711 			continue;
712 		}
713 		*p2++ = *p++;
714 	}
715 	*p2 = '\0';
716 	if (fdf >= 0 && (fp = fileluopen(fdf, lmode)) == NULL)
717 		close(fdf);
718 	err = geterrno();
719 	close(fd);
720 	seterrno(err);
721 #endif
722 	return (fp);
723 }
724 
725 LOCAL int
_fileopenat(fd,name,smode)726 _fileopenat(fd, name, smode)
727 	int	fd;
728 	char	*name;
729 	char	*smode;
730 {
731 	int	ret;
732 	int	omode = 0;
733 	int	flag = 0;
734 
735 	if (!_cvmod(smode, &omode, &flag))
736 		return (-1);
737 
738 retry:
739 	if ((ret = openat(fd, name, omode, S_IRWALL)) < 0) {
740 		if (geterrno() == EINTR)
741 			goto retry;
742 		return (-1);
743 	}
744 
745 	return (ret);
746 }
747 
748 EXPORT int
_lfileopen(name,smode)749 _lfileopen(name, smode)
750 	char	*name;
751 	char	*smode;
752 {
753 #ifdef	HAVE_FCHDIR
754 	char	*p;
755 	int	fd;
756 	int	err;
757 #endif
758 	int	fdf;
759 
760 	if ((fdf = _fileopenat(AT_FDCWD, name, smode)) < 0 &&
761 	    geterrno() != ENAMETOOLONG) {
762 		return (fdf);
763 	}
764 
765 #ifdef	HAVE_FCHDIR
766 	if (fdf >= 0)
767 		return (fdf);
768 
769 	fd = hop_dirs(name, &p);
770 	fdf = _fileopenat(fd, p, smode);
771 	err = geterrno();
772 	close(fd);
773 	seterrno(err);
774 #endif
775 	return (fdf);
776 }
777 
778 EXPORT DIR *
lopendir(name)779 lopendir(name)
780 	char	*name;
781 {
782 #ifdef	HAVE_FCHDIR
783 	char	*p;
784 	int	fd;
785 	int	dfd;
786 #endif
787 	DIR	*ret = NULL;
788 
789 	if ((ret = opendir(name)) == NULL && geterrno() != ENAMETOOLONG)
790 		return ((DIR *)NULL);
791 
792 #ifdef	HAVE_FCHDIR
793 	if (ret)
794 		return (ret);
795 
796 	fd = hop_dirs(name, &p);
797 	if ((dfd = openat(fd, p, O_RDONLY|O_DIRECTORY|O_NDELAY)) < 0) {
798 		close(fd);
799 		return ((DIR *)NULL);
800 	}
801 	close(fd);
802 	ret = fdopendir(dfd);
803 #endif
804 	return (ret);
805 }
806 
807 #ifdef	HAVE_FCHDIR
808 EXPORT int
hop_dirs(name,np)809 hop_dirs(name, np)
810 	char	*name;
811 	char	**np;
812 {
813 	char	*p;
814 	char	*p2;
815 	int	fd;
816 	int	dfd;
817 	int	err;
818 
819 	p = name;
820 	fd = AT_FDCWD;
821 	if (*p == '/') {
822 		fd = openat(fd, "/", O_SEARCH|O_DIRECTORY|O_NDELAY);
823 		while (*p == '/')
824 			p++;
825 	}
826 	while (*p) {
827 		if ((p2 = strchr(p, '/')) != NULL) {
828 			if (p2[1] == '\0')
829 				break;
830 			*p2 = '\0';
831 		} else {
832 			break;	/* No slash remains, return the prefix fd */
833 		}
834 		if ((dfd = openat(fd, p, O_SEARCH|O_DIRECTORY|O_NDELAY)) < 0) {
835 			err = geterrno();
836 
837 			close(fd);
838 			if (err == EMFILE)
839 				seterrno(err);
840 			else
841 				seterrno(ENAMETOOLONG);
842 			*p2 = '/';
843 			return (dfd);
844 		}
845 		close(fd);	/* Don't care about AT_FDCWD, it is negative */
846 		fd = dfd;
847 		*p2++ = '/';	/* p2 is always != NULL here */
848 		while (*p2 == '/')
849 			p2++;
850 		p = p2;
851 	}
852 	*np = p;
853 	return (fd);
854 }
855 #endif
856