xref: /illumos-gate/usr/src/lib/libc/port/gen/readdir_r.c (revision a5f69788de7ac07553de47f7fec8c05a9a94c105)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*	Copyright (c) 1988 AT&T	*/
30 /*	  All Rights Reserved  	*/
31 
32 
33 /*
34  * readdir_r -- C library extension routine
35  */
36 
37 #include	<sys/feature_tests.h>
38 
39 #if !defined(_LP64)
40 #pragma weak readdir64_r = _readdir64_r
41 #endif
42 #pragma weak readdir_r = _readdir_r
43 
44 #include	"synonyms.h"
45 #include	<mtlib.h>
46 #include	<sys/types.h>
47 #include	<sys/dirent.h>
48 #include	<dirent.h>
49 #include	<thread.h>
50 #include	<string.h>
51 #include	<synch.h>
52 #include	<stdio.h>
53 #include	<limits.h>
54 #include	<errno.h>
55 #include	"libc.h"
56 
57 extern mutex_t	_dirent_lock;
58 
59 #define	LBUFSIZE	(sizeof (struct dirent64) + _POSIX_PATH_MAX + 1)
60 
61 #ifdef _LP64
62 
63 /*
64  * POSIX.1c standard version of the thread function readdir_r.
65  */
66 
67 int
68 readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result)
69 {
70 	struct dirent	*dp;	/* -> directory data */
71 	int saveloc = 0;
72 
73 	lmutex_lock(&_dirent_lock);
74 	if (dirp->dd_size != 0) {
75 		dp = (struct dirent *)(uintptr_t)&dirp->dd_buf[dirp->dd_loc];
76 		saveloc = dirp->dd_loc;   /* save for possible EOF */
77 		dirp->dd_loc += (int)dp->d_reclen;
78 	}
79 
80 	if (dirp->dd_loc >= dirp->dd_size)
81 		dirp->dd_loc = dirp->dd_size = 0;
82 
83 	if (dirp->dd_size == 0 &&	/* refill buffer */
84 	    (dirp->dd_size = getdents(dirp->dd_fd,
85 	    (struct dirent *)(uintptr_t)dirp->dd_buf, DIRBUF)) <= 0) {
86 		if (dirp->dd_size == 0) { /* This means EOF */
87 			dirp->dd_loc = saveloc;  /* EOF so save for telldir */
88 			lmutex_unlock(&_dirent_lock);
89 			*result = NULL;
90 			return (0); /* EOF */
91 		}
92 		lmutex_unlock(&_dirent_lock);
93 		*result = NULL;
94 		return (errno);	/* error */
95 	}
96 
97 	dp = (struct dirent *)(uintptr_t)&dirp->dd_buf[dirp->dd_loc];
98 	(void) memcpy(entry, dp, (size_t)dp->d_reclen);
99 	lmutex_unlock(&_dirent_lock);
100 	*result = entry;
101 	return (0);
102 }
103 
104 #else	/* _LP64 */
105 
106 /*
107  * POSIX.1c standard version of the thr function readdir_r.
108  * Large file version.
109  */
110 
111 int
112 readdir64_r(DIR *dirp, struct dirent64 *entry, struct dirent64 **result)
113 {
114 	struct dirent64	*dp64;	/* -> directory data */
115 	int saveloc = 0;
116 
117 	lmutex_lock(&_dirent_lock);
118 	if (dirp->dd_size != 0) {
119 		dp64 = (struct dirent64 *)
120 			(uintptr_t)&dirp->dd_buf[dirp->dd_loc];
121 		/* was converted by readdir and needs to be reversed */
122 		if (dp64->d_ino == (ino64_t)-1) {
123 			struct dirent	*dp32;	/* -> 32 bit directory data */
124 
125 			dp32 = (struct dirent *)(&dp64->d_off);
126 			dp64->d_ino = (ino64_t)dp32->d_ino;
127 			dp64->d_off = (off64_t)dp32->d_off;
128 			dp64->d_reclen = (unsigned short)(dp32->d_reclen +
129 				((char *)&dp64->d_off - (char *)dp64));
130 		}
131 		saveloc = dirp->dd_loc;   /* save for possible EOF */
132 		dirp->dd_loc += (int)dp64->d_reclen;
133 	}
134 
135 	if (dirp->dd_loc >= dirp->dd_size)
136 		dirp->dd_loc = dirp->dd_size = 0;
137 
138 	if (dirp->dd_size == 0 &&	/* refill buffer */
139 	    (dirp->dd_size = getdents64(dirp->dd_fd,
140 	    (struct dirent64 *)(uintptr_t)dirp->dd_buf, DIRBUF)) <= 0) {
141 		if (dirp->dd_size == 0) { /* This means EOF */
142 			dirp->dd_loc = saveloc;  /* EOF so save for telldir */
143 			lmutex_unlock(&_dirent_lock);
144 			*result = NULL;
145 			return (0); /* EOF */
146 		}
147 		lmutex_unlock(&_dirent_lock);
148 		*result = NULL;
149 		return (errno);	/* error */
150 	}
151 
152 	dp64 = (struct dirent64 *)(uintptr_t)&dirp->dd_buf[dirp->dd_loc];
153 	(void) memcpy(entry, dp64, (size_t)dp64->d_reclen);
154 	*result = entry;
155 	lmutex_unlock(&_dirent_lock);
156 	return (0);
157 }
158 
159 /*
160  * POSIX.1c Draft-6 version of the function readdir_r.
161  * It was implemented by Solaris 2.3.
162  */
163 
164 struct dirent *
165 readdir_r(DIR *dirp, struct dirent *entry)
166 {
167 	long buf[LBUFSIZE / sizeof (long) + 1];
168 	struct dirent64	*dp64;
169 
170 	if (readdir64_r(dirp, (struct dirent64 *)(uintptr_t)buf, &dp64) != 0 ||
171 	    dp64 == NULL)
172 		return (NULL);
173 
174 	if ((dp64->d_ino > SIZE_MAX) || ((uint64_t)dp64->d_off >
175 	    (uint64_t)UINT32_MAX)) {
176 		errno = EOVERFLOW;
177 		return (NULL);
178 	}
179 
180 	entry->d_ino = (ino_t)dp64->d_ino;
181 	entry->d_off = (off_t)dp64->d_off;
182 	entry->d_reclen = (unsigned short)((((char *)entry->d_name -
183 	    (char *)entry) + strlen(dp64->d_name) + 1 + 3) & ~3);
184 	(void) strcpy(entry->d_name, dp64->d_name);
185 
186 	return (entry);
187 }
188 
189 /*
190  * POSIX.1c standard version of the thr function readdir_r.
191  * User gets it via static readdir_r from header file.
192  */
193 
194 int
195 __posix_readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result)
196 {
197 	long buf[LBUFSIZE / sizeof (long) + 1];
198 	struct dirent64 *dp64;
199 	int ret;
200 
201 	ret = readdir64_r(dirp, (struct dirent64 *)(uintptr_t)buf, &dp64);
202 	if (ret != 0 || dp64 == NULL) {
203 		*result = NULL;
204 		return (ret);
205 	}
206 
207 	if ((dp64->d_ino > SIZE_MAX) || ((uint64_t)dp64->d_off >
208 		(uint64_t)UINT32_MAX)) {
209 		*result = NULL;
210 		return (EOVERFLOW);
211 	}
212 
213 	entry->d_ino = (ino_t)dp64->d_ino;
214 	entry->d_off = (off_t)dp64->d_off;
215 	entry->d_reclen = (unsigned short)((((char *)entry->d_name -
216 		(char *)entry) + strlen(dp64->d_name) + 1 + 3) & ~3);
217 	(void) strcpy(entry->d_name, dp64->d_name);
218 	*result = entry;
219 	return (0);
220 }
221 
222 #endif	/* _LP64 */
223