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 17 extern void fill_stat_buffer(Inode* inode, struct stat& stat); 18 19 20 Attribute::Attribute(Inode* inode) 21 : 22 fNodeGetter(inode->GetVolume()), 23 fInode(inode), 24 fSmall(NULL), 25 fAttribute(NULL), 26 fName(NULL) 27 { 28 } 29 30 31 Attribute::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 43 Attribute::~Attribute() 44 { 45 Put(); 46 } 47 48 49 status_t 50 Attribute::InitCheck() 51 { 52 return (fSmall != NULL || fAttribute != NULL) ? B_OK : B_NO_INIT; 53 } 54 55 56 status_t 57 Attribute::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 76 status_t 77 Attribute::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 99 void 100 Attribute::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 115 status_t 116 Attribute::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 145 status_t 146 Attribute::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 174 status_t 175 Attribute::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 197 status_t 198 Attribute::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 208 status_t 209 Attribute::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 220 status_t 221 Attribute::_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