1 /* 2 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 #include <sys/xattr.h> 7 8 #include <ctype.h> 9 #include <errno.h> 10 #include <stdio.h> 11 #include <stdlib.h> 12 #include <string.h> 13 14 #include <algorithm> 15 16 #include <fs_attr.h> 17 #include <TypeConstants.h> 18 19 #include <syscall_utils.h> 20 21 22 static const char* const kXattrNamespace = "user.haiku."; 23 static const size_t kXattrNamespaceLength = 11; 24 25 26 namespace { 27 28 29 struct AttributeName { 30 char name[B_ATTR_NAME_LENGTH + 32]; 31 uint32 type; 32 33 void Init(const char* haikuName, uint32 type) 34 { 35 if (type == B_XATTR_TYPE) { 36 // a simple xattr -- copy the name verbatim 37 strlcpy(name, haikuName, sizeof(name)); 38 this->type = type; 39 } else { 40 // a Haiku attribute -- map the name 41 42 // create a type string -- if the four bytes of the type are 43 // printable, just use them as the type string, otherwise convert 44 // to hex 45 char typeString[9]; 46 uint8 typeBytes[4] = { (uint8)((type >> 24) & 0xff), 47 (uint8)((type >> 16) & 0xff), (uint8)((type >> 8) & 0xff), 48 (uint8)(type & 0xff) }; 49 if (isprint(typeBytes[0]) && isprint(typeBytes[1]) 50 && isprint(typeBytes[2]) && isprint(typeBytes[3])) { 51 typeString[0] = typeBytes[0]; 52 typeString[1] = typeBytes[1]; 53 typeString[2] = typeBytes[2]; 54 typeString[3] = typeBytes[3]; 55 typeString[4] = '\0'; 56 } else 57 sprintf(typeString, "%08" B_PRIx32 , type); 58 59 snprintf(name, sizeof(name), "%s%s#%s", kXattrNamespace, 60 haikuName, typeString); 61 } 62 } 63 64 void Init(const char* xattrName) 65 { 66 if (strncmp(xattrName, kXattrNamespace, kXattrNamespaceLength) == 0) { 67 // a Haiku attribute -- extract the actual name and type 68 xattrName += kXattrNamespaceLength; 69 70 if (_DecodeNameAndType(xattrName)) 71 return; 72 } 73 74 // a simple xattr 75 strlcpy(name, xattrName, sizeof(name)); 76 this->type = B_XATTR_TYPE; 77 } 78 79 private: 80 81 bool _DecodeNameAndType(const char* xattrName) 82 { 83 const char* typeString = strrchr(xattrName, '#'); 84 if (typeString == NULL || typeString == xattrName) 85 return false; 86 typeString++; 87 88 size_t typeStringLength = strlen(typeString); 89 if (typeStringLength == 4) { 90 // the type string consists of the literal type bytes 91 type = ((uint32)typeString[0] << 24) | ((uint32)typeString[1] << 16) 92 | ((uint32)typeString[2] << 8) | (uint32)typeString[3]; 93 } else if (typeStringLength == 8) { 94 // must be hex-encoded 95 char* numberEnd; 96 type = strtoul(typeString, &numberEnd, 16); 97 if (numberEnd != typeString + 8) 98 return false; 99 } else 100 return false; 101 102 strlcpy(name, xattrName, 103 std::min(sizeof(name), (size_t)(typeString - xattrName))); 104 // typeString - xattrName - 1 is the name length, but we need to 105 // specify one more for the terminating null 106 return true; 107 } 108 }; 109 110 111 struct Node { 112 Node(const char* path, bool traverseSymlinks) 113 { 114 fFileFD = open(path, O_RDONLY | (traverseSymlinks ? 0 : O_NOTRAVERSE)); 115 fOwnsFileFD = true; 116 } 117 118 Node(int fileFD) 119 { 120 fFileFD = fileFD; 121 fOwnsFileFD = false; 122 123 if (fileFD < 0) 124 errno = B_FILE_ERROR; 125 } 126 127 ~Node() 128 { 129 if (fFileFD >= 0 && fOwnsFileFD) 130 close(fFileFD); 131 } 132 133 int Set(const char* attribute, int flags, const void* buffer, size_t size) 134 { 135 if (fFileFD < 0) 136 return -1; 137 138 // flags to open mode 139 int openMode = O_WRONLY | O_TRUNC; 140 if (flags == XATTR_CREATE) { 141 openMode |= O_CREAT | O_EXCL; 142 } else if (flags == XATTR_REPLACE) { 143 // pure open -- attribute must exist 144 } else 145 openMode |= O_CREAT; 146 147 AttributeName attributeName; 148 attributeName.Init(attribute); 149 150 int attributeFD = fs_fopen_attr(fFileFD, attributeName.name, 151 attributeName.type, openMode); 152 if (attributeFD < 0) { 153 // translate B_ENTRY_NOT_FOUND to ENOATTR 154 if (errno == B_ENTRY_NOT_FOUND) 155 errno = ENOATTR; 156 157 return -1; 158 } 159 160 ssize_t written = write(attributeFD, buffer, size); 161 162 fs_close_attr(attributeFD); 163 164 if (written < 0) 165 return -1; 166 if ((size_t)written != size) 167 RETURN_AND_SET_ERRNO(B_FILE_ERROR); 168 169 return 0; 170 } 171 172 ssize_t Get(const char* attribute, void* buffer, size_t size) 173 { 174 if (fFileFD < 0) 175 return -1; 176 177 AttributeName attributeName; 178 attributeName.Init(attribute); 179 180 // get the attribute size -- we read all or nothing 181 attr_info info; 182 if (fs_stat_attr(fFileFD, attributeName.name, &info) != 0) { 183 // translate B_ENTRY_NOT_FOUND to ENOATTR 184 if (errno == B_ENTRY_NOT_FOUND) 185 errno = ENOATTR; 186 return -1; 187 } 188 189 // if an empty buffer is given, return the attribute size 190 if (size == 0) 191 return info.size; 192 193 // if the buffer is too small, fail 194 if (size < info.size) { 195 errno = ERANGE; 196 return -1; 197 } 198 199 ssize_t bytesRead = fs_read_attr(fFileFD, attributeName.name, 200 info.type, 0, buffer, info.size); 201 202 // translate B_ENTRY_NOT_FOUND to ENOATTR 203 if (bytesRead < 0 && errno == B_ENTRY_NOT_FOUND) 204 errno = ENOATTR; 205 206 return bytesRead; 207 } 208 209 int Remove(const char* attribute) 210 { 211 if (fFileFD < 0) 212 return -1; 213 214 AttributeName attributeName; 215 attributeName.Init(attribute); 216 217 int result = fs_remove_attr(fFileFD, attributeName.name); 218 219 // translate B_ENTRY_NOT_FOUND to ENOATTR 220 if (result != 0 && errno == B_ENTRY_NOT_FOUND) 221 errno = ENOATTR; 222 223 return result; 224 } 225 226 ssize_t GetList(void* _buffer, size_t size) 227 { 228 char* buffer = (char*)_buffer; 229 230 if (fFileFD < 0) 231 return -1; 232 233 // open attribute directory 234 DIR* dir = fs_fopen_attr_dir(fFileFD); 235 if (dir == NULL) 236 return -1; 237 238 // read the attributes 239 size_t remainingSize = size; 240 size_t totalSize = 0; 241 while (struct dirent* entry = readdir(dir)) { 242 attr_info info; 243 if (fs_stat_attr(fFileFD, entry->d_name, &info) != 0) 244 continue; 245 246 AttributeName attributeName; 247 attributeName.Init(entry->d_name, info.type); 248 249 size_t nameLength = strlen(attributeName.name); 250 totalSize += nameLength + 1; 251 252 if (remainingSize > nameLength) { 253 strcpy((char*)buffer, attributeName.name); 254 buffer += nameLength + 1; 255 remainingSize -= nameLength + 1; 256 } else 257 remainingSize = 0; 258 } 259 260 closedir(dir); 261 262 // If the buffer was too small, fail. 263 if (size != 0 && totalSize > size) { 264 errno = ERANGE; 265 return -1; 266 } 267 268 return totalSize; 269 } 270 271 private: 272 int fFileFD; 273 bool fOwnsFileFD; 274 }; 275 276 277 } // namespace 278 279 280 // #pragma mark - 281 282 283 ssize_t 284 getxattr(const char* path, const char* attribute, void* buffer, size_t size) 285 { 286 return Node(path, true).Get(attribute, buffer, size); 287 } 288 289 290 ssize_t 291 lgetxattr(const char* path, const char* attribute, void* buffer, size_t size) 292 { 293 return Node(path, false).Get(attribute, buffer, size); 294 } 295 296 297 ssize_t 298 fgetxattr(int fd, const char* attribute, void* buffer, size_t size) 299 { 300 return Node(fd).Get(attribute, buffer, size); 301 } 302 303 304 int 305 setxattr(const char* path, const char* attribute, const void* buffer, 306 size_t size, int flags) 307 { 308 return Node(path, true).Set(attribute, flags, buffer, size); 309 } 310 311 312 int 313 lsetxattr(const char* path, const char* attribute, const void* buffer, 314 size_t size, int flags) 315 { 316 return Node(path, false).Set(attribute, flags, buffer, size); 317 } 318 319 320 int 321 fsetxattr(int fd, const char* attribute, const void* buffer, size_t size, 322 int flags) 323 { 324 return Node(fd).Set(attribute, flags, buffer, size); 325 } 326 327 328 int 329 removexattr (const char* path, const char* attribute) 330 { 331 return Node(path, true).Remove(attribute); 332 } 333 334 335 int 336 lremovexattr (const char* path, const char* attribute) 337 { 338 return Node(path, false).Remove(attribute); 339 } 340 341 342 int 343 fremovexattr (int fd, const char* attribute) 344 { 345 return Node(fd).Remove(attribute); 346 } 347 348 349 ssize_t 350 listxattr(const char* path, char* buffer, size_t size) 351 { 352 return Node(path, true).GetList(buffer, size); 353 } 354 355 356 ssize_t 357 llistxattr(const char* path, char* buffer, size_t size) 358 { 359 return Node(path, false).GetList(buffer, size); 360 } 361 362 363 ssize_t 364 flistxattr(int fd, char* buffer, size_t size) 365 { 366 return Node(fd).GetList(buffer, size); 367 } 368