xref: /freebsd/lib/libc/gen/telldir.c (revision a96ef4501919d7ac08e94e98dc34b0bdd744802b)
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 1983, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 __SCCSID("@(#)telldir.c	8.1 (Berkeley) 6/4/93");
34 __FBSDID("$FreeBSD$");
35 
36 #include "namespace.h"
37 #include <sys/param.h>
38 #include <sys/queue.h>
39 #include <dirent.h>
40 #include <pthread.h>
41 #include <stdlib.h>
42 #include <unistd.h>
43 #include "un-namespace.h"
44 
45 #include "libc_private.h"
46 #include "gen-private.h"
47 #include "telldir.h"
48 
49 /*
50  * return a pointer into a directory
51  */
52 long
53 telldir(DIR *dirp)
54 {
55 	struct ddloc_mem *lp, *flp;
56 	union ddloc_packed ddloc;
57 
58 	if (__isthreaded)
59 		_pthread_mutex_lock(&dirp->dd_lock);
60 	/*
61 	 * Outline:
62 	 * 1) If the directory position fits in a packed structure, return that.
63 	 * 2) Otherwise, see if it's already been recorded in the linked list
64 	 * 3) Otherwise, malloc a new one
65 	 */
66 	if (dirp->dd_seek < (off_t)(1l << DD_SEEK_BITS) &&
67 	    dirp->dd_loc < (1l << DD_LOC_BITS)) {
68 		ddloc.s.is_packed = 1;
69 		ddloc.s.loc = dirp->dd_loc;
70 		ddloc.s.seek = dirp->dd_seek;
71 		goto out;
72 	}
73 
74 	flp = NULL;
75 	LIST_FOREACH(lp, &dirp->dd_td->td_locq, loc_lqe) {
76 		if (lp->loc_seek == dirp->dd_seek) {
77 			if (flp == NULL)
78 				flp = lp;
79 			if (lp->loc_loc == dirp->dd_loc)
80 				break;
81 		} else if (flp != NULL) {
82 			lp = NULL;
83 			break;
84 		}
85 	}
86 	if (lp == NULL) {
87 		lp = malloc(sizeof(struct ddloc_mem));
88 		if (lp == NULL) {
89 			if (__isthreaded)
90 				_pthread_mutex_unlock(&dirp->dd_lock);
91 			return (-1);
92 		}
93 		lp->loc_index = dirp->dd_td->td_loccnt++;
94 		lp->loc_seek = dirp->dd_seek;
95 		lp->loc_loc = dirp->dd_loc;
96 		if (flp != NULL)
97 			LIST_INSERT_BEFORE(flp, lp, loc_lqe);
98 		else
99 			LIST_INSERT_HEAD(&dirp->dd_td->td_locq, lp, loc_lqe);
100 	}
101 	ddloc.i.is_packed = 0;
102 	/*
103 	 * Technically this assignment could overflow on 32-bit architectures,
104 	 * but we would get ENOMEM long before that happens.
105 	 */
106 	ddloc.i.index = lp->loc_index;
107 
108 out:
109 	if (__isthreaded)
110 		_pthread_mutex_unlock(&dirp->dd_lock);
111 	return (ddloc.l);
112 }
113 
114 /*
115  * seek to an entry in a directory.
116  * Only values returned by "telldir" should be passed to seekdir.
117  */
118 void
119 _seekdir(DIR *dirp, long loc)
120 {
121 	struct ddloc_mem *lp;
122 	struct dirent *dp;
123 	union ddloc_packed ddloc;
124 	off_t loc_seek;
125 	long loc_loc;
126 
127 	ddloc.l = loc;
128 
129 	if (ddloc.s.is_packed) {
130 		loc_seek = ddloc.s.seek;
131 		loc_loc = ddloc.s.loc;
132 	} else {
133 		LIST_FOREACH(lp, &dirp->dd_td->td_locq, loc_lqe) {
134 			if (lp->loc_index == ddloc.i.index)
135 				break;
136 		}
137 		if (lp == NULL)
138 			return;
139 
140 		loc_seek = lp->loc_seek;
141 		loc_loc = lp->loc_loc;
142 	}
143 	if (loc_loc == dirp->dd_loc && loc_seek == dirp->dd_seek)
144 		return;
145 
146 	/* If it's within the same chunk of data, don't bother reloading. */
147 	if (loc_seek == dirp->dd_seek) {
148 		/*
149 		 * If we go back to 0 don't make the next readdir
150 		 * trigger a call to getdirentries().
151 		 */
152 		if (loc_loc == 0)
153 			dirp->dd_flags |= __DTF_SKIPREAD;
154 		dirp->dd_loc = loc_loc;
155 		return;
156 	}
157 	(void) lseek(dirp->dd_fd, (off_t)loc_seek, SEEK_SET);
158 	dirp->dd_seek = loc_seek;
159 	dirp->dd_loc = 0;
160 	dirp->dd_flags &= ~__DTF_SKIPREAD; /* current contents are invalid */
161 	while (dirp->dd_loc < loc_loc) {
162 		dp = _readdir_unlocked(dirp, 0);
163 		if (dp == NULL)
164 			break;
165 	}
166 }
167 
168 /*
169  * After readdir returns the last entry in a block, a call to telldir
170  * returns a location that is after the end of that last entry.
171  * However, that location doesn't refer to a valid directory entry.
172  * Ideally, the call to telldir would return a location that refers to
173  * the first entry in the next block.  That location is not known
174  * until the next block is read, so readdir calls this function after
175  * fetching a new block to fix any such telldir locations.
176  */
177 void
178 _fixtelldir(DIR *dirp, long oldseek, long oldloc)
179 {
180 	struct ddloc_mem *lp;
181 
182 	lp = LIST_FIRST(&dirp->dd_td->td_locq);
183 	if (lp != NULL) {
184 		if (lp->loc_loc == oldloc &&
185 		    lp->loc_seek == oldseek) {
186 			lp->loc_seek = dirp->dd_seek;
187 			lp->loc_loc = dirp->dd_loc;
188 		}
189 	}
190 }
191 
192 /*
193  * Reclaim memory for telldir cookies which weren't used.
194  */
195 void
196 _reclaim_telldir(DIR *dirp)
197 {
198 	struct ddloc_mem *lp;
199 	struct ddloc_mem *templp;
200 
201 	lp = LIST_FIRST(&dirp->dd_td->td_locq);
202 	while (lp != NULL) {
203 		templp = lp;
204 		lp = LIST_NEXT(lp, loc_lqe);
205 		free(templp);
206 	}
207 	LIST_INIT(&dirp->dd_td->td_locq);
208 }
209