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