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
26extern const char *__progname;
27static const char *kProgramName = __progname;
28
29bool gRecursive = false;		// enter directories recursively
30bool gVerbose = false;
31char *gAttrPattern;
32bool gIsPattern = false;
33bool gFromVolume = false;	// copy indices from another volume
34BList gAttrList;				// list of indices of that volume
35
36
37class Attribute {
38public:
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
50protected:
51	BString		fName;
52	type_code	fType;
53	void		*fBuffer;
54	size_t		fLength;
55};
56
57
58Attribute::Attribute(const char *name)
59	:
60	fName(name),
61	fBuffer(NULL),
62	fLength(0)
63{
64}
65
66
67Attribute::~Attribute()
68{
69	free(fBuffer);
70}
71
72
73status_t
74Attribute::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
98status_t
99Attribute::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
112status_t
113Attribute::RemoveFromFile(BNode *node)
114{
115	return node->RemoveAttr(fName.String());
116}
117
118
119//	#pragma mark -
120
121
122bool
123nameMatchesPattern(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
153bool
154isAttrInList(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
165void
166handleFile(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
266void
267copyIndicesFromVolume(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
306void
307printUsage(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
317int
318main(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