btrfs: [] on the end of a struct field is a variable length array.
[haiku.git] / src / add-ons / kernel / file_systems / exfat / Inode.cpp
blobf7c1d405afa3f9d278471b94d2ae2fda36ed1222
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 */
8 #include "Inode.h"
10 #include <real_time_clock.h>
11 #include <string.h>
12 #include <stdlib.h>
14 #include "CachedBlock.h"
15 #include "DataStream.h"
16 #include "Utility.h"
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)
31 Inode::Inode(Volume* volume, cluster_t cluster, uint32 offset)
33 fVolume(volume),
34 fID(volume->GetIno(cluster, offset, 0)),
35 fCluster(cluster),
36 fOffset(offset),
37 fCache(NULL),
38 fMap(NULL)
40 TRACE("Inode::Inode(%" B_PRIu32 ", %" B_PRIu32 ") inode %" B_PRIdINO "\n",
41 Cluster(), Offset(), ID());
42 _Init();
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());
55 TRACE("Inode::Inode(%" B_PRIdINO ") end\n", ID());
59 Inode::Inode(Volume* volume, ino_t ino)
61 fVolume(volume),
62 fID(ino),
63 fCluster(0),
64 fOffset(0),
65 fCache(NULL),
66 fMap(NULL),
67 fInitStatus(B_NO_INIT)
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;
75 TRACE("Inode::Inode(%" B_PRIdINO ") cluster %" B_PRIu32 "\n", ID(),
76 Cluster());
77 _Init();
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());
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);
93 Inode::Inode(Volume* volume)
95 fVolume(volume),
96 fID(0),
97 fCache(NULL),
98 fMap(NULL),
99 fInitStatus(B_NO_INIT)
101 _Init();
105 Inode::~Inode()
107 TRACE("Inode destructor\n");
108 file_cache_delete(FileCache());
109 file_map_delete(Map());
110 TRACE("Inode destructor: Done\n");
114 status_t
115 Inode::InitCheck()
117 return fInitStatus;
121 status_t
122 Inode::UpdateNodeFromDisk()
124 DirectoryIterator iterator(this);
125 iterator.LookupEntry(this);
126 return B_OK;
130 cluster_t
131 Inode::NextCluster(cluster_t cluster) const
133 if (!IsContiguous() || IsDirectory())
134 return GetVolume()->NextCluster(cluster);
135 return cluster + 1;
139 mode_t
140 Inode::Mode() const
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;
153 status_t
154 Inode::CheckPermissions(int accessMode) const
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;
160 return check_access_permissions(accessMode, Mode(), (gid_t)GroupID(),
161 (uid_t)UserID());
165 status_t
166 Inode::FindBlock(off_t pos, off_t& physical, off_t *_length)
168 DataStream stream(fVolume, this, Size());
169 return stream.FindBlock(pos, physical, _length);
173 status_t
174 Inode::ReadAt(off_t pos, uint8* buffer, size_t* _length)
176 size_t length = *_length;
178 // set/check boundaries for pos/length
179 if (pos < 0) {
180 ERROR("inode %" B_PRIdINO ": ReadAt failed(pos %" B_PRIdOFF", length %"
181 B_PRIuSIZE ")\n", ID(), pos, length);
182 return B_BAD_VALUE;
185 if (pos >= Size() || length == 0) {
186 TRACE("inode %" B_PRIdINO ": ReadAt 0 (pos %" B_PRIdOFF", length %"
187 B_PRIuSIZE ")\n", ID(), pos, length);
188 *_length = 0;
189 return B_NO_ERROR;
192 return file_cache_read(FileCache(), NULL, pos, buffer, _length);
196 bool
197 Inode::VisitFile(struct exfat_entry* entry)
199 fFileEntry = *entry;
200 return false;
204 bool
205 Inode::VisitFileInfo(struct exfat_entry* entry)
207 fFileInfoEntry = *entry;
208 return false;
212 void
213 Inode::_Init()
215 memset(&fFileEntry, 0, sizeof(fFileEntry));
216 memset(&fFileInfoEntry, 0, sizeof(fFileInfoEntry));
217 rw_lock_init(&fLock, "exfat inode");
221 // If divisible by 4, but not divisible by 100, but divisible by 400, it's a leap year
222 // 1996 is leap, 1900 is not, 2000 is, 2100 is not
223 #define IS_LEAP_YEAR(y) ((((y) % 4) == 0) && (((y) % 100) || ((((y)) % 400) == 0)))
225 /* returns leap days since 1970 */
226 static int leaps(int yr, int mon)
228 // yr is 1970-based, mon 0-based
229 int result = (yr+2)/4 - (yr + 70) / 100;
230 if((yr+70) >= 100) result++; // correct for 2000
231 if (IS_LEAP_YEAR(yr + 1970))
232 if (mon < 2) result--;
233 return result;
236 static int daze[] = { 0,0,31,59,90,120,151,181,212,243,273,304,334,0,0,0 };
238 void
239 Inode::_GetTimespec(uint16 date, uint16 time, struct timespec &timespec) const
241 static int32 tzoffset = -1; /* in minutes */
242 if (tzoffset == -1)
243 tzoffset = get_timezone_offset() / 60;
245 time_t days = daze[(date >> 5) & 15] + ((date >> 9) + 10) * 365
246 + leaps((date >> 9) + 10, ((date >> 5) & 15) - 1) + (date & 31) -1;
248 timespec.tv_sec = ((days * 24 + (time >> 11)) * 60 + ((time >> 5) & 63)
249 - tzoffset) * 60 + 2 * (time & 31);
250 timespec.tv_nsec = 0;