1 /* 2 * Copyright 2011, Jérôme Duval, korli@users.berlios.de. 3 * Copyright 2008, Axel Dörfler, axeld@pinc-software.de. 4 * This file may be used under the terms of the MIT License. 5 */ 6 7 8 #include "Inode.h" 9 10 #include <real_time_clock.h> 11 #include <string.h> 12 #include <stdlib.h> 13 14 #include "CachedBlock.h" 15 #include "DataStream.h" 16 #include "Utility.h" 17 18 19 #undef ASSERT 20 //#define TRACE_EXFAT 21 #ifdef TRACE_EXFAT 22 # define TRACE(x...) dprintf("\33[34mexfat:\33[0m " x) 23 # define ASSERT(x) { if (!(x)) kernel_debugger("exfat: assert failed: " #x "\n"); } 24 #else 25 # define TRACE(x...) ; 26 # define ASSERT(x) ; 27 #endif 28 #define ERROR(x...) dprintf("\33[34mexfat:\33[0m " x) 29 30 31 Inode::Inode(Volume* volume, cluster_t cluster, uint32 offset) 32 : 33 fVolume(volume), 34 fID(volume->GetIno(cluster, offset, 0)), 35 fCluster(cluster), 36 fOffset(offset), 37 fCache(NULL), 38 fMap(NULL) 39 { 40 TRACE("Inode::Inode(%ld, %d) inode %" B_PRIdINO "\n", Cluster(), Offset(), 41 ID()); 42 _Init(); 43 44 if (ID() == 1) { 45 fFileEntry.file.SetAttribs(EXFAT_ENTRY_ATTRIB_SUBDIR); 46 fFileEntry.file_info.SetStartCluster(Cluster()); 47 fFileInfoEntry.file_info.SetFlag(0); 48 } else { 49 fInitStatus = UpdateNodeFromDisk(); 50 if (fInitStatus == B_OK && !IsDirectory() && !IsSymLink()) { 51 fCache = file_cache_create(fVolume->ID(), ID(), Size()); 52 fMap = file_map_create(fVolume->ID(), ID(), Size()); 53 } 54 } 55 TRACE("Inode::Inode(%" B_PRIdINO ") end\n", ID()); 56 } 57 58 59 Inode::Inode(Volume* volume, ino_t ino) 60 : 61 fVolume(volume), 62 fID(ino), 63 fCluster(0), 64 fOffset(0), 65 fCache(NULL), 66 fMap(NULL), 67 fInitStatus(B_NO_INIT) 68 { 69 struct node_key *key = volume->GetNode(ino, fParent); 70 if (key != NULL) { 71 fCluster = key->cluster; 72 fOffset = key->offset; 73 fInitStatus = B_OK; 74 } 75 TRACE("Inode::Inode(%" B_PRIdINO ") cluster %ld\n", ID(), Cluster()); 76 _Init(); 77 78 if (fInitStatus == B_OK && ID() != 1) { 79 fInitStatus = UpdateNodeFromDisk(); 80 if (!IsDirectory() && !IsSymLink()) { 81 fCache = file_cache_create(fVolume->ID(), ID(), Size()); 82 fMap = file_map_create(fVolume->ID(), ID(), Size()); 83 } 84 } else if (fInitStatus == B_OK && ID() == 1) { 85 fFileEntry.file.SetAttribs(EXFAT_ENTRY_ATTRIB_SUBDIR); 86 fFileInfoEntry.file_info.SetStartCluster(Cluster()); 87 fFileInfoEntry.file_info.SetFlag(0); 88 } 89 } 90 91 92 Inode::Inode(Volume* volume) 93 : 94 fVolume(volume), 95 fID(0), 96 fCache(NULL), 97 fMap(NULL), 98 fInitStatus(B_NO_INIT) 99 { 100 _Init(); 101 } 102 103 104 Inode::~Inode() 105 { 106 TRACE("Inode destructor\n"); 107 file_cache_delete(FileCache()); 108 file_map_delete(Map()); 109 TRACE("Inode destructor: Done\n"); 110 } 111 112 113 status_t 114 Inode::InitCheck() 115 { 116 return fInitStatus; 117 } 118 119 120 status_t 121 Inode::UpdateNodeFromDisk() 122 { 123 DirectoryIterator iterator(this); 124 iterator.LookupEntry(this); 125 return B_OK; 126 } 127 128 129 cluster_t 130 Inode::NextCluster(cluster_t cluster) const 131 { 132 if (!IsContiguous() || IsDirectory()) 133 return GetVolume()->NextCluster(cluster); 134 return cluster + 1; 135 } 136 137 138 mode_t 139 Inode::Mode() const 140 { 141 mode_t mode = S_IRUSR | S_IRGRP | S_IROTH; 142 if (!fVolume->IsReadOnly()) 143 mode |= S_IWUSR | S_IWGRP | S_IWOTH; 144 if (fFileEntry.file.Attribs() & EXFAT_ENTRY_ATTRIB_SUBDIR) 145 mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH; 146 else 147 mode |= S_IFREG; 148 return mode; 149 } 150 151 152 status_t 153 Inode::CheckPermissions(int accessMode) const 154 { 155 // you never have write access to a read-only volume 156 if ((accessMode & W_OK) != 0 && fVolume->IsReadOnly()) 157 return B_READ_ONLY_DEVICE; 158 159 // get node permissions 160 mode_t mode = Mode(); 161 int userPermissions = (mode & S_IRWXU) >> 6; 162 int groupPermissions = (mode & S_IRWXG) >> 3; 163 int otherPermissions = mode & S_IRWXO; 164 165 // get the node permissions for this uid/gid 166 int permissions = 0; 167 uid_t uid = geteuid(); 168 gid_t gid = getegid(); 169 170 if (uid == 0) { 171 // user is root 172 // root has always read/write permission, but at least one of the 173 // X bits must be set for execute permission 174 permissions = userPermissions | groupPermissions | otherPermissions 175 | R_OK | W_OK; 176 } else if (uid == (uid_t)UserID()) { 177 // user is node owner 178 permissions = userPermissions; 179 } else if (gid == (gid_t)GroupID()) { 180 // user is in owning group 181 permissions = groupPermissions; 182 } else { 183 // user is one of the others 184 permissions = otherPermissions; 185 } 186 187 return (accessMode & ~permissions) == 0 ? B_OK : B_NOT_ALLOWED; 188 return B_OK; 189 } 190 191 192 status_t 193 Inode::FindBlock(off_t pos, off_t& physical, off_t *_length) 194 { 195 DataStream stream(fVolume, this, Size()); 196 return stream.FindBlock(pos, physical, _length); 197 } 198 199 200 status_t 201 Inode::ReadAt(off_t pos, uint8* buffer, size_t* _length) 202 { 203 size_t length = *_length; 204 205 // set/check boundaries for pos/length 206 if (pos < 0) { 207 ERROR("inode %" B_PRIdINO ": ReadAt failed(pos %lld, length %lu)\n", 208 ID(), pos, length); 209 return B_BAD_VALUE; 210 } 211 212 if (pos >= Size() || length == 0) { 213 TRACE("inode %" B_PRIdINO ": ReadAt 0 (pos %lld, length %lu)\n", 214 ID(), pos, length); 215 *_length = 0; 216 return B_NO_ERROR; 217 } 218 219 return file_cache_read(FileCache(), NULL, pos, buffer, _length); 220 } 221 222 223 bool 224 Inode::VisitFile(struct exfat_entry* entry) 225 { 226 fFileEntry = *entry; 227 return false; 228 } 229 230 231 bool 232 Inode::VisitFileInfo(struct exfat_entry* entry) 233 { 234 fFileInfoEntry = *entry; 235 return false; 236 } 237 238 239 void 240 Inode::_Init() 241 { 242 memset(&fFileEntry, 0, sizeof(fFileEntry)); 243 memset(&fFileInfoEntry, 0, sizeof(fFileInfoEntry)); 244 rw_lock_init(&fLock, "exfat inode"); 245 } 246 247 248 // If divisible by 4, but not divisible by 100, but divisible by 400, it's a leap year 249 // 1996 is leap, 1900 is not, 2000 is, 2100 is not 250 #define IS_LEAP_YEAR(y) ((((y) % 4) == 0) && (((y) % 100) || ((((y)) % 400) == 0))) 251 252 /* returns leap days since 1970 */ 253 static int leaps(int yr, int mon) 254 { 255 // yr is 1970-based, mon 0-based 256 int result = (yr+2)/4 - (yr + 70) / 100; 257 if((yr+70) >= 100) result++; // correct for 2000 258 if (IS_LEAP_YEAR(yr + 1970)) 259 if (mon < 2) result--; 260 return result; 261 } 262 263 static int daze[] = { 0,0,31,59,90,120,151,181,212,243,273,304,334,0,0,0 }; 264 265 void 266 Inode::_GetTimespec(uint16 date, uint16 time, struct timespec ×pec) const 267 { 268 static int32 tzoffset = -1; /* in minutes */ 269 if (tzoffset == -1) 270 tzoffset = get_timezone_offset() / 60; 271 272 time_t days = daze[(date>>5)&15] + ((date>>9)+10)*365 + leaps((date>>9)+10,((date>>5)&15)-1)+(date&31)-1; 273 274 timespec.tv_sec = ((days * 24 + (time >> 11)) * 60 + ((time>>5)&63) + tzoffset) * 60 + 2*(time&31); 275 timespec.tv_nsec = 0; 276 } 277 278 279