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