xref: /haiku/src/bin/mvattr.cpp (revision 23d878482ed22e55dad6d1fca1df7bea42eb157c)
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 
20 static 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 
34 extern const char *__progname;
35 static const char *kProgramName = __progname;
36 
37 static const size_t kBufferSize = 1024 * 1024;
38 static uint8 kBuffer[kBufferSize];
39 
40 
41 status_t
42 moveAttribute(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 
118 status_t
119 moveAttributes(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 
127 status_t
128 renameAttribute(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 
144 void
145 usage(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 
159 int
160 main(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