1 /* 2 * Copyright 2001-2009, Axel Dörfler, axeld@pinc-software.de. 3 * This file may be used under the terms of the MIT License. 4 */ 5 6 7 #include <ctype.h> 8 #include <errno.h> 9 #include <new> 10 #include <stdio.h> 11 #include <stdlib.h> 12 #include <string.h> 13 14 #include <Directory.h> 15 #include <Entry.h> 16 #include <File.h> 17 #include <List.h> 18 #include <Node.h> 19 #include <Path.h> 20 #include <String.h> 21 #include <fs_attr.h> 22 #include <fs_index.h> 23 #include <fs_info.h> 24 25 26 extern const char *__progname; 27 static const char *kProgramName = __progname; 28 29 bool gRecursive = false; // enter directories recursively 30 bool gVerbose = false; 31 char *gAttrPattern; 32 bool gIsPattern = false; 33 bool gFromVolume = false; // copy indices from another volume 34 BList gAttrList; // list of indices of that volume 35 36 37 class Attribute { 38 public: 39 Attribute(const char *name); 40 ~Attribute(); 41 42 status_t ReadFromFile(BNode *node); 43 status_t WriteToFile(BNode *node); 44 status_t RemoveFromFile(BNode *node); 45 46 const char *Name() const { return fName.String(); } 47 type_code Type() const { return fType; } 48 size_t Length() const { return fLength; } 49 50 protected: 51 BString fName; 52 type_code fType; 53 void *fBuffer; 54 size_t fLength; 55 }; 56 57 58 Attribute::Attribute(const char *name) 59 : 60 fName(name), 61 fBuffer(NULL), 62 fLength(0) 63 { 64 } 65 66 67 Attribute::~Attribute() 68 { 69 free(fBuffer); 70 } 71 72 73 status_t 74 Attribute::ReadFromFile(BNode *node) 75 { 76 attr_info info; 77 status_t status = node->GetAttrInfo(fName.String(), &info); 78 if (status != B_OK) 79 return status; 80 81 fType = info.type; 82 fLength = info.size; 83 84 if ((fBuffer = malloc(fLength)) == NULL) 85 return B_NO_MEMORY; 86 87 ssize_t bytesRead = node->ReadAttr(fName.String(), fType, 0, fBuffer, 88 fLength); 89 if (bytesRead < B_OK) 90 return bytesRead; 91 if (bytesRead < (ssize_t)fLength) 92 return B_IO_ERROR; 93 94 return B_OK; 95 } 96 97 98 status_t 99 Attribute::WriteToFile(BNode *node) 100 { 101 ssize_t bytesWritten = node->WriteAttr(fName.String(), fType, 0, fBuffer, 102 fLength); 103 if (bytesWritten < B_OK) 104 return bytesWritten; 105 if (bytesWritten < (ssize_t)fLength) 106 return B_IO_ERROR; 107 108 return B_OK; 109 } 110 111 112 status_t 113 Attribute::RemoveFromFile(BNode *node) 114 { 115 return node->RemoveAttr(fName.String()); 116 } 117 118 119 // #pragma mark - 120 121 122 bool 123 nameMatchesPattern(char *name) 124 { 125 if (!gIsPattern) 126 return !strcmp(name,gAttrPattern); 127 128 // test the beginning 129 int i = 0; 130 for(; name[i]; i++) { 131 if (gAttrPattern[i] == '*') 132 break; 133 if (name[i] != gAttrPattern[i]) 134 return false; 135 } 136 137 // test the end 138 int j = strlen(name) - 1; 139 int k = strlen(gAttrPattern) - 1; 140 for(; j >= i && k >= i; j--, k--) { 141 if (gAttrPattern[k] == '*') 142 return true; 143 if (name[j] != gAttrPattern[k]) 144 return false; 145 } 146 if (gAttrPattern[k] != '*') 147 return false; 148 149 return true; 150 } 151 152 153 bool 154 isAttrInList(char *name) 155 { 156 for (int32 index = gAttrList.CountItems();index-- > 0;) { 157 const char *attr = (const char *)gAttrList.ItemAt(index); 158 if (!strcmp(attr, name)) 159 return true; 160 } 161 return false; 162 } 163 164 165 void 166 handleFile(BEntry *entry, BNode *node) 167 { 168 // Recurse the directories 169 if (gRecursive && entry->IsDirectory()) { 170 BDirectory dir(entry); 171 BEntry entryIterator; 172 173 dir.Rewind(); 174 while (dir.GetNextEntry(&entryIterator, false) == B_OK) { 175 BNode innerNode; 176 handleFile(&entryIterator, &innerNode); 177 } 178 179 // also rewrite the attributes of the directory 180 } 181 182 char name[B_FILE_NAME_LENGTH]; 183 entry->GetName(name); 184 185 status_t status = node->SetTo(entry); 186 if (status != B_OK) { 187 fprintf(stderr, "%s: could not open \"%s\": %s\n", kProgramName, name, 188 strerror(status)); 189 return; 190 } 191 192 // rewrite file attributes 193 194 char attrName[B_ATTR_NAME_LENGTH]; 195 Attribute *attr; 196 BList list; 197 198 // building list 199 node->RewindAttrs(); 200 while (node->GetNextAttrName(attrName) == B_OK) { 201 if (gFromVolume) { 202 if (!isAttrInList(attrName)) 203 continue; 204 } else if (!nameMatchesPattern(attrName)) 205 continue; 206 207 attr = new(std::nothrow) Attribute(attrName); 208 if (attr == NULL) { 209 fprintf(stderr, "%s: out of memory.\n", kProgramName); 210 exit(1); 211 } 212 213 status = attr->ReadFromFile(node); 214 if (status != B_OK) { 215 fprintf(stderr, "%s: could not read attribute \"%s\" of file " 216 "\"%s\": %s\n", kProgramName, attrName, name, strerror(status)); 217 delete attr; 218 continue; 219 } 220 if (gVerbose) { 221 printf("%s: read attribute '%s' (%ld bytes of data)\n", name, 222 attrName, attr->Length()); 223 } 224 if (!list.AddItem(attr)) { 225 fprintf(stderr, "%s: out of memory.\n", kProgramName); 226 exit(1); 227 } 228 229 if (!gFromVolume) { 230 // creates index to that attribute if necessary 231 entry_ref ref; 232 if (entry->GetRef(&ref) == B_OK) { 233 index_info indexInfo; 234 if (fs_stat_index(ref.device, attrName, &indexInfo) != B_OK) 235 fs_create_index(ref.device, attrName, attr->Type(), 0); 236 } 237 } 238 } 239 240 // remove attrs 241 for (int32 i = list.CountItems(); i-- > 0;) { 242 attr = static_cast<Attribute *>(list.ItemAt(i)); 243 if (attr->RemoveFromFile(node) != B_OK) { 244 fprintf(stderr, "%s: could not remove attribute '%s' from file " 245 "'%s'.\n", kProgramName, attr->Name(), name); 246 } 247 } 248 249 // rewrite attrs and empty the list 250 while ((attr = static_cast<Attribute *>(list.RemoveItem((int32)0))) != NULL) { 251 // write attribute back to file 252 status = attr->WriteToFile(node); 253 if (status != B_OK) { 254 fprintf(stderr, "%s: could not write attribute '%s' to file \"%s\":" 255 " %s\n", kProgramName, attr->Name(), name, strerror(status)); 256 } else if (gVerbose) { 257 printf("%s: wrote attribute '%s' (%ld bytes of data)\n", name, 258 attr->Name(), attr->Length()); 259 } 260 261 delete attr; 262 } 263 } 264 265 266 void 267 copyIndicesFromVolume(const char *path, BEntry &to) 268 { 269 entry_ref ref; 270 if (to.GetRef(&ref) != B_OK) { 271 fprintf(stderr, "%s: Could not open target volume.\n", kProgramName); 272 return; 273 } 274 275 dev_t targetDevice = ref.device; 276 dev_t sourceDevice = dev_for_path(path); 277 if (sourceDevice < B_OK) { 278 fprintf(stderr, "%s: Could not open source volume: %s\n", kProgramName, 279 strerror(sourceDevice)); 280 return; 281 } 282 283 DIR *indexDirectory = fs_open_index_dir(sourceDevice); 284 if (indexDirectory == NULL) 285 return; 286 287 while (dirent *index = fs_read_index_dir(indexDirectory)) { 288 index_info indexInfo; 289 if (fs_stat_index(sourceDevice, index->d_name, &indexInfo) != B_OK) { 290 fprintf(stderr, "%s: Could not get information about index " 291 "\"%s\": %s\n", kProgramName, index->d_name, strerror(errno)); 292 continue; 293 } 294 295 if (fs_create_index(targetDevice, index->d_name, indexInfo.type, 0) 296 == -1 && errno != B_FILE_EXISTS && errno != B_BAD_VALUE) { 297 fprintf(stderr, "Could not create index '%s' (type = %" B_PRIu32 298 "): %s\n", index->d_name, indexInfo.type, strerror(errno)); 299 } else 300 gAttrList.AddItem(strdup(index->d_name)); 301 } 302 fs_close_index_dir(indexDirectory); 303 } 304 305 306 void 307 printUsage(char *cmd) 308 { 309 printf("usage: %s [-rvf] attr <list of filenames and/or directories>\n" 310 " -r\tenter directories recursively\n" 311 " -v\tverbose output\n" 312 " -f\tcreate/update all indices from the source volume,\n\t\"attr\" is " 313 "the path to the source volume\n", cmd); 314 } 315 316 317 int 318 main(int argc, char **argv) 319 { 320 char *cmd = argv[0]; 321 322 if (argc < 3) { 323 printUsage(cmd); 324 return 1; 325 } 326 327 while (*++argv && **argv == '-') { 328 for (int i = 1; (*argv)[i]; i++) { 329 switch ((*argv)[i]) { 330 case 'f': 331 gFromVolume = true; 332 break; 333 case 'r': 334 gRecursive = true; 335 break; 336 case 'v': 337 gVerbose = true; 338 break; 339 default: 340 printUsage(cmd); 341 return(1); 342 } 343 } 344 } 345 gAttrPattern = *argv; 346 if (strchr(gAttrPattern,'*')) 347 gIsPattern = true; 348 349 while (*++argv) { 350 BEntry entry(*argv); 351 BNode node; 352 353 if (entry.InitCheck() == B_OK) { 354 if (gFromVolume) 355 copyIndicesFromVolume(gAttrPattern, entry); 356 handleFile(&entry, &node); 357 } else 358 fprintf(stderr, "%s: could not find \"%s\".\n", kProgramName, *argv); 359 } 360 361 return 0; 362 } 363