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