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 } 187 188 if (fAttribute != NULL) 189 fill_stat_buffer(fAttribute, stat); 190 191 return B_OK; 192 } 193 194 195 status_t 196 Attribute::Read(attr_cookie* cookie, off_t pos, uint8* buffer, size_t* _length) 197 { 198 if (fSmall == NULL && fAttribute == NULL) 199 return B_NO_INIT; 200 201 // TODO: move small_data logic from Inode::ReadAttribute() over to here! 202 return fInode->ReadAttribute(cookie->name, 0, pos, buffer, _length); 203 } 204 205 206 status_t 207 Attribute::Write(Transaction& transaction, attr_cookie* cookie, off_t pos, 208 const uint8* buffer, size_t* _length) 209 { 210 if (!cookie->create && fSmall == NULL && fAttribute == NULL) 211 return B_NO_INIT; 212 213 return fInode->WriteAttribute(transaction, cookie->name, cookie->type, 214 pos, buffer, _length); 215 } 216 217 218 status_t 219 Attribute::_Truncate() 220 { 221 if (fSmall != NULL) { 222 // TODO: as long as Inode::_AddSmallData() works like it does, 223 // we've got nothing to do here 224 return B_OK; 225 } 226 227 if (fAttribute != NULL) { 228 Transaction transaction(fAttribute->GetVolume(), 229 fAttribute->BlockNumber()); 230 fAttribute->WriteLockInTransaction(transaction); 231 232 status_t status = fAttribute->SetFileSize(transaction, 0); 233 if (status >= B_OK) 234 status = fAttribute->WriteBack(transaction); 235 236 if (status < B_OK) 237 return status; 238 239 transaction.Done(); 240 } 241 242 return B_OK; 243 } 244 245