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