1/*
2 * Copyright 2012, Axel D��rfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include <errno.h>
8#include <fcntl.h>
9#include <getopt.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13#include <unistd.h>
14
15#include <fs_attr.h>
16
17#include <AutoDeleter.h>
18
19
20static struct option const kLongOptions[] = {
21	{"help", no_argument, 0, 'h'},
22	{"force", no_argument, 0, 'f'},
23	{"cross-file", no_argument, 0, 'x'},
24	{"verbose", no_argument, 0, 'v'},
25	{NULL}
26};
27
28// command flags
29#define FLAG_VERBOSE				1
30#define FLAG_FORCE					2
31#define FLAG_DO_NOT_FOLLOW_LINKS	4
32
33
34extern const char *__progname;
35static const char *kProgramName = __progname;
36
37static const size_t kBufferSize = 1024 * 1024;
38static uint8 kBuffer[kBufferSize];
39
40
41status_t
42moveAttribute(const char* fromFile, const char* fromAttr, const char* toFile,
43	const char* toAttr, uint32 flags)
44{
45	int fromFileFD = open(fromFile, O_RDONLY);
46	if (fromFileFD < 0)
47		return errno;
48
49	FileDescriptorCloser fromFileFDCloser(fromFileFD);
50	FileDescriptorCloser toFileFDCloser;
51
52	int toFileFD = fromFileFD;
53	if (strcmp(fromFile, toFile) != 0) {
54		toFileFD = open(toFile, O_RDONLY);
55		if (toFileFD < 0)
56			return errno;
57
58		toFileFDCloser.SetTo(toFileFD);
59	}
60
61	attr_info fromInfo;
62	if (fs_stat_attr(fromFileFD, fromAttr, &fromInfo) != 0) {
63		// TODO: optionally fail
64		if ((flags & FLAG_VERBOSE) != 0)
65			fprintf(stderr, "Warning: file \"%s\" does not have attribute "
66				"\"%s\"\n", fromFile, fromAttr);
67		return B_OK;
68	}
69
70	attr_info toInfo;
71	if ((flags & FLAG_FORCE) == 0
72		&& fs_stat_attr(toFileFD, toAttr, &toInfo) == 0)
73		return B_FILE_EXISTS;
74
75	int fromAttrFD = fs_fopen_attr(fromFileFD, fromAttr, fromInfo.type,
76		O_RDONLY | O_EXCL);
77	if (fromAttrFD < 0)
78		return errno;
79
80	FileDescriptorCloser fromAttrFDCloser(fromAttrFD);
81
82	int toAttrFD = fs_fopen_attr(toFileFD, toAttr, fromInfo.type,
83		O_CREAT | O_WRONLY | O_TRUNC);
84	if (fromAttrFD < 0)
85		return errno;
86
87	FileDescriptorCloser toAttrFDCloser(toAttrFD);
88
89	size_t bytesLeft = fromInfo.size;
90	off_t offset = 0;
91	while (bytesLeft > 0) {
92		size_t size = min_c(kBufferSize, bytesLeft);
93		ssize_t bytesRead = read_pos(fromAttrFD, offset, kBuffer, size);
94		if (bytesRead < 0) {
95			fs_remove_attr(toFileFD, toAttr);
96			return errno;
97		}
98
99		ssize_t bytesWritten = write_pos(toAttrFD, offset, kBuffer, bytesRead);
100		if (bytesWritten != bytesRead) {
101			fs_remove_attr(toFileFD, toAttr);
102			if (bytesWritten >= 0)
103				return B_IO_ERROR;
104			return errno;
105		}
106
107		bytesLeft -= bytesRead;
108		offset += bytesRead;
109	}
110
111	if (fs_remove_attr(fromFileFD, fromAttr) < 0)
112		return errno;
113
114	return B_OK;
115}
116
117
118status_t
119moveAttributes(const char* fromFile, const char* toFile,
120	const char* attrPattern, uint32 flags)
121{
122	// TODO: implement me (with pattern support)
123	return EXIT_FAILURE;
124}
125
126
127status_t
128renameAttribute(const char* fileName, const char* fromAttr, const char* toAttr,
129	uint32 flags)
130{
131	if ((flags & FLAG_VERBOSE) != 0)
132		printf("Rename attribute: %s\n", fileName);
133
134	status_t status = moveAttribute(fileName, fromAttr, fileName, toAttr,
135		flags);
136	if (status != B_OK) {
137		fprintf(stderr, "%s: Could not rename attribute for file \"%s\": %s\n",
138			kProgramName, fileName, strerror(status));
139	}
140	return status;
141}
142
143
144void
145usage(int returnValue)
146{
147	fprintf(stderr, "usage: %s [-fPv] attr-from attr-to file1 [file2...]\n"
148		"   or: %s -x[fPv] <attr-pattern> from-file to-file\n\n"
149		"\t-f|--force       Overwrite existing attributes\n"
150		"\t-P               Don't resolve links\n"
151		"\t-x|--cross-file  Moves the attributes to another file\n"
152		"\t-v|--verbose     Verbose output\n",
153		kProgramName, kProgramName);
154
155	exit(returnValue);
156}
157
158
159int
160main(int argc, char** argv)
161{
162	uint32 flags = 0;
163	bool crossFile = false;
164
165	int c;
166	while ((c = getopt_long(argc, argv, "hfxPv", kLongOptions, NULL)) != -1) {
167		switch (c) {
168			case 0:
169				break;
170			case 'f':
171				flags |= FLAG_FORCE;
172				break;
173			case 'x':
174				crossFile = true;
175				break;
176			case 'P':
177				flags |= FLAG_DO_NOT_FOLLOW_LINKS;
178				break;
179			case 'v':
180				flags |= FLAG_VERBOSE;
181				break;
182			case 'h':
183				usage(EXIT_SUCCESS);
184				break;
185			default:
186				usage(EXIT_FAILURE);
187				break;
188		}
189	}
190
191	if (argc - optind < 3)
192		usage(EXIT_FAILURE);
193
194	if (crossFile) {
195		const char* attrPattern = argv[optind++];
196		const char* fromFile = argv[optind++];
197		const char* toFile = argv[optind++];
198
199		return moveAttributes(fromFile, toFile, attrPattern, flags);
200	}
201
202	const char* fromAttr = argv[optind++];
203	const char* toAttr = argv[optind++];
204	int returnCode = EXIT_SUCCESS;
205
206	for (int i = optind; i < argc; i++) {
207		if (renameAttribute(argv[i], fromAttr, toAttr, flags) != B_OK)
208			returnCode = EXIT_FAILURE;
209	}
210
211	return returnCode;
212}
213