xref: /haiku/src/system/boot/loader/file_systems/fat/Directory.cpp (revision 4f2fd49bdc6078128b1391191e4edac647044c3d)
1 /*
2 ** Copyright 2003, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
3 ** Distributed under the terms of the OpenBeOS License.
4 */
5 
6 
7 #include "Directory.h"
8 #include "Volume.h"
9 #include "File.h"
10 
11 #include <StorageDefs.h>
12 #include <util/kernel_cpp.h>
13 
14 #include <string.h>
15 #include <unistd.h>
16 #include <stdint.h>
17 #include <stdio.h>
18 
19 //#define TRACE(x) dprintf x
20 #define TRACE(x) do {} while (0)
21 
22 namespace FATFS {
23 
24 struct dir_entry {
25 	void *Buffer() const { return (void *)fName; };
26 	const char *BaseName() const { return fName; };
27 	const char *Extension() const { return fExt; };
28 	uint8		Flags() const { return fFlags; };
29 	uint32		Cluster(int32 fatBits) const;
30 	uint32		Size() const { return B_LENDIAN_TO_HOST_INT32(fSize); };
31 	bool		IsFile() const;
32 	bool		IsDir() const;
33 	char fName[8];
34 	char fExt[3];
35 	uint8 fFlags;
36 	uint8 fReserved1;
37 	uint8 fCreateTime10ms;
38 	uint16 fCreateTime;
39 	uint16 fCreateDate;
40 	uint16 fAccessDate;
41 	uint16 fClusterMSB;
42 	uint16 fModifiedTime;
43 	uint16 fModifiedDate;
44 	uint16 fClusterLSB;
45 	uint32 fSize;
46 } _PACKED;
47 
48 
49 uint32
50 dir_entry::Cluster(int32 fatBits) const
51 {
52 	uint32 c = B_LENDIAN_TO_HOST_INT16(fClusterLSB);
53 	if (fatBits == 32)
54 		c += ((uint32)B_LENDIAN_TO_HOST_INT16(fClusterMSB) << 16);
55 	return c;
56 }
57 
58 bool
59 dir_entry::IsFile() const
60 {
61 	return ((Flags() & (FAT_VOLUME|FAT_SUBDIR)) == 0);
62 }
63 
64 
65 bool
66 dir_entry::IsDir() const
67 {
68 	return ((Flags() & (FAT_VOLUME|FAT_SUBDIR)) == FAT_SUBDIR);
69 }
70 
71 
72 struct dir_cookie {
73 	int32	index;
74 	struct dir_entry entry;
75 	off_t	Offset() const { return index * sizeof(struct dir_entry); }
76 };
77 
78 
79 Directory::Directory(Volume &volume, uint32 cluster, const char *name)
80 	:
81 	fVolume(volume),
82 	fStream(volume, cluster, UINT32_MAX, name)
83 {
84 	TRACE(("FASFS::Directory::(, %lu, %s)\n", cluster, name));
85 }
86 
87 
88 Directory::~Directory()
89 {
90 	TRACE(("FASFS::Directory::~()\n"));
91 }
92 
93 
94 status_t
95 Directory::InitCheck()
96 {
97 	status_t err;
98 	err = fStream.InitCheck();
99 	if (err < B_OK)
100 		return err;
101 	return B_OK;
102 }
103 
104 
105 status_t
106 Directory::Open(void **_cookie, int mode)
107 {
108 	TRACE(("FASFS::Directory::%s(, %d)\n", __FUNCTION__, mode));
109 	_inherited::Open(_cookie, mode);
110 
111 	struct dir_cookie *c = new struct dir_cookie;
112 	if (c == NULL)
113 		return B_NO_MEMORY;
114 
115 	c->index = -1;
116 
117 	*_cookie = (void *)c;
118 	return B_OK;
119 }
120 
121 
122 status_t
123 Directory::Close(void *cookie)
124 {
125 	TRACE(("FASFS::Directory::%s()\n", __FUNCTION__));
126 	_inherited::Close(cookie);
127 
128 	delete (struct dir_cookie *)cookie;
129 	return B_OK;
130 }
131 
132 
133 Node *
134 Directory::Lookup(const char *name, bool traverseLinks)
135 {
136 	TRACE(("FASFS::Directory::%s('%s', %d)\n", __FUNCTION__, name, traverseLinks));
137 	if (!strcmp(name, ".")) {
138 		Acquire();
139 		return this;
140 	}
141 	char *dot = strchr(name, '.');
142 	int baselen = strlen(name);
143 	if (baselen > FATFS_BASENAME_LENGTH) // !?
144 		return NULL;
145 	char *ext = NULL;
146 	int extlen = 0;
147 	if (dot) {
148 		baselen = dot - name;
149 		ext = dot + 1;
150 		if (strlen(ext) > FATFS_EXTNAME_LENGTH) // !?
151 			return NULL;
152 		extlen = strlen(ext);
153 	}
154 
155 	status_t err;
156 	struct dir_cookie cookie;
157 	struct dir_cookie *c = &cookie;
158 	c->index = -1;
159 
160 	do {
161 		err = GetNextEntry(c);
162 		if (err < B_OK)
163 			return NULL;
164 		TRACE(("FASFS::Directory::%s: %s <> '%8.8s.%3.3s'\n", __FUNCTION__,
165 			name, c->entry.BaseName(), c->entry.Extension()));
166 		int i;
167 		for (i = 0; i < FATFS_BASENAME_LENGTH; i++)
168 			if (c->entry.BaseName()[i] == ' ')
169 				break;
170 		int nlen = MIN(i,FATFS_BASENAME_LENGTH);
171 		for (i = 0; i < FATFS_EXTNAME_LENGTH; i++)
172 			if (c->entry.Extension()[i] == ' ')
173 				break;
174 		int elen = MIN(i,FATFS_EXTNAME_LENGTH);
175 		if (nlen != baselen)
176 			continue;
177 		if (elen != extlen)
178 			continue;
179 		if (strncasecmp(name, c->entry.BaseName(), nlen))
180 			continue;
181 		if (strncasecmp(ext, c->entry.Extension(), elen))
182 			continue;
183 		TRACE(("GOT IT!\n"));
184 		break;
185 	} while (true);
186 
187 	if (c->entry.IsFile()) {
188 		TRACE(("IS FILE\n"));
189 		return new File(fVolume, c->entry.Cluster(fVolume.FatBits()),
190 			c->entry.Size(), name);
191 	}
192 	if (c->entry.IsDir()) {
193 		TRACE(("IS DIR\n"));
194 		return new Directory(fVolume, c->entry.Cluster(fVolume.FatBits()),
195 			name);
196 	}
197 	return NULL;
198 }
199 
200 
201 status_t
202 Directory::GetNextEntry(void *cookie, char *name, size_t size)
203 {
204 	TRACE(("FASFS::Directory::%s()\n", __FUNCTION__));
205 	struct dir_cookie *c = (struct dir_cookie *)cookie;
206 	status_t err;
207 
208 	err = GetNextEntry(cookie);
209 	if (err < B_OK)
210 		return err;
211 
212 	strlcpy(name, c->entry.fName, MIN(size, FATFS_BASENAME_LENGTH));
213 	strlcpy(name, ".", size);
214 	strlcpy(name, c->entry.fExt, MIN(size, FATFS_EXTNAME_LENGTH));
215 	return B_OK;
216 }
217 
218 
219 status_t
220 Directory::GetNextNode(void *cookie, Node **_node)
221 {
222 	return B_ERROR;
223 }
224 
225 
226 status_t
227 Directory::Rewind(void *cookie)
228 {
229 	TRACE(("FASFS::Directory::%s()\n", __FUNCTION__));
230 	struct dir_cookie *c = (struct dir_cookie *)cookie;
231 	c->index = -1;
232 
233 	return B_OK;
234 }
235 
236 
237 bool
238 Directory::IsEmpty()
239 {
240 	TRACE(("FASFS::Directory::%s()\n", __FUNCTION__));
241 	struct dir_cookie cookie;
242 	struct dir_cookie *c = &cookie;
243 	c->index = -1;
244 	if (GetNextEntry(c) == B_OK)
245 		return false;
246 	return true;
247 }
248 
249 
250 status_t
251 Directory::GetName(char *name, size_t size) const
252 {
253 	TRACE(("FASFS::Directory::%s()\n", __FUNCTION__));
254 	if (this == fVolume.Root())
255 		return fVolume.GetName(name, size);
256 	return fStream.GetName(name, size);
257 }
258 
259 
260 ino_t
261 Directory::Inode() const
262 {
263 	TRACE(("FASFS::Directory::%s()\n", __FUNCTION__));
264 	return fStream.FirstCluster() << 16;
265 }
266 
267 status_t
268 Directory::GetNextEntry(void *cookie, uint8 mask, uint8 match)
269 {
270 	TRACE(("FASFS::Directory::%s(, %02x, %02x)\n", __FUNCTION__, mask, match));
271 	struct dir_cookie *c = (struct dir_cookie *)cookie;
272 
273 	do {
274 		c->index++;
275 		size_t len = sizeof(c->entry);
276 		if (fStream.ReadAt(c->Offset(), (uint8 *)&c->entry, &len) < B_OK)
277 			return B_ENTRY_NOT_FOUND;
278 		TRACE(("FASFS::Directory::%s: got one entry\n", __FUNCTION__));
279 		if ((uint8)c->entry.fName[0] == 0x00) // last one
280 			return B_ENTRY_NOT_FOUND;
281 		if ((uint8)c->entry.fName[0] == 0xe5) // deleted
282 			continue;
283 		if (c->entry.Flags() == 0x0f) // LFN entry
284 			continue;
285 		if (c->entry.Flags() & (FAT_VOLUME|FAT_SUBDIR) == FAT_VOLUME) {
286 			// TODO handle Volume name (set fVolume's name)
287 			continue;
288 		}
289 		TRACE(("FASFS::Directory::%s: checking '%8.8s.%3.3s', %02x\n", __FUNCTION__,
290 			c->entry.BaseName(), c->entry.Extension(), c->entry.Flags()));
291 		if ((c->entry.Flags() & mask) == match)
292 			break;
293 	} while (true);
294 	TRACE(("FATFS::Directory::%s: '%8.8s.%3.3s'\n", __FUNCTION__,
295 		c->entry.BaseName(), c->entry.Extension()));
296 	return B_OK;
297 }
298 
299 }	// namespace FATFS
300