1/* 2 * Copyright 2004-2020, 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 18extern void fill_stat_buffer(Inode* inode, struct stat& stat); 19 20 21Attribute::Attribute(Inode* inode) 22 : 23 fNodeGetter(inode->GetVolume()), 24 fInode(inode), 25 fSmall(NULL), 26 fAttribute(NULL), 27 fName(NULL) 28{ 29} 30 31 32Attribute::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 44Attribute::~Attribute() 45{ 46 Put(); 47} 48 49 50status_t 51Attribute::InitCheck() 52{ 53 return (fSmall != NULL || fAttribute != NULL) ? B_OK : B_NO_INIT; 54} 55 56 57status_t 58Attribute::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 77status_t 78Attribute::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 status_t status = fNodeGetter.SetTo(fInode); 87 if (status != B_OK) 88 return status; 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 103void 104Attribute::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 119status_t 120Attribute::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 151status_t 152Attribute::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 180status_t 181Attribute::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 203status_t 204Attribute::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 214status_t 215Attribute::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 226status_t 227Attribute::_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