1 /*	$OpenBSD: seekdir.c,v 1.13 2015/09/12 13:34:22 guenther Exp $ */
2 /*
3  * Copyright (c) 2013 Ingo Schwarze <schwarze@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <dirent.h>
19 #include <unistd.h>
20 
21 #include "thread_private.h"
22 #include "telldir.h"
23 
24 /*
25  * Seek to an entry in a directory.
26  * Only values returned by "telldir" should be passed to seekdir.
27  */
28 void
seekdir(DIR * dirp,long loc)29 seekdir(DIR *dirp, long loc)
30 {
31 	struct dirent *dp;
32 
33 	/*
34 	 * First check whether the directory entry to seek for
35 	 * is still buffered in the directory structure in memory.
36 	 */
37 
38 	_MUTEX_LOCK(&dirp->dd_lock);
39 	if (dirp->dd_size && dirp->dd_bufpos == loc) {
40 		dirp->dd_loc = 0;
41 		dirp->dd_curpos = loc;
42 		_MUTEX_UNLOCK(&dirp->dd_lock);
43 		return;
44 	}
45 
46 	for (dirp->dd_loc = 0;
47 	     dirp->dd_loc < dirp->dd_size;
48 	     dirp->dd_loc += dp->d_reclen) {
49 		dp = (struct dirent *)(dirp->dd_buf + dirp->dd_loc);
50 		if (dp->d_off != loc)
51 			continue;
52 
53 		/*
54 		 * Entry found in the buffer, use it.  If readdir(3)
55 		 * follows, this will save us a getdents(2) syscall.
56 		 * Note that d_off is the offset of the _next_ entry,
57 		 * so advance dd_loc.
58 		 */
59 
60 		dirp->dd_loc += dp->d_reclen;
61 		dirp->dd_curpos = loc;
62 		_MUTEX_UNLOCK(&dirp->dd_lock);
63 		return;
64 	}
65 
66 	/*
67 	 * The entry is not in the buffer, prepare a call to getdents(2).
68 	 * In particular, invalidate dd_loc.
69 	 */
70 
71 	dirp->dd_loc = dirp->dd_size;
72 	dirp->dd_bufpos = dirp->dd_curpos = lseek(dirp->dd_fd, loc, SEEK_SET);
73 	_MUTEX_UNLOCK(&dirp->dd_lock);
74 }
75 DEF_WEAK(seekdir);
76