1 /* 2 * Copyright 2004-2017, Axel Dörfler, axeld@pinc-software.de. 3 * This file may be used under the terms of the MIT License. 4 */ 5 6 7 //! Connection between pure inode and kernel_interface attributes. 8 9 10 #include "Attribute.h" 11 12 13 // TODO: clean this up, find a better separation between Inode and this class 14 // TODO: even after Create(), the attribute cannot be stat() for until the 15 // first write 16 17 18 extern void fill_stat_buffer(Inode* inode, struct stat& stat); 19 20 21 Attribute::Attribute(Inode* inode) 22 : 23 fNodeGetter(inode->GetVolume()), 24 fInode(inode), 25 fSmall(NULL), 26 fAttribute(NULL), 27 fName(NULL) 28 { 29 } 30 31 32 Attribute::Attribute(Inode* inode, attr_cookie* cookie) 33 : 34 fNodeGetter(inode->GetVolume()), 35 fInode(inode), 36 fSmall(NULL), 37 fAttribute(NULL), 38 fName(NULL) 39 { 40 Get(cookie->name); 41 } 42 43 44 Attribute::~Attribute() 45 { 46 Put(); 47 } 48 49 50 status_t 51 Attribute::InitCheck() 52 { 53 return (fSmall != NULL || fAttribute != NULL) ? B_OK : B_NO_INIT; 54 } 55 56 57 status_t 58 Attribute::CheckAccess(const char* name, int openMode) 59 { 60 // Opening the name attribute using this function is not allowed, 61 // also using the reserved indices name, last_modified, and size 62 // shouldn't be allowed. 63 // TODO: we might think about allowing to update those values, but 64 // really change their corresponding values in the bfs_inode structure 65 if (name[0] == FILE_NAME_NAME && name[1] == '\0' 66 // TODO: reenable this check -- some WonderBrush locale files used them 67 /* || !strcmp(name, "name") 68 || !strcmp(name, "last_modified") 69 || !strcmp(name, "size")*/) 70 RETURN_ERROR(B_NOT_ALLOWED); 71 72 return fInode->CheckPermissions(open_mode_to_access(openMode) 73 | (openMode & O_TRUNC ? W_OK : 0)); 74 } 75 76 77 status_t 78 Attribute::Get(const char* name) 79 { 80 Put(); 81 82 fName = name; 83 84 // try to find it in the small data region 85 if (recursive_lock_lock(&fInode->SmallDataLock()) == B_OK) { 86 fNodeGetter.SetToNode(fInode); 87 if (fNodeGetter.Node() == NULL) 88 return B_IO_ERROR; 89 90 fSmall = fInode->FindSmallData(fNodeGetter.Node(), (const char*)name); 91 if (fSmall != NULL) 92 return B_OK; 93 94 recursive_lock_unlock(&fInode->SmallDataLock()); 95 fNodeGetter.Unset(); 96 } 97 98 // then, search in the attribute directory 99 return fInode->GetAttribute(name, &fAttribute); 100 } 101 102 103 void 104 Attribute::Put() 105 { 106 if (fSmall != NULL) { 107 recursive_lock_unlock(&fInode->SmallDataLock()); 108 fNodeGetter.Unset(); 109 fSmall = NULL; 110 } 111 112 if (fAttribute != NULL) { 113 fInode->ReleaseAttribute(fAttribute); 114 fAttribute = NULL; 115 } 116 } 117 118 119 status_t 120 Attribute::Create(const char* name, type_code type, int openMode, 121 attr_cookie** _cookie) 122 { 123 status_t status = CheckAccess(name, openMode); 124 if (status != B_OK) 125 return status; 126 127 bool exists = Get(name) == B_OK; 128 if (exists && (openMode & O_EXCL) != 0) 129 return B_FILE_EXISTS; 130 131 attr_cookie* cookie = new(std::nothrow) attr_cookie; 132 if (cookie == NULL) 133 RETURN_ERROR(B_NO_MEMORY); 134 135 fName = name; 136 137 // initialize the cookie 138 strlcpy(cookie->name, fName, B_ATTR_NAME_LENGTH); 139 cookie->type = type; 140 cookie->open_mode = openMode; 141 cookie->create = true; 142 143 if (exists && (openMode & O_TRUNC) != 0) 144 _Truncate(); 145 146 *_cookie = cookie; 147 return B_OK; 148 } 149 150 151 status_t 152 Attribute::Open(const char* name, int openMode, attr_cookie** _cookie) 153 { 154 status_t status = CheckAccess(name, openMode); 155 if (status < B_OK) 156 return status; 157 158 status = Get(name); 159 if (status < B_OK) 160 return status; 161 162 attr_cookie* cookie = new(std::nothrow) attr_cookie; 163 if (cookie == NULL) 164 RETURN_ERROR(B_NO_MEMORY); 165 166 // initialize the cookie 167 strlcpy(cookie->name, fName, B_ATTR_NAME_LENGTH); 168 cookie->open_mode = openMode; 169 cookie->create = false; 170 171 // Should we truncate the attribute? 172 if ((openMode & O_TRUNC) != 0) 173 _Truncate(); 174 175 *_cookie = cookie; 176 return B_OK; 177 } 178 179 180 status_t 181 Attribute::Stat(struct stat& stat) 182 { 183 if (fSmall == NULL && fAttribute == NULL) 184 return B_NO_INIT; 185 186 if (fSmall != NULL) { 187 fill_stat_buffer(fInode, stat); 188 189 // overwrite some data to suit our needs 190 stat.st_type = fSmall->Type(); 191 stat.st_size = fSmall->DataSize(); 192 stat.st_mtim = stat.st_ctim; 193 // attribute changes cause status_change updates 194 } 195 196 if (fAttribute != NULL) 197 fill_stat_buffer(fAttribute, stat); 198 199 return B_OK; 200 } 201 202 203 status_t 204 Attribute::Read(attr_cookie* cookie, off_t pos, uint8* buffer, size_t* _length) 205 { 206 if (fSmall == NULL && fAttribute == NULL) 207 return B_NO_INIT; 208 209 // TODO: move small_data logic from Inode::ReadAttribute() over to here! 210 return fInode->ReadAttribute(cookie->name, 0, pos, buffer, _length); 211 } 212 213 214 status_t 215 Attribute::Write(Transaction& transaction, attr_cookie* cookie, off_t pos, 216 const uint8* buffer, size_t* _length, bool* _created) 217 { 218 if (!cookie->create && fSmall == NULL && fAttribute == NULL) 219 return B_NO_INIT; 220 221 return fInode->WriteAttribute(transaction, cookie->name, cookie->type, 222 pos, buffer, _length, _created); 223 } 224 225 226 status_t 227 Attribute::_Truncate() 228 { 229 if (fSmall != NULL) { 230 // TODO: as long as Inode::_AddSmallData() works like it does, 231 // we've got nothing to do here 232 return B_OK; 233 } 234 235 if (fAttribute != NULL) { 236 Transaction transaction(fAttribute->GetVolume(), 237 fAttribute->BlockNumber()); 238 fAttribute->WriteLockInTransaction(transaction); 239 240 status_t status = fAttribute->SetFileSize(transaction, 0); 241 if (status >= B_OK) 242 status = fAttribute->WriteBack(transaction); 243 244 if (status < B_OK) 245 return status; 246 247 transaction.Done(); 248 } 249 250 return B_OK; 251 } 252 253