xref: /illumos-gate/usr/src/lib/libc/port/rt/pos4obj.c (revision 1da57d551424de5a9d469760be7c4b4d4f10a755)
1f841f6adSraf /*
2f841f6adSraf  * CDDL HEADER START
3f841f6adSraf  *
4f841f6adSraf  * The contents of this file are subject to the terms of the
5f841f6adSraf  * Common Development and Distribution License (the "License").
6f841f6adSraf  * You may not use this file except in compliance with the License.
7f841f6adSraf  *
8f841f6adSraf  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9f841f6adSraf  * or http://www.opensolaris.org/os/licensing.
10f841f6adSraf  * See the License for the specific language governing permissions
11f841f6adSraf  * and limitations under the License.
12f841f6adSraf  *
13f841f6adSraf  * When distributing Covered Code, include this CDDL HEADER in each
14f841f6adSraf  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15f841f6adSraf  * If applicable, add the following below this CDDL HEADER, with the
16f841f6adSraf  * fields enclosed by brackets "[]" replaced with your own identifying
17f841f6adSraf  * information: Portions Copyright [yyyy] [name of copyright owner]
18f841f6adSraf  *
19f841f6adSraf  * CDDL HEADER END
20f841f6adSraf  */
21f841f6adSraf 
22f841f6adSraf /*
23a574db85Sraf  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24f841f6adSraf  * Use is subject to license terms.
25f841f6adSraf  */
26f841f6adSraf 
27*7257d1b4Sraf #include "lint.h"
28f841f6adSraf #include "mtlib.h"
29f841f6adSraf #include <sys/types.h>
30f841f6adSraf #include <errno.h>
31f841f6adSraf #include <fcntl.h>
32f841f6adSraf #include <sys/stat.h>
33f841f6adSraf #include <unistd.h>
34f841f6adSraf #include <stdlib.h>
35f841f6adSraf #include <limits.h>
36f841f6adSraf #include <pthread.h>
37f841f6adSraf #include <thread.h>
38f841f6adSraf #include <string.h>
39f841f6adSraf #include <dirent.h>
40f841f6adSraf #include <stdio.h>
41f841f6adSraf #include <dlfcn.h>
428cd45542Sraf #include <atomic.h>
43f841f6adSraf #include <md5.h>
44f841f6adSraf #include "pos4obj.h"
45f841f6adSraf 
46f841f6adSraf #define	HASHSTRLEN	32
47f841f6adSraf 
48f841f6adSraf static	char	*__pos4obj_name(const char *, const char *);
49f841f6adSraf static	void	__pos4obj_md5toa(unsigned char *, unsigned char *);
50f841f6adSraf static	void	__pos4obj_clean(char *);
51f841f6adSraf 
52f841f6adSraf static	char	objroot[] = "/tmp/";
53f841f6adSraf static	long int	name_max = 0;
54f841f6adSraf 
55f841f6adSraf int
__open_nc(const char * path,int oflag,mode_t mode)56f841f6adSraf __open_nc(const char *path, int oflag, mode_t mode)
57f841f6adSraf {
58a574db85Sraf 	int		cancel_state;
59a574db85Sraf 	int		val;
60f841f6adSraf 	struct stat64	statbuf;
61f841f6adSraf 
62f841f6adSraf 	/*
63f841f6adSraf 	 * Ensure path is not a symlink to somewhere else. This provides
64f841f6adSraf 	 * a modest amount of protection against easy security attacks.
65f841f6adSraf 	 */
66f841f6adSraf 	if (lstat64(path, &statbuf) == 0) {
67f841f6adSraf 		if (S_ISLNK(statbuf.st_mode)) {
68f841f6adSraf 			errno = EINVAL;
69f841f6adSraf 			return (-1);
70f841f6adSraf 		}
71f841f6adSraf 	}
72f841f6adSraf 
73a574db85Sraf 	(void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_state);
74f841f6adSraf 	val = open64(path, oflag, mode);
75a574db85Sraf 	(void) pthread_setcancelstate(cancel_state, NULL);
76f841f6adSraf 
77f841f6adSraf 	return (val);
78f841f6adSraf }
79f841f6adSraf 
80f841f6adSraf int
__close_nc(int fildes)81f841f6adSraf __close_nc(int fildes)
82f841f6adSraf {
83a574db85Sraf 	int	cancel_state;
84a574db85Sraf 	int	val;
85f841f6adSraf 
86a574db85Sraf 	(void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_state);
87f841f6adSraf 	val = close(fildes);
88a574db85Sraf 	(void) pthread_setcancelstate(cancel_state, NULL);
89f841f6adSraf 
90f841f6adSraf 	return (val);
91f841f6adSraf }
92f841f6adSraf 
93f841f6adSraf /*
94f841f6adSraf  * This is to avoid loading libmd.so.1 unless we absolutely have to.
95f841f6adSraf  */
96f841f6adSraf typedef void (*md5_calc_t)(unsigned char *, unsigned char *, unsigned int);
97f841f6adSraf static md5_calc_t real_md5_calc = NULL;
98f841f6adSraf static mutex_t md5_lock = DEFAULTMUTEX;
99f841f6adSraf 
100f841f6adSraf static void
load_md5_calc(void)101f841f6adSraf load_md5_calc(void)
102f841f6adSraf {
103f90fe29eSraf 	void *md5_handle = dlopen("libmd.so.1", RTLD_LAZY);
1048cd45542Sraf 	md5_calc_t md5_calc = (md5_handle == NULL)? NULL :
1058cd45542Sraf 	    (md5_calc_t)dlsym(md5_handle, "md5_calc");
106f90fe29eSraf 
107f841f6adSraf 	lmutex_lock(&md5_lock);
108f841f6adSraf 	if (real_md5_calc == NULL) {
1098cd45542Sraf 		if (md5_calc == NULL)
110f841f6adSraf 			real_md5_calc = (md5_calc_t)(-1);
111f841f6adSraf 		else {
1128cd45542Sraf 			real_md5_calc = md5_calc;
113f90fe29eSraf 			md5_handle = NULL;	/* don't dlclose it */
114f841f6adSraf 		}
1158cd45542Sraf 		membar_producer();
116f841f6adSraf 	}
117f841f6adSraf 	lmutex_unlock(&md5_lock);
118f90fe29eSraf 
119f90fe29eSraf 	if (md5_handle)
120f90fe29eSraf 		(void) dlclose(md5_handle);
121f841f6adSraf }
122f841f6adSraf 
123f841f6adSraf static char *
__pos4obj_name(const char * path,const char * type)124f841f6adSraf __pos4obj_name(const char *path, const char *type)
125f841f6adSraf {
126f841f6adSraf 	int	shortpath = 1;
127f841f6adSraf 	int	olderrno;
128f841f6adSraf 	size_t	len;
129f841f6adSraf 	char	*dfile;
130f841f6adSraf 	unsigned char	hashbuf[HASHSTRLEN + 1];
131f841f6adSraf 	unsigned char	md5_digest[MD5_DIGEST_LENGTH];
132f841f6adSraf 
133f841f6adSraf 	/*
134f841f6adSraf 	 * If the path is path_max - strlen(type) characters or less,
135f841f6adSraf 	 * the name of the file to use will be the path prefixed by
136f841f6adSraf 	 * the type.
137f841f6adSraf 	 *
138f841f6adSraf 	 * In the special case where the path is longer than
139f841f6adSraf 	 * path_max - strlen(type) characters, we create a string based on the
140f841f6adSraf 	 * MD5 hash of the path. We prefix that string with a '.' to
141f841f6adSraf 	 * make it obscure, and create a directory in objroot with
142f841f6adSraf 	 * that name. In that directory, we create a directory named
143f841f6adSraf 	 * after the type of object requested.  Inside the type
144f841f6adSraf 	 * directory, the filename will be the path of the object. This
145f841f6adSraf 	 * prevents collisions in all namespaces.
146f841f6adSraf 	 *
147f841f6adSraf 	 * Example:
148f841f6adSraf 	 * Let objroot = "/tmp/", path = "/<longpath>", and type = ".MQD"
149f841f6adSraf 	 * Let the MD5 hash of "<longpath>" = "<hash>"
150f841f6adSraf 	 *
151f841f6adSraf 	 * The desired file is /tmp/.<hash>/.MQD/<longpath>
152f841f6adSraf 	 */
153f841f6adSraf 
154f841f6adSraf 	/*
155f841f6adSraf 	 * Do not include the leading '/' in the path length.
156f841f6adSraf 	 * Assumes __pos4obj_check(path) has already been called.
157f841f6adSraf 	 */
158f841f6adSraf 	if ((strlen(path) - 1) > (name_max - strlen(type)))
159f841f6adSraf 		shortpath = 0;
160f841f6adSraf 
161f841f6adSraf 	if (shortpath) {
162f841f6adSraf 		/*
163f841f6adSraf 		 * strlen(path) includes leading slash as space for NUL.
164f841f6adSraf 		 */
165f841f6adSraf 		len = strlen(objroot) + strlen(type) + strlen(path);
166f841f6adSraf 	} else {
167f841f6adSraf 		/*
168f841f6adSraf 		 * Long path name. Add 3 for extra '/', '.' and '\0'
169f841f6adSraf 		 */
170f841f6adSraf 		len = strlen(objroot) + HASHSTRLEN + strlen(type) +
171f841f6adSraf 		    strlen(path) + 3;
172f841f6adSraf 	}
173f841f6adSraf 
174f841f6adSraf 	if ((dfile = malloc(len)) == NULL)
175f841f6adSraf 		return (NULL);
176f841f6adSraf 
177f841f6adSraf 	(void) memset(dfile, 0, len);
178f841f6adSraf 	(void) strcpy(dfile, objroot);
179f841f6adSraf 
180f841f6adSraf 	if (shortpath) {
181f841f6adSraf 		(void) strcat(dfile, type);
182f841f6adSraf 		(void) strcat(dfile, path + 1);
183f841f6adSraf 		return (dfile);
184f841f6adSraf 	}
185f841f6adSraf 
186f841f6adSraf 	/*
187f841f6adSraf 	 * If we can successfully load it, call md5_calc().
188f841f6adSraf 	 * Otherwise, (this "can't happen") return NULL.
189f841f6adSraf 	 */
190f841f6adSraf 	if (real_md5_calc == NULL)
191f841f6adSraf 		load_md5_calc();
192f841f6adSraf 	if (real_md5_calc == (md5_calc_t)(-1)) {
193f841f6adSraf 		free(dfile);
194f841f6adSraf 		return (NULL);
195f841f6adSraf 	}
196f841f6adSraf 
197f841f6adSraf 	real_md5_calc(md5_digest, (unsigned char *)path + 1, strlen(path + 1));
198f841f6adSraf 	__pos4obj_md5toa(hashbuf, md5_digest);
199f841f6adSraf 	(void) strcat(dfile, ".");
200f841f6adSraf 	(void) strcat(dfile, (const char *)hashbuf);
201f841f6adSraf 
202f841f6adSraf 	/*
203f841f6adSraf 	 * Errno must be preserved across the following calls to
204f841f6adSraf 	 * mkdir.  This needs to be done to prevent incorrect error
205f841f6adSraf 	 * reporting in certain cases. When we attempt to open a
206f841f6adSraf 	 * non-existent object without the O_CREAT flag, it will
207f841f6adSraf 	 * always create a lock file first.  The lock file is created
208f841f6adSraf 	 * and then the open is attempted, but fails with ENOENT. The
209f841f6adSraf 	 * lock file is then destroyed. In the following code path, we
210f841f6adSraf 	 * are finding the absolute path to the lock file after
211f841f6adSraf 	 * already having attempted the open (which set errno to
212f841f6adSraf 	 * ENOENT). The following calls to mkdir will return -1 and
213f841f6adSraf 	 * set errno to EEXIST, since the hash and type directories
214f841f6adSraf 	 * were created when the lock file was created. The correct
215f841f6adSraf 	 * errno is the ENOENT from the attempted open of the desired
216f841f6adSraf 	 * object.
217f841f6adSraf 	 */
218f841f6adSraf 	olderrno = errno;
219f841f6adSraf 
220f841f6adSraf 	/*
221f841f6adSraf 	 * Create hash directory. Use 777 permissions so everyone can use it.
222f841f6adSraf 	 */
223f841f6adSraf 	if (mkdir(dfile, S_IRWXU|S_IRWXG|S_IRWXO) == 0) {
224f841f6adSraf 		if (chmod(dfile, S_IRWXU|S_IRWXG|S_IRWXO) == -1) {
225f841f6adSraf 			free(dfile);
226f841f6adSraf 			return (NULL);
227f841f6adSraf 		}
228f841f6adSraf 	} else {
229f841f6adSraf 		if (errno != EEXIST) {
230f841f6adSraf 			free(dfile);
231f841f6adSraf 			return (NULL);
232f841f6adSraf 		}
233f841f6adSraf 	}
234f841f6adSraf 
235f841f6adSraf 	(void) strcat(dfile, "/");
236f841f6adSraf 	(void) strcat(dfile, type);
237f841f6adSraf 
238f841f6adSraf 	/*
239f841f6adSraf 	 * Create directory for requested type. Use 777 perms so everyone
240f841f6adSraf 	 * can use it.
241f841f6adSraf 	 */
242f841f6adSraf 	if (mkdir(dfile, S_IRWXU|S_IRWXG|S_IRWXO) == 0) {
243f841f6adSraf 		if (chmod(dfile, S_IRWXU|S_IRWXG|S_IRWXO) == -1) {
244f841f6adSraf 			free(dfile);
245f841f6adSraf 			return (NULL);
246f841f6adSraf 		}
247f841f6adSraf 	} else {
248f841f6adSraf 		if (errno != EEXIST) {
249f841f6adSraf 			free(dfile);
250f841f6adSraf 			return (NULL);
251f841f6adSraf 		}
252f841f6adSraf 	}
253f841f6adSraf 
254f841f6adSraf 	errno = olderrno;
255f841f6adSraf 	(void) strcat(dfile, path);
256f841f6adSraf 	return (dfile);
257f841f6adSraf }
258f841f6adSraf 
259f841f6adSraf /*
260f841f6adSraf  * Takes a 128-bit MD5 digest and transforms to a sequence of 32 ASCII
261f841f6adSraf  * characters. Output is the hexadecimal representation of the digest.
262f841f6adSraf  *
263f841f6adSraf  * The output buffer must be at least HASHSTRLEN + 1 characters
264f841f6adSraf  * long.  HASHSTRLEN is the size of the MD5 digest (128 bits)
265f841f6adSraf  * divided by the number of bits used per char of output (4). The
266f841f6adSraf  * extra character at the end is for the NUL terminating character.
267f841f6adSraf  */
268f841f6adSraf 
269f841f6adSraf static void
__pos4obj_md5toa(unsigned char * dest,unsigned char * src)270f841f6adSraf __pos4obj_md5toa(unsigned char *dest, unsigned char *src)
271f841f6adSraf {
272f841f6adSraf 	int i;
273f841f6adSraf 	uint32_t *p;
274f841f6adSraf 
275f841f6adSraf 	/* LINTED pointer cast may result in improper alignment */
276f841f6adSraf 	p = (uint32_t *)src;
277f841f6adSraf 
278f841f6adSraf 	for (i = 0; i < (MD5_DIGEST_LENGTH / 4); i++)
279f841f6adSraf 		(void) snprintf((char *)dest + (i * 8), 9, "%.8x", *p++);
280f841f6adSraf 
281f841f6adSraf 	dest[HASHSTRLEN] = '\0';
282f841f6adSraf }
283f841f6adSraf 
284f841f6adSraf /*
285f841f6adSraf  * This open function assume that there is no simultaneous
286f841f6adSraf  * open/unlink operation is going on. The caller is supposed
287f841f6adSraf  * to ensure that both open in O_CREAT mode happen atomically.
288f841f6adSraf  * It returns the crflag as 1 if file is created else 0.
289f841f6adSraf  */
290f841f6adSraf int
__pos4obj_open(const char * name,char * type,int oflag,mode_t mode,int * crflag)291f841f6adSraf __pos4obj_open(const char *name, char *type, int oflag,
292f841f6adSraf 		mode_t mode, int *crflag)
293f841f6adSraf {
294f841f6adSraf 	int fd;
295f841f6adSraf 	char *dfile;
296f841f6adSraf 
297f841f6adSraf 	errno = 0;
298f841f6adSraf 	*crflag = 0;
299f841f6adSraf 
300f841f6adSraf 	if ((dfile = __pos4obj_name(name, type)) == NULL) {
301f841f6adSraf 		return (-1);
302f841f6adSraf 	}
303f841f6adSraf 
304f841f6adSraf 	if (!(oflag & O_CREAT)) {
305f841f6adSraf 		if ((fd = __open_nc(dfile, oflag, mode)) == -1)
306f841f6adSraf 			__pos4obj_clean(dfile);
307f841f6adSraf 
308f841f6adSraf 		free(dfile);
309f841f6adSraf 		return (fd);
310f841f6adSraf 	}
311f841f6adSraf 
312f841f6adSraf 	/*
313f841f6adSraf 	 * We need to make sure that crflag is set iff we actually create
314f841f6adSraf 	 * the file.  We do this by or'ing in O_EXCL, and attempting an
315f841f6adSraf 	 * open.  If that fails with an EEXIST, and O_EXCL wasn't specified
316f841f6adSraf 	 * by the caller, then the file seems to exist;  we'll try an
317f841f6adSraf 	 * open with O_CREAT cleared.  If that succeeds, then the file
318f841f6adSraf 	 * did indeed exist.  If that fails with an ENOENT, however, the
319f841f6adSraf 	 * file was removed between the opens;  we need to take another
320f841f6adSraf 	 * lap.
321f841f6adSraf 	 */
322f841f6adSraf 	for (;;) {
323f841f6adSraf 		if ((fd = __open_nc(dfile, (oflag | O_EXCL), mode)) == -1) {
324f841f6adSraf 			if (errno == EEXIST && !(oflag & O_EXCL)) {
325f841f6adSraf 				fd = __open_nc(dfile, oflag & ~O_CREAT, mode);
326f841f6adSraf 
327f841f6adSraf 				if (fd == -1 && errno == ENOENT)
328f841f6adSraf 					continue;
329f841f6adSraf 				break;
330f841f6adSraf 			}
331f841f6adSraf 		} else {
332f841f6adSraf 			*crflag = 1;
333f841f6adSraf 		}
334f841f6adSraf 		break;
335f841f6adSraf 	}
336f841f6adSraf 
337f841f6adSraf 	free(dfile);
338f841f6adSraf 	return (fd);
339f841f6adSraf }
340f841f6adSraf 
341f841f6adSraf 
342f841f6adSraf int
__pos4obj_unlink(const char * name,const char * type)343f841f6adSraf __pos4obj_unlink(const char *name, const char *type)
344f841f6adSraf {
345f841f6adSraf 	int	err;
346f841f6adSraf 	char	*dfile;
347f841f6adSraf 
348f841f6adSraf 	if ((dfile = __pos4obj_name(name, type)) == NULL) {
349f841f6adSraf 		return (-1);
350f841f6adSraf 	}
351f841f6adSraf 
352f841f6adSraf 	err = unlink(dfile);
353f841f6adSraf 
354f841f6adSraf 	__pos4obj_clean(dfile);
355f841f6adSraf 
356f841f6adSraf 	free(dfile);
357f841f6adSraf 
358f841f6adSraf 	return (err);
359f841f6adSraf }
360f841f6adSraf 
361f841f6adSraf /*
362f841f6adSraf  * This function opens the lock file for each named object
363f841f6adSraf  * the presence of this file in the file system is the lock
364f841f6adSraf  */
365f841f6adSraf int
__pos4obj_lock(const char * name,const char * ltype)366f841f6adSraf __pos4obj_lock(const char *name, const char *ltype)
367f841f6adSraf {
368f841f6adSraf 	char	*dfile;
369f841f6adSraf 	int	fd;
370f841f6adSraf 	int	limit = 64;
371f841f6adSraf 
372f841f6adSraf 	if ((dfile = __pos4obj_name(name, ltype)) == NULL) {
373f841f6adSraf 		return (-1);
374f841f6adSraf 	}
375f841f6adSraf 
376f841f6adSraf 	while (limit-- > 0) {
377f841f6adSraf 		if ((fd = __open_nc(dfile, O_RDWR | O_CREAT | O_EXCL, 0666))
378f841f6adSraf 		    < 0) {
379f841f6adSraf 			if (errno != EEXIST)
380f841f6adSraf 				break;
381f841f6adSraf 			(void) sleep(1);
382f841f6adSraf 			continue;
383f841f6adSraf 		}
384f841f6adSraf 
385f841f6adSraf 		(void) __close_nc(fd);
386f841f6adSraf 		free(dfile);
387f841f6adSraf 		return (1);
388f841f6adSraf 	}
389f841f6adSraf 
390f841f6adSraf 	free(dfile);
391f841f6adSraf 	return (-1);
392f841f6adSraf }
393f841f6adSraf 
394f841f6adSraf /*
395f841f6adSraf  * Unlocks the file by unlinking it from the filesystem
396f841f6adSraf  */
397f841f6adSraf int
__pos4obj_unlock(const char * path,const char * type)398f841f6adSraf __pos4obj_unlock(const char *path, const char *type)
399f841f6adSraf {
400f841f6adSraf 	return (__pos4obj_unlink(path, type));
401f841f6adSraf }
402f841f6adSraf 
403f841f6adSraf /*
404f841f6adSraf  * Removes unused hash and type directories that may exist in specified path.
405f841f6adSraf  */
406f841f6adSraf static void
__pos4obj_clean(char * path)407f841f6adSraf __pos4obj_clean(char *path)
408f841f6adSraf {
409f841f6adSraf 	char	*p;
410f841f6adSraf 	int	olderrno;
411f841f6adSraf 
412f841f6adSraf 	/*
413f841f6adSraf 	 * path is either
414f841f6adSraf 	 * 1) /<objroot>/<type><path>  or
415f841f6adSraf 	 * 2) /<objroot>/.<hash>/<type>/<path>
416f841f6adSraf 	 *
417f841f6adSraf 	 * In case 1, there is nothing to clean.
418f841f6adSraf 	 *
419f841f6adSraf 	 * Detect case 2 by looking for a '/' after /objroot/ and
420f841f6adSraf 	 * remove the two trailing directories, if empty.
421f841f6adSraf 	 */
422f841f6adSraf 	if (strchr(path + strlen(objroot), '/') == NULL)
423f841f6adSraf 		return;
424f841f6adSraf 
425f841f6adSraf 	/*
426f841f6adSraf 	 * Preserve errno across calls to rmdir. See block comment in
427f841f6adSraf 	 * __pos4obj_name() for explanation.
428f841f6adSraf 	 */
429f841f6adSraf 	olderrno = errno;
430f841f6adSraf 
431f841f6adSraf 	if ((p = strrchr(path, '/')) == NULL)
432f841f6adSraf 		return;
433f841f6adSraf 	*p = '\0';
434f841f6adSraf 
435f841f6adSraf 	(void) rmdir(path);
436f841f6adSraf 
437f841f6adSraf 	if ((p = strrchr(path, '/')) == NULL)
438f841f6adSraf 		return;
439f841f6adSraf 	*p = '\0';
440f841f6adSraf 
441f841f6adSraf 	(void) rmdir(path);
442f841f6adSraf 
443f841f6adSraf 	errno = olderrno;
444f841f6adSraf }
445f841f6adSraf 
446f841f6adSraf 
447f841f6adSraf /*
448f841f6adSraf  * Check that path starts with a /, does not contain a / within it
449f841f6adSraf  * and is not longer than PATH_MAX or NAME_MAX
450f841f6adSraf  */
451f841f6adSraf int
__pos4obj_check(const char * path)452f841f6adSraf __pos4obj_check(const char *path)
453f841f6adSraf {
454f841f6adSraf 	long int	i;
455f841f6adSraf 
456f841f6adSraf 	/*
457f841f6adSraf 	 * This assumes that __pos4obj_check() is called before
458f841f6adSraf 	 * any of the other functions in this file
459f841f6adSraf 	 */
460f841f6adSraf 	if (name_max == 0 || name_max == -1) {
461f841f6adSraf 		name_max = pathconf(objroot, _PC_NAME_MAX);
462f841f6adSraf 		if (name_max == -1)
463f841f6adSraf 			return (-1);
464f841f6adSraf 	}
465f841f6adSraf 
466f841f6adSraf 	if (*path++ != '/') {
467f841f6adSraf 		errno = EINVAL;
468f841f6adSraf 		return (-1);
469f841f6adSraf 	}
470f841f6adSraf 
471f841f6adSraf 	for (i = 0; *path != '\0'; i++) {
472f841f6adSraf 		if (*path++ == '/') {
473f841f6adSraf 			errno = EINVAL;
474f841f6adSraf 			return (-1);
475f841f6adSraf 		}
476f841f6adSraf 	}
477f841f6adSraf 
478f841f6adSraf 	if (i > PATH_MAX || i > name_max) {
479f841f6adSraf 		errno = ENAMETOOLONG;
480f841f6adSraf 		return (-1);
481f841f6adSraf 	}
482f841f6adSraf 
483f841f6adSraf 	return (0);
484f841f6adSraf }
485