xref: /haiku/src/bin/reindex.cpp (revision ae0a10cad3999b13cbfa47a3d947a5219d2d90f4)
1 /*
2  * Copyright 2001-2009, Axel Dörfler, axeld@pinc-software.de.
3  * This file may be used under the terms of the MIT License.
4  */
5 
6 
7 #include <ctype.h>
8 #include <errno.h>
9 #include <new>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 
14 #include <Directory.h>
15 #include <Entry.h>
16 #include <File.h>
17 #include <List.h>
18 #include <Node.h>
19 #include <Path.h>
20 #include <String.h>
21 #include <fs_attr.h>
22 #include <fs_index.h>
23 #include <fs_info.h>
24 
25 
26 extern const char *__progname;
27 static const char *kProgramName = __progname;
28 
29 bool gRecursive = false;		// enter directories recursively
30 bool gVerbose = false;
31 char *gAttrPattern;
32 bool gIsPattern = false;
33 bool gFromVolume = false;	// copy indices from another volume
34 BList gAttrList;				// list of indices of that volume
35 
36 
37 class Attribute {
38 public:
39 	Attribute(const char *name);
40 	~Attribute();
41 
42 	status_t ReadFromFile(BNode *node);
43 	status_t WriteToFile(BNode *node);
44 	status_t RemoveFromFile(BNode *node);
45 
46 	const char	*Name() const { return fName.String(); }
47 	type_code	Type() const { return fType; }
48 	size_t		Length() const { return fLength; }
49 
50 protected:
51 	BString		fName;
52 	type_code	fType;
53 	void		*fBuffer;
54 	size_t		fLength;
55 };
56 
57 
58 Attribute::Attribute(const char *name)
59 	:
60 	fName(name),
61 	fBuffer(NULL),
62 	fLength(0)
63 {
64 }
65 
66 
67 Attribute::~Attribute()
68 {
69 	free(fBuffer);
70 }
71 
72 
73 status_t
74 Attribute::ReadFromFile(BNode *node)
75 {
76 	attr_info info;
77 	status_t status = node->GetAttrInfo(fName.String(), &info);
78 	if (status != B_OK)
79 		return status;
80 
81 	fType = info.type;
82 	fLength = info.size;
83 
84 	if ((fBuffer = malloc(fLength)) == NULL)
85 		return B_NO_MEMORY;
86 
87 	ssize_t bytesRead = node->ReadAttr(fName.String(), fType, 0, fBuffer,
88 		fLength);
89 	if (bytesRead < B_OK)
90 		return bytesRead;
91 	if (bytesRead < (ssize_t)fLength)
92 		return B_IO_ERROR;
93 
94 	return B_OK;
95 }
96 
97 
98 status_t
99 Attribute::WriteToFile(BNode *node)
100 {
101 	ssize_t bytesWritten = node->WriteAttr(fName.String(), fType, 0, fBuffer,
102 		fLength);
103 	if (bytesWritten < B_OK)
104 		return bytesWritten;
105 	if (bytesWritten < (ssize_t)fLength)
106 		return B_IO_ERROR;
107 
108 	return B_OK;
109 }
110 
111 
112 status_t
113 Attribute::RemoveFromFile(BNode *node)
114 {
115 	return node->RemoveAttr(fName.String());
116 }
117 
118 
119 //	#pragma mark -
120 
121 
122 bool
123 nameMatchesPattern(char *name)
124 {
125 	if (!gIsPattern)
126 		return !strcmp(name,gAttrPattern);
127 
128 	// test the beginning
129 	int i = 0;
130 	for(; name[i]; i++) {
131 		if (gAttrPattern[i] == '*')
132 			break;
133 		if (name[i] != gAttrPattern[i])
134 			return false;
135 	}
136 
137 	// test the end
138 	int j = strlen(name) - 1;
139 	int k = strlen(gAttrPattern) - 1;
140 	for(; j >= i && k >= i; j--, k--) {
141 		if (gAttrPattern[k] == '*')
142 			return true;
143 		if (name[j] != gAttrPattern[k])
144 			return false;
145 	}
146 	if (gAttrPattern[k] != '*')
147 		return false;
148 
149 	return true;
150 }
151 
152 
153 bool
154 isAttrInList(char *name)
155 {
156 	for (int32 index = gAttrList.CountItems();index-- > 0;) {
157 		const char *attr = (const char *)gAttrList.ItemAt(index);
158 		if (!strcmp(attr, name))
159 			return true;
160 	}
161 	return false;
162 }
163 
164 
165 void
166 handleFile(BEntry *entry, BNode *node)
167 {
168 	// Recurse the directories
169 	if (gRecursive && entry->IsDirectory()) {
170 		BDirectory dir(entry);
171 		BEntry entryIterator;
172 
173 		dir.Rewind();
174 		while (dir.GetNextEntry(&entryIterator, false) == B_OK) {
175 			BNode innerNode;
176 			handleFile(&entryIterator, &innerNode);
177 		}
178 
179 		// also rewrite the attributes of the directory
180 	}
181 
182 	char name[B_FILE_NAME_LENGTH];
183 	entry->GetName(name);
184 
185 	status_t status = node->SetTo(entry);
186 	if (status != B_OK) {
187 		fprintf(stderr, "%s: could not open \"%s\": %s\n", kProgramName, name,
188 			strerror(status));
189 		return;
190 	}
191 
192 	// rewrite file attributes
193 
194 	char attrName[B_ATTR_NAME_LENGTH];
195 	Attribute *attr;
196 	BList list;
197 
198 	// building list
199 	node->RewindAttrs();
200 	while (node->GetNextAttrName(attrName) == B_OK) {
201 		if (gFromVolume) {
202 			if (!isAttrInList(attrName))
203 				continue;
204 		} else if (!nameMatchesPattern(attrName))
205 			continue;
206 
207 		attr = new(std::nothrow) Attribute(attrName);
208 		if (attr == NULL) {
209 			fprintf(stderr, "%s: out of memory.\n", kProgramName);
210 			exit(1);
211 		}
212 
213 		status = attr->ReadFromFile(node);
214 		if (status != B_OK) {
215 			fprintf(stderr, "%s: could not read attribute \"%s\" of file "
216 				"\"%s\": %s\n", kProgramName, attrName, name, strerror(status));
217 			delete attr;
218 			continue;
219 		}
220 		if (gVerbose) {
221 			printf("%s: read attribute '%s' (%ld bytes of data)\n", name,
222 				attrName, attr->Length());
223 		}
224 		if (!list.AddItem(attr)) {
225 			fprintf(stderr, "%s: out of memory.\n", kProgramName);
226 			exit(1);
227 		}
228 
229 		if (!gFromVolume) {
230 			// creates index to that attribute if necessary
231 			entry_ref ref;
232 			if (entry->GetRef(&ref) == B_OK) {
233 				index_info indexInfo;
234 				if (fs_stat_index(ref.device, attrName, &indexInfo) != B_OK)
235 					fs_create_index(ref.device, attrName, attr->Type(), 0);
236 			}
237 		}
238 	}
239 
240 	// remove attrs
241 	for (int32 i = list.CountItems(); i-- > 0;) {
242 		attr = static_cast<Attribute *>(list.ItemAt(i));
243 		if (attr->RemoveFromFile(node) != B_OK) {
244 			fprintf(stderr, "%s: could not remove attribute '%s' from file "
245 				"'%s'.\n", kProgramName, attr->Name(), name);
246 		}
247 	}
248 
249 	// rewrite attrs and empty the list
250 	while ((attr = static_cast<Attribute *>(list.RemoveItem((int32)0))) != NULL) {
251 		// write attribute back to file
252 		status = attr->WriteToFile(node);
253 		if (status != B_OK) {
254 			fprintf(stderr, "%s: could not write attribute '%s' to file \"%s\":"
255 				" %s\n", kProgramName, attr->Name(), name, strerror(status));
256 		} else if (gVerbose) {
257 			printf("%s: wrote attribute '%s' (%ld bytes of data)\n", name,
258 				attr->Name(), attr->Length());
259 		}
260 
261 		delete attr;
262 	}
263 }
264 
265 
266 void
267 copyIndicesFromVolume(const char *path, BEntry &to)
268 {
269 	entry_ref ref;
270 	if (to.GetRef(&ref) != B_OK) {
271 		fprintf(stderr, "%s: Could not open target volume.\n", kProgramName);
272 		return;
273 	}
274 
275 	dev_t targetDevice = ref.device;
276 	dev_t sourceDevice = dev_for_path(path);
277 	if (sourceDevice < B_OK) {
278 		fprintf(stderr, "%s: Could not open source volume: %s\n", kProgramName,
279 			strerror(sourceDevice));
280 		return;
281 	}
282 
283 	DIR *indexDirectory = fs_open_index_dir(sourceDevice);
284 	if (indexDirectory == NULL)
285 		return;
286 
287 	while (dirent *index = fs_read_index_dir(indexDirectory)) {
288 		index_info indexInfo;
289 		if (fs_stat_index(sourceDevice, index->d_name, &indexInfo) != B_OK) {
290 			fprintf(stderr, "%s: Could not get information about index "
291 				"\"%s\": %s\n", kProgramName, index->d_name, strerror(errno));
292 			continue;
293 		}
294 
295 		if (fs_create_index(targetDevice, index->d_name, indexInfo.type, 0)
296 				== -1 && errno != B_FILE_EXISTS && errno != B_BAD_VALUE) {
297 			fprintf(stderr, "Could not create index '%s' (type = %" B_PRIu32
298 				"): %s\n", index->d_name, indexInfo.type, strerror(errno));
299 		} else
300 			gAttrList.AddItem(strdup(index->d_name));
301 	}
302 	fs_close_index_dir(indexDirectory);
303 }
304 
305 
306 void
307 printUsage(char *cmd)
308 {
309 	printf("usage: %s [-rvf] attr <list of filenames and/or directories>\n"
310 		"  -r\tenter directories recursively\n"
311 		"  -v\tverbose output\n"
312 		"  -f\tcreate/update all indices from the source volume,\n\t\"attr\" is "
313 			"the path to the source volume\n", cmd);
314 }
315 
316 
317 int
318 main(int argc, char **argv)
319 {
320 	char *cmd = argv[0];
321 
322 	if (argc < 3) {
323 		printUsage(cmd);
324 		return 1;
325 	}
326 
327 	while (*++argv && **argv == '-') {
328 		for (int i = 1; (*argv)[i]; i++) {
329 			switch ((*argv)[i]) {
330 				case 'f':
331 					gFromVolume = true;
332 					break;
333 				case 'r':
334 					gRecursive = true;
335 					break;
336 				case 'v':
337 					gVerbose = true;
338 					break;
339 				default:
340 					printUsage(cmd);
341 					return(1);
342 			}
343 		}
344 	}
345 	gAttrPattern = *argv;
346 	if (strchr(gAttrPattern,'*'))
347 		gIsPattern = true;
348 
349 	while (*++argv) {
350 		BEntry entry(*argv);
351 		BNode node;
352 
353 		if (entry.InitCheck() == B_OK) {
354 			if (gFromVolume)
355 				copyIndicesFromVolume(gAttrPattern, entry);
356 			handleFile(&entry, &node);
357 		} else
358 			fprintf(stderr, "%s: could not find \"%s\".\n", kProgramName, *argv);
359 	}
360 
361 	return 0;
362 }
363