1/* 2 * Copyright 2011, J��r��me Duval, korli@users.berlios.de. 3 * Copyright 2008-2014, 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 31Inode::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(%" B_PRIu32 ", %" B_PRIu32 ") inode %" B_PRIdINO "\n", 41 Cluster(), Offset(), 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 59Inode::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 %" B_PRIu32 "\n", ID(), 76 Cluster()); 77 _Init(); 78 79 if (fInitStatus == B_OK && ID() != 1) { 80 fInitStatus = UpdateNodeFromDisk(); 81 if (!IsDirectory() && !IsSymLink()) { 82 fCache = file_cache_create(fVolume->ID(), ID(), Size()); 83 fMap = file_map_create(fVolume->ID(), ID(), Size()); 84 } 85 } else if (fInitStatus == B_OK && ID() == 1) { 86 fFileEntry.file.SetAttribs(EXFAT_ENTRY_ATTRIB_SUBDIR); 87 fFileInfoEntry.file_info.SetStartCluster(Cluster()); 88 fFileInfoEntry.file_info.SetFlag(0); 89 } 90} 91 92 93Inode::Inode(Volume* volume) 94 : 95 fVolume(volume), 96 fID(0), 97 fCache(NULL), 98 fMap(NULL), 99 fInitStatus(B_NO_INIT) 100{ 101 _Init(); 102} 103 104 105Inode::~Inode() 106{ 107 TRACE("Inode destructor\n"); 108 file_cache_delete(FileCache()); 109 file_map_delete(Map()); 110 TRACE("Inode destructor: Done\n"); 111} 112 113 114status_t 115Inode::InitCheck() 116{ 117 return fInitStatus; 118} 119 120 121status_t 122Inode::UpdateNodeFromDisk() 123{ 124 DirectoryIterator iterator(this); 125 iterator.LookupEntry(this); 126 return B_OK; 127} 128 129 130cluster_t 131Inode::NextCluster(cluster_t cluster) const 132{ 133 if (!IsContiguous() || IsDirectory()) 134 return GetVolume()->NextCluster(cluster); 135 return cluster + 1; 136} 137 138 139mode_t 140Inode::Mode() const 141{ 142 mode_t mode = S_IRUSR | S_IRGRP | S_IROTH; 143 if (!fVolume->IsReadOnly()) 144 mode |= S_IWUSR | S_IWGRP | S_IWOTH; 145 if (fFileEntry.file.Attribs() & EXFAT_ENTRY_ATTRIB_SUBDIR) 146 mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH; 147 else 148 mode |= S_IFREG; 149 return mode; 150} 151 152 153status_t 154Inode::CheckPermissions(int accessMode) const 155{ 156 // you never have write access to a read-only volume 157 if ((accessMode & W_OK) != 0 && fVolume->IsReadOnly()) 158 return B_READ_ONLY_DEVICE; 159 160 return check_access_permissions(accessMode, Mode(), (gid_t)GroupID(), 161 (uid_t)UserID()); 162} 163 164 165status_t 166Inode::FindBlock(off_t pos, off_t& physical, off_t *_length) 167{ 168 DataStream stream(fVolume, this, Size()); 169 return stream.FindBlock(pos, physical, _length); 170} 171 172 173status_t 174Inode::ReadAt(off_t pos, uint8* buffer, size_t* _length) 175{ 176 return file_cache_read(FileCache(), NULL, pos, buffer, _length); 177} 178 179 180bool 181Inode::VisitFile(struct exfat_entry* entry) 182{ 183 fFileEntry = *entry; 184 return false; 185} 186 187 188bool 189Inode::VisitFileInfo(struct exfat_entry* entry) 190{ 191 fFileInfoEntry = *entry; 192 return false; 193} 194 195 196void 197Inode::_Init() 198{ 199 memset(&fFileEntry, 0, sizeof(fFileEntry)); 200 memset(&fFileInfoEntry, 0, sizeof(fFileInfoEntry)); 201 rw_lock_init(&fLock, "exfat inode"); 202} 203 204 205// If divisible by 4, but not divisible by 100, but divisible by 400, it's a leap year 206// 1996 is leap, 1900 is not, 2000 is, 2100 is not 207#define IS_LEAP_YEAR(y) ((((y) % 4) == 0) && (((y) % 100) || ((((y)) % 400) == 0))) 208 209/* returns leap days since 1970 */ 210static int leaps(int yr, int mon) 211{ 212 // yr is 1970-based, mon 0-based 213 int result = (yr+2)/4 - (yr + 70) / 100; 214 if((yr+70) >= 100) result++; // correct for 2000 215 if (IS_LEAP_YEAR(yr + 1970)) 216 if (mon < 2) result--; 217 return result; 218} 219 220static int daze[] = { 0,0,31,59,90,120,151,181,212,243,273,304,334,0,0,0 }; 221 222void 223Inode::_GetTimespec(uint16 date, uint16 time, struct timespec ×pec) const 224{ 225 static int32 tzoffset = -1; /* in minutes */ 226 if (tzoffset == -1) 227 tzoffset = get_timezone_offset() / 60; 228 229 time_t days = daze[(date >> 5) & 15] + ((date >> 9) + 10) * 365 230 + leaps((date >> 9) + 10, ((date >> 5) & 15) - 1) + (date & 31) -1; 231 232 timespec.tv_sec = ((days * 24 + (time >> 11)) * 60 + ((time >> 5) & 63) 233 - tzoffset) * 60 + 2 * (time & 31); 234 timespec.tv_nsec = 0; 235} 236 237 238