/ tests / dir.c
dir.c
  1  #include <dirent.h>
  2  #include <fcntl.h>
  3  #include <stdio.h>
  4  #include <unistd.h>
  5  
  6  #include <darwintest.h>
  7  #include <darwintest_utils.h>
  8  
  9  T_DECL(seekdir_basic, "seekdir")
 10  {
 11  	const char *path = dt_tmpdir();
 12  	// make sure there are a couple of entries in the dir aside from . and ..
 13  	int fd = open(path, O_RDONLY | O_DIRECTORY);
 14  	openat(fd, "a", O_CREAT | O_WRONLY, 0600);
 15  	openat(fd, "b", O_CREAT | O_WRONLY, 0600);
 16  	openat(fd, "c", O_CREAT | O_WRONLY, 0600);
 17  
 18  	DIR *dirp = fdopendir(fd);
 19  	struct dirent *entry = NULL;
 20  
 21  	T_ASSERT_NOTNULL(dirp, NULL);
 22  
 23  	T_ASSERT_NOTNULL(entry = readdir(dirp), NULL); // .
 24  	T_ASSERT_NOTNULL(entry = readdir(dirp), NULL); // ..
 25  
 26  	// we can get any entry -- no ordering
 27  	T_ASSERT_NOTNULL(entry = readdir(dirp), NULL);
 28  	// remember position for the second entry
 29  	long second_pos = telldir(dirp);
 30  	// read the second entry
 31  	T_ASSERT_NOTNULL(entry = readdir(dirp), NULL);
 32  	char *second_name = strdup(entry->d_name);
 33  	T_ASSERT_NOTNULL(second_name, NULL);
 34  	// read the third entry
 35  	T_ASSERT_NOTNULL(entry = readdir(dirp), NULL);
 36  
 37  	// go back to the second entry and read it
 38  	seekdir(dirp, second_pos);
 39  	T_ASSERT_NOTNULL(entry = readdir(dirp), NULL);
 40  
 41  	// make sure the name matches the old copy
 42  	T_ASSERT_EQ_STR(second_name, entry->d_name, NULL);
 43  
 44  	// return to 2nd once again, reinitializing second_pos and re-reading
 45  	seekdir(dirp, second_pos);
 46  	second_pos = telldir(dirp);
 47  	T_ASSERT_NOTNULL(entry = readdir(dirp), NULL);
 48  
 49  	// make sure the name matches the old copy
 50  	T_ASSERT_EQ_STR(second_name, entry->d_name, NULL);
 51  
 52  	// verify that last pos
 53  	seekdir(dirp, second_pos);
 54  	T_ASSERT_NOTNULL(entry = readdir(dirp), NULL);
 55  	T_ASSERT_EQ_STR(second_name, entry->d_name, NULL);
 56  
 57  	free(second_name);
 58  	T_ASSERT_POSIX_ZERO(closedir(dirp), NULL);
 59  }
 60  
 61  T_DECL(readdir, "readdir")
 62  {
 63  	const char *path = dt_tmpdir();
 64  	int fd = open(path, O_RDONLY | O_DIRECTORY);
 65  	openat(fd, "foobarbaz", O_CREAT | O_WRONLY, 0600);
 66  
 67  	DIR *dirp = fdopendir(fd);
 68  	T_ASSERT_NOTNULL(dirp, NULL);
 69  
 70  	struct dirent *entry = NULL;
 71  	while ((entry = readdir(dirp)) != NULL) {
 72  		if (strcmp(entry->d_name, "foobarbaz")) {
 73  			break;
 74  		}
 75  	}
 76  
 77  	T_ASSERT_NOTNULL(entry, "found the entry");
 78  
 79  	T_ASSERT_POSIX_ZERO(closedir(dirp), NULL);
 80  }
 81  
 82  T_DECL(tell_seek_tell, "tell-seek-tell returns the same location")
 83  {
 84  	// http://pubs.opengroup.org/onlinepubs/009695399/functions/telldir.html
 85  	// If the most recent operation on the directory stream was a seekdir(),
 86  	// the directory position returned from the telldir() shall be the same as
 87  	// that supplied as a loc argument for seekdir().
 88  
 89  	const char *path = dt_tmpdir();
 90  	// make sure there are a couple of entries in the dir aside from . and ..
 91  	{
 92  		int fd = open(path, O_RDONLY | O_DIRECTORY);
 93  		openat(fd, "a", O_CREAT | O_WRONLY, 0600);
 94  		openat(fd, "b", O_CREAT | O_WRONLY, 0600);
 95  		openat(fd, "c", O_CREAT | O_WRONLY, 0600);
 96  		close(fd);
 97  	}
 98  
 99  	DIR *dirp = opendir(path);
100  	T_ASSERT_NOTNULL(dirp, NULL);
101  	struct dirent *entry = NULL;
102  
103  	T_ASSERT_NOTNULL(entry = readdir(dirp), NULL);
104  	T_ASSERT_NOTNULL(entry = readdir(dirp), NULL);
105  	long pos1 = telldir(dirp);
106  	T_ASSERT_NOTNULL(entry = readdir(dirp), NULL);
107  	T_ASSERT_NOTNULL(entry = readdir(dirp), NULL);
108  	seekdir(dirp, pos1);
109  	long pos2 = telldir(dirp);
110  
111  	T_ASSERT_EQ(pos1, pos2, NULL);
112  
113  	T_ASSERT_POSIX_ZERO(closedir(dirp), NULL);
114  }
115  
116  T_DECL(rewinddir, "rewinddir")
117  {
118  	const char *path = dt_tmpdir();
119  	// make sure there are a couple of entries in the dir aside from . and ..
120  	{
121  		int fd = open(path, O_RDONLY | O_DIRECTORY);
122  		openat(fd, "a", O_CREAT | O_WRONLY, 0600);
123  		openat(fd, "b", O_CREAT | O_WRONLY, 0600);
124  		openat(fd, "c", O_CREAT | O_WRONLY, 0600);
125  		close(fd);
126  	}
127  
128  	DIR *dirp = opendir(path);
129  	T_ASSERT_NOTNULL(dirp, NULL);
130  	struct dirent *entry = NULL;
131  
132  	T_ASSERT_NOTNULL(entry = readdir(dirp), NULL);
133  	T_ASSERT_NOTNULL(entry = readdir(dirp), NULL);
134  	T_ASSERT_NOTNULL(entry = readdir(dirp), NULL);
135  	char *third_name = strdup(entry->d_name);
136  
137  	rewinddir(dirp);
138  
139  	T_ASSERT_NOTNULL(entry = readdir(dirp), NULL);
140  	T_ASSERT_NOTNULL(entry = readdir(dirp), NULL);
141  	T_ASSERT_NOTNULL(entry = readdir(dirp), NULL);
142  
143  	T_ASSERT_EQ_STR(third_name, entry->d_name, NULL);
144  
145  	free(third_name);
146  	T_ASSERT_POSIX_ZERO(closedir(dirp), NULL);
147  }
148  
149  
150  T_DECL(rewinddir_dup, "rewinddir dup")
151  {
152  	// An older implementation of rewinddir failed to seek the fd which was
153  	// passed to fdopendir()
154  
155  	const char *path = dt_tmpdir();
156  	// make sure there are a couple of entries in the dir aside from . and ..
157  	int fd = open(path, O_RDONLY | O_DIRECTORY);
158  	openat(fd, "a", O_CREAT | O_WRONLY, 0600);
159  	openat(fd, "b", O_CREAT | O_WRONLY, 0600);
160  	openat(fd, "c", O_CREAT | O_WRONLY, 0600);
161  
162  	// prep an fd with a non-zero seek
163  	DIR *dirp = fdopendir(fd);
164  	T_ASSERT_NOTNULL(dirp, NULL);
165  	struct dirent *entry = NULL;
166  
167  	T_ASSERT_NOTNULL(entry = readdir(dirp), NULL);
168  	T_ASSERT_NOTNULL(entry = readdir(dirp), NULL);
169  	T_ASSERT_NOTNULL(entry = readdir(dirp), NULL);
170  
171  	// remember the entry name and dup the fd
172  	char *third_name = strdup(entry->d_name);
173  	int fd2 = dup(fd);
174  
175  	T_ASSERT_POSIX_ZERO(closedir(dirp), NULL);
176  
177  	dirp = fdopendir(fd2);
178  	// rewind back to 0
179  	rewinddir(dirp);
180  
181  	T_ASSERT_NOTNULL(dirp, NULL);
182  	T_ASSERT_NOTNULL(entry = readdir(dirp), NULL);
183  	T_ASSERT_NOTNULL(entry = readdir(dirp), NULL);
184  	T_ASSERT_NOTNULL(entry = readdir(dirp), NULL);
185  
186  	T_ASSERT_EQ_STR(third_name, entry->d_name, NULL);
187  
188  	free(third_name);
189  	T_ASSERT_POSIX_ZERO(closedir(dirp), NULL);
190  }
191  
192  static int
193  _select_abc(const struct dirent *entry)
194  {
195  	return strcmp(entry->d_name, "a") == 0 ||
196  			strcmp(entry->d_name, "b") == 0 ||
197  			strcmp(entry->d_name, "c") == 0;
198  }
199  
200  T_DECL(scandir, "scandir")
201  {
202  	const char *path = dt_tmpdir();
203  	{
204  		int fd = open(path, O_RDONLY | O_DIRECTORY);
205  		openat(fd, "a", O_CREAT | O_WRONLY, 0600);
206  		openat(fd, "b", O_CREAT | O_WRONLY, 0600);
207  		openat(fd, "c", O_CREAT | O_WRONLY, 0600);
208  		close(fd);
209  	}
210  
211  	struct dirent **entries = NULL;
212  	int found = scandir(path, &entries, _select_abc, alphasort);
213  
214  	T_ASSERT_EQ(found, 3, NULL);
215  
216  	T_ASSERT_EQ_STR(entries[0]->d_name, "a", NULL);
217  	T_ASSERT_EQ_STR(entries[1]->d_name, "b", NULL);
218  	T_ASSERT_EQ_STR(entries[2]->d_name, "c", NULL);
219  
220  	free(entries[0]);
221  	free(entries[1]);
222  	free(entries[2]);
223  	free(entries);
224  }
225  
226  T_DECL(scandir_b, "scandir_b")
227  {
228  	const char *path = dt_tmpdir();
229  	{
230  		int fd = open(path, O_RDONLY | O_DIRECTORY);
231  		openat(fd, "a", O_CREAT | O_WRONLY, 0600);
232  		openat(fd, "b", O_CREAT | O_WRONLY, 0600);
233  		openat(fd, "c", O_CREAT | O_WRONLY, 0600);
234  		close(fd);
235  	}
236  
237  	const struct dirent **entries = NULL;
238  	int found = scandir_b(path, &entries,
239  			^(const struct dirent *entry) {
240  				return strcmp(entry->d_name, "a") == 0 ||
241  						strcmp(entry->d_name, "b") == 0 ||
242  						strcmp(entry->d_name, "c") == 0;
243  			},
244  			^(const struct dirent **d1, const struct dirent **d2) {
245  				return strcoll((*d1)->d_name, (*d2)->d_name);
246  			});
247  
248  	T_ASSERT_EQ(found, 3, NULL);
249  
250  	T_ASSERT_EQ_STR(entries[0]->d_name, "a", NULL);
251  	T_ASSERT_EQ_STR(entries[1]->d_name, "b", NULL);
252  	T_ASSERT_EQ_STR(entries[2]->d_name, "c", NULL);
253  
254  	free(entries[0]);
255  	free(entries[1]);
256  	free(entries[2]);
257  	free(entries);
258  }