vfs: check userland buffers before reading them.
[haiku.git] / src / bin / catattr.cpp
blob73387c6f7cc7da7ae7be92fa848430c31bb6b13a
1 /*
2 * Copyright 2010, Alexander Shagarov, alexander.shagarov@gmail.com.
3 * Copyright 2005, Stephan Aßmus, superstippi@yellowbites.com.
4 * Copyright 2004-2010, Axel Dörfler, axeld@pinc-software.de.
5 * Copyright 2002, Sebastian Nozzi.
7 * Distributed under the terms of the MIT license.
8 */
11 #include <ctype.h>
12 #include <errno.h>
13 #include <getopt.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <unistd.h>
19 #include <fs_attr.h>
20 #include <Mime.h>
21 #include <String.h>
22 #include <TypeConstants.h>
25 /*! Used to present the characters in the raw data view */
26 static void
27 putCharOrDot(uchar c)
29 putchar(isgraph(c) ? c : '.');
33 /*! Dumps the contents of the attribute in the form of
34 raw data. This view is used for the type B_RAW_DATA_TYPE,
35 for custom types and for any type that is not directly
36 supported by the utility "addattr"
38 static void
39 dumpRawData(const char *buffer, size_t size)
41 const uint32 kChunkSize = 16;
42 uint32 dumpPosition = 0;
44 while (dumpPosition < size) {
45 // Position for this line
46 printf("0x%06" B_PRIx32 ": ", dumpPosition);
48 // Print the bytes in form of hexadecimal numbers
49 for (uint32 i = 0; i < kChunkSize; i++) {
50 if (dumpPosition + i < size) {
51 printf("%02x ", (uint8)buffer[dumpPosition + i]);
52 } else
53 printf(" ");
56 // Print the bytes in form of printable characters
57 // (whenever possible)
58 printf(" '");
59 for (uint32 i = 0; i < kChunkSize; i++) {
60 if (dumpPosition < size)
61 putCharOrDot(buffer[dumpPosition]);
62 else
63 putchar(' ');
65 dumpPosition++;
67 printf("'\n");
72 static const char*
73 type_to_string(uint32 type)
75 static char buffer[32];
77 int32 missed = 0, shift = 24;
78 uint8 value[4];
79 for (int32 i = 0; i < 4; i++, shift -= 8) {
80 value[i] = uint8(type >> shift);
81 if (value[i] < ' ' || value[i] > 127) {
82 value[i] = '.';
83 missed++;
87 if (missed < 2) {
88 sprintf(buffer, "'%c%c%c%c'", value[0], value[1], value[2],
89 value[3]);
90 } else
91 sprintf(buffer, "0x%08" B_PRIx32, type);
93 return buffer;
97 static BString
98 type_name(uint32 type)
100 switch (type) {
101 case B_INT8_TYPE:
102 return "int8";
103 case B_UINT8_TYPE:
104 return "uint8";
105 case B_INT16_TYPE:
106 return "int16";
107 case B_UINT16_TYPE:
108 return "uint16";
109 case B_INT32_TYPE:
110 return "int32";
111 case B_UINT32_TYPE:
112 return "uint32";
113 case B_INT64_TYPE:
114 return "int64";
115 case B_UINT64_TYPE:
116 return "uint64";
117 case B_FLOAT_TYPE:
118 return "float";
119 case B_DOUBLE_TYPE:
120 return "double";
121 case B_BOOL_TYPE:
122 return "bool";
123 case B_STRING_TYPE:
124 return "string";
125 case B_MESSAGE_TYPE:
126 return "message";
127 case B_RAW_TYPE:
128 return "raw_data";
130 default:
131 return type_to_string(type);
136 static status_t
137 catAttr(const char *attribute, const char *fileName, bool keepRaw,
138 bool dataOnly, bool resolveLinks)
140 int fd = open(fileName, O_RDONLY | (resolveLinks ? 0 : O_NOTRAVERSE));
141 if (fd < 0)
142 return errno;
144 attr_info info;
145 if (fs_stat_attr(fd, attribute, &info) < 0) {
146 close(fd);
147 return errno;
150 // limit size of the attribute, only the first 64k will make it on screen
151 off_t size = info.size;
152 bool cut = false;
153 if (size > 64 * 1024) {
154 size = 64 * 1024;
155 cut = true;
158 char* buffer = (char*)malloc(size);
159 if (!buffer) {
160 fprintf(stderr, "Could not allocate read buffer!\n");
161 close(fd);
162 return B_NO_MEMORY;
165 ssize_t bytesRead = fs_read_attr(fd, attribute, info.type, 0, buffer, size);
166 if (bytesRead < 0) {
167 free(buffer);
168 close(fd);
169 return errno;
172 if (bytesRead != size) {
173 fprintf(stderr, "Could only read %ld bytes from attribute!\n",
174 bytesRead);
175 free(buffer);
176 close(fd);
177 return B_ERROR;
180 if (keepRaw) {
181 off_t pos = 0;
182 ssize_t written = 0;
183 while (pos < info.size) {
184 // write what we have read so far
185 written = write(STDOUT_FILENO, buffer, bytesRead);
186 // check for write error
187 if (written < bytesRead) {
188 if (written >= 0) {
189 fprintf(stderr, "Could only write %ld bytes to stream!\n",
190 written);
191 written = B_ERROR;
192 } else {
193 fprintf(stderr, "Failed to write to stream: %s\n",
194 strerror(written));
196 break;
198 // read next chunk of data at pos
199 pos += bytesRead;
200 bytesRead = fs_read_attr(fd, attribute, info.type, pos, buffer,
201 size);
202 // check for read error
203 if (bytesRead < size && pos + bytesRead < info.size) {
204 if (bytesRead >= 0) {
205 fprintf(stderr, "Could only read %ld bytes from "
206 "attribute!\n", bytesRead);
207 } else {
208 fprintf(stderr, "Failed to read from attribute: %s\n",
209 strerror(bytesRead));
211 written = B_ERROR;
212 break;
215 free(buffer);
216 if (written > 0)
217 written = B_OK;
218 close(fd);
219 return written;
222 if (!dataOnly)
223 printf("%s : %s : ", fileName, type_name(info.type).String());
225 switch (info.type) {
226 case B_INT8_TYPE:
227 printf("%" B_PRId8 "\n", *((int8*)buffer));
228 break;
229 case B_UINT8_TYPE:
230 printf("%" B_PRIu8 "\n", *((uint8*)buffer));
231 break;
232 case B_INT16_TYPE:
233 printf("%" B_PRId16 "\n", *((int16*)buffer));
234 break;
235 case B_UINT16_TYPE:
236 printf("%" B_PRIu16 "\n", *((uint16*)buffer));
237 break;
238 case B_INT32_TYPE:
239 printf("%" B_PRId32 "\n", *((int32*)buffer));
240 break;
241 case B_UINT32_TYPE:
242 printf("%" B_PRIu32 "\n", *((uint32*)buffer));
243 break;
244 case B_INT64_TYPE:
245 printf("%" B_PRId64 "\n", *((int64*)buffer));
246 break;
247 case B_UINT64_TYPE:
248 printf("%" B_PRIu64 "\n", *((uint64*)buffer));
249 break;
250 case B_FLOAT_TYPE:
251 printf("%f\n", *((float*)buffer));
252 break;
253 case B_DOUBLE_TYPE:
254 printf("%f\n", *((double*)buffer));
255 break;
256 case B_BOOL_TYPE:
257 printf("%d\n", *((unsigned char*)buffer));
258 break;
259 case B_STRING_TYPE:
260 case B_MIME_STRING_TYPE:
261 case 'MSIG':
262 case 'MSDC':
263 case 'MPTH':
264 printf("%s\n", buffer);
265 break;
267 case B_MESSAGE_TYPE:
269 BMessage message;
270 if (!cut && message.Unflatten(buffer) == B_OK) {
271 message.PrintToStream();
272 break;
274 // supposed to fall through
277 default:
278 // The rest of the attributes types are displayed as raw data
279 dumpRawData(buffer, size);
280 break;
283 free(buffer);
284 close(fd);
285 return B_OK;
289 static int
290 usage(const char* program, int returnCode)
292 // Issue usage message
293 fprintf(returnCode == EXIT_SUCCESS ? stdout : stderr,
294 "usage: %s [-P] [--raw|-r] <attribute-name> <file1> [<file2>...]\n"
295 " -P\t\tDon't resolve links\n"
296 " --raw,-r\tGet the raw data of attributes\n"
297 " --data,-d\tShow the attribute data only\n", program);
299 return returnCode;
304 main(int argc, char *argv[])
306 char *program = strrchr(argv[0], '/');
307 if (program == NULL)
308 program = argv[0];
309 else
310 program++;
312 const struct option kLongOptions[] = {
313 {"raw", no_argument, NULL, 'r'},
314 {"data", no_argument, NULL, 'd'},
315 {"help", no_argument, NULL, 'h'},
316 {NULL, 0, NULL, 0}
319 bool keepRaw = false;
320 bool resolveLinks = true;
321 bool dataOnly = false;
323 int option;
324 while ((option = getopt_long(argc, argv, "rdPh", kLongOptions, NULL))
325 != -1) {
326 switch (option) {
327 case 'r':
328 keepRaw = true;
329 break;
330 case 'P':
331 resolveLinks = false;
332 break;
333 case 'd':
334 dataOnly = true;
335 break;
336 case 'h':
337 return usage(program, EXIT_SUCCESS);
339 default:
340 return usage(program, EXIT_FAILURE);
344 if (optind + 2 > argc)
345 return usage(program, EXIT_FAILURE);
347 int succeeded = 0;
348 const char* attrName = argv[optind++];
350 while (optind < argc) {
351 const char* fileName = argv[optind++];
352 status_t status = catAttr(attrName, fileName, keepRaw, dataOnly,
353 resolveLinks);
354 if (status != B_OK) {
355 fprintf(stderr, "%s: \"%s\", attribute \"%s\": %s\n",
356 program, fileName, attrName, strerror(status));
357 } else
358 succeeded++;
361 return succeeded != 0 ? EXIT_SUCCESS : EXIT_FAILURE;