1 /* 2 * Copyright 2017, Chế Vũ Gia Hy, cvghy116@gmail.com. 3 * Copyright 2010-2011, Jérôme Duval, korli@users.berlios.de. 4 * Copyright 2010, François Revol, <revol@free.fr>. 5 * Copyright 2004-2008, Axel Dörfler, axeld@pinc-software.de. 6 * This file may be used under the terms of the MIT License. 7 */ 8 9 //! connection between pure inode and kernel_interface attributes 10 11 12 #include "Attribute.h" 13 #include "BTree.h" 14 #include "CRCTable.h" 15 #include "Utility.h" 16 17 18 //#define TRACE_BTRFS 19 #ifdef TRACE_BTRFS 20 # define TRACE(x...) dprintf("\33[34mbtrfs:\33[0m " x) 21 #else 22 # define TRACE(x...) ; 23 #endif 24 25 26 Attribute::Attribute(Inode* inode) 27 : 28 fVolume(inode->GetVolume()), 29 fInode(inode), 30 fName(NULL) 31 { 32 } 33 34 35 Attribute::Attribute(Inode* inode, attr_cookie* cookie) 36 : 37 fVolume(inode->GetVolume()), 38 fInode(inode), 39 fName(cookie->name) 40 { 41 } 42 43 44 Attribute::~Attribute() 45 { 46 } 47 48 49 status_t 50 Attribute::CheckAccess(const char* name, int openMode) 51 { 52 return fInode->CheckPermissions(open_mode_to_access(openMode) 53 | (openMode & O_TRUNC ? W_OK : 0)); 54 } 55 56 57 status_t 58 Attribute::Open(const char* name, int openMode, attr_cookie** _cookie) 59 { 60 TRACE("Open\n"); 61 status_t status = CheckAccess(name, openMode); 62 if (status < B_OK) 63 return status; 64 65 status = _Lookup(name, strlen(name)); 66 if (status < B_OK) 67 return status; 68 69 attr_cookie* cookie = new(std::nothrow) attr_cookie; 70 if (cookie == NULL) 71 return B_NO_MEMORY; 72 73 fName = name; 74 75 // initialize the cookie 76 strlcpy(cookie->name, fName, B_ATTR_NAME_LENGTH); 77 cookie->open_mode = openMode; 78 cookie->create = false; 79 80 *_cookie = cookie; 81 return B_OK; 82 } 83 84 85 status_t 86 Attribute::Stat(struct stat& stat) 87 { 88 TRACE("Stat\n"); 89 90 size_t nameLength = strlen(fName); 91 btrfs_dir_entry* entries; 92 uint32 length; 93 status_t status = _Lookup(fName, nameLength, &entries, &length); 94 if (status < B_OK) 95 return status; 96 97 btrfs_dir_entry* entry; 98 status = _FindEntry(entries, length, fName, nameLength, &entry); 99 if (status != B_OK) { 100 free(entries); 101 return status; 102 } 103 104 // found an entry to stat 105 stat.st_type = B_XATTR_TYPE; 106 stat.st_size = entry->DataLength(); 107 free(entries); 108 return B_OK; 109 } 110 111 112 status_t 113 Attribute::Read(attr_cookie* cookie, off_t pos, uint8* buffer, size_t* _length) 114 { 115 if (pos < 0LL) 116 return ERANGE; 117 118 size_t nameLength = strlen(fName); 119 btrfs_dir_entry* entries; 120 uint32 length; 121 status_t status = _Lookup(fName, nameLength, &entries, &length); 122 if (status < B_OK) 123 return status; 124 125 btrfs_dir_entry* entry; 126 status = _FindEntry(entries, length, fName, nameLength, &entry); 127 if (status != B_OK) { 128 free(entries); 129 return status; 130 } 131 132 // found an entry to read 133 if (pos + *_length > entry->DataLength()) 134 length = entry->DataLength() - pos; 135 else 136 length = *_length - pos; 137 memcpy(buffer, (uint8*)entry + entry->NameLength() 138 + sizeof(btrfs_dir_entry) + (uint32)pos, length); 139 *_length = length; 140 141 free(entries); 142 return B_OK; 143 } 144 145 146 status_t 147 Attribute::_Lookup(const char* name, size_t nameLength, 148 btrfs_dir_entry** _entries, uint32* _length) 149 { 150 uint32 hash = calculate_crc((uint32)~1, (uint8*)name, nameLength); 151 struct btrfs_key key; 152 key.SetType(BTRFS_KEY_TYPE_XATTR_ITEM); 153 key.SetObjectID(fInode->ID()); 154 key.SetOffset(hash); 155 BTree::Path path(fInode->GetVolume()->FSTree()); 156 157 btrfs_dir_entry* entries; 158 uint32 length; 159 status_t status = fInode->GetVolume()->FSTree()->FindExact(&path, key, 160 (void**)&entries, &length); 161 if (status != B_OK) { 162 TRACE("AttributeIterator::Lookup(): Couldn't find entry with hash %" 163 B_PRIu32 " \"%s\"\n", hash, name); 164 return status; 165 } 166 167 if (_entries == NULL) 168 free(entries); 169 else 170 *_entries = entries; 171 172 if (_length != NULL) 173 *_length = length; 174 175 return B_OK; 176 } 177 178 179 status_t 180 Attribute::_FindEntry(btrfs_dir_entry* entries, size_t length, 181 const char* name, size_t nameLength, btrfs_dir_entry** _entry) 182 { 183 btrfs_dir_entry* entry = entries; 184 uint16 current = 0; 185 while (current < length) { 186 current += entry->Length(); 187 break; 188 // TODO there could be several entries with the same name hash 189 entry = (btrfs_dir_entry*)((uint8*)entry + entry->Length()); 190 } 191 192 *_entry = entry; 193 return B_OK; 194 } 195