1/*
2 * Copyright 2009, Haiku Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Michael Lotz <mmlr@mlotz.ch>
7 */
8
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12
13#include <fs_attr.h>
14
15#include <Directory.h>
16#include <Entry.h>
17#include <File.h>
18#include <Node.h>
19
20
21#define ATTRIBUTE_FILE_MAGIC	'attr'
22#define ATTRIBUTE_DIR_NAME		"_HAIKU"
23#define COPY_BUFFER_SIZE		128 * 1024
24
25
26struct attribute_file {
27	uint32		magic; // 'attr'
28	uint32		entry_count;
29	uint8		entries[1];
30} _PACKED;
31
32
33struct attribute_entry {
34	type_code	type;
35	uint32		size;
36	uint8		name_length; // including 0 byte
37	char		name[1]; // 0 terminated, followed by data
38} _PACKED;
39
40
41void
42recurse_directory(BDirectory &directory, uint8 *copyBuffer)
43{
44	BNode node;
45	entry_ref ref;
46	BDirectory attributeDir;
47	bool attributeDirCreated = false;
48	char nameBuffer[B_FILE_NAME_LENGTH];
49	directory.Rewind();
50
51	while (directory.GetNextRef(&ref) == B_OK) {
52		if (strcmp(ref.name, ATTRIBUTE_DIR_NAME) == 0)
53			continue;
54
55		if (node.SetTo(&ref) != B_OK) {
56			printf("failed to set node to ref \"%s\"\n", ref.name);
57			continue;
58		}
59
60		node.RewindAttrs();
61		BFile attributeFile;
62		uint32 attributeCount = 0;
63		while (node.GetNextAttrName(nameBuffer) == B_OK) {
64			attr_info info;
65			if (node.GetAttrInfo(nameBuffer, &info) != B_OK) {
66				printf("failed to get attr info of \"%s\" on file \"%s\"\n",
67					nameBuffer, ref.name);
68				continue;
69			}
70
71			if (attributeCount == 0) {
72				if (!attributeDirCreated) {
73					directory.CreateDirectory(ATTRIBUTE_DIR_NAME, NULL);
74					if (!directory.Contains(ATTRIBUTE_DIR_NAME,
75						B_DIRECTORY_NODE)) {
76						printf("attribute store directory not available\n");
77						return;
78					}
79
80					attributeDir.SetTo(&directory, ATTRIBUTE_DIR_NAME);
81					attributeDirCreated = true;
82				}
83
84				attributeDir.CreateFile(ref.name, NULL);
85				if (attributeFile.SetTo(&attributeDir, ref.name,
86					B_WRITE_ONLY | B_ERASE_FILE) != B_OK) {
87					printf("cannot open attribute file for writing\n");
88					break;
89				}
90
91				attributeFile.Seek(sizeof(attribute_file) - 1, SEEK_SET);
92			}
93
94			attribute_entry entry;
95			entry.type = info.type;
96			entry.size = info.size;
97			entry.name_length = strlen(nameBuffer) + 1;
98			attributeFile.Write(&entry, sizeof(attribute_entry) - 1);
99			attributeFile.Write(nameBuffer, entry.name_length);
100
101			off_t offset = 0;
102			while (info.size > 0) {
103				size_t copySize = min_c(info.size, COPY_BUFFER_SIZE);
104				if (node.ReadAttr(nameBuffer, info.type, offset, copyBuffer,
105					copySize) < B_OK) {
106					printf("error reading attribute \"%s\" of file \"%s\"\n",
107						nameBuffer, ref.name);
108					return;
109				}
110
111				attributeFile.Write(copyBuffer, copySize);
112				info.size -= COPY_BUFFER_SIZE;
113				offset += COPY_BUFFER_SIZE;
114			}
115
116			attributeCount++;
117		}
118
119		if (attributeCount > 0) {
120			attribute_file file;
121			file.magic = ATTRIBUTE_FILE_MAGIC;
122			file.entry_count = attributeCount;
123			attributeFile.WriteAt(0, &file, sizeof(attribute_file) - 1);
124		}
125
126		if (node.IsDirectory()) {
127			BDirectory subDirectory(&ref);
128			recurse_directory(subDirectory, copyBuffer);
129		}
130	}
131}
132
133
134int
135main(int argc, char *argv[])
136{
137	if (argc < 2) {
138		printf("usage: %s <root directory>\n", argv[0]);
139		return 1;
140	}
141
142	uint8 *copyBuffer = (uint8 *)malloc(COPY_BUFFER_SIZE);
143	if (copyBuffer == NULL) {
144		printf("cannot allocate copy buffer\n");
145		return 2;
146	}
147
148	BDirectory root(argv[1]);
149	recurse_directory(root, copyBuffer);
150
151	free(copyBuffer);
152	return 0;
153}
154