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
moveAttribute(const char * fromFile,const char * fromAttr,const char * toFile,const char * toAttr,uint32 flags)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
moveAttributes(const char * fromFile,const char * toFile,const char * attrPattern,uint32 flags)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
renameAttribute(const char * fileName,const char * fromAttr,const char * toAttr,uint32 flags)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
usage(int returnValue)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
main(int argc,char ** argv)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