xref: /haiku/src/add-ons/kernel/file_systems/exfat/Inode.cpp (revision 3f5d1e78252bb593cf5b430f37c3d18d779afab1)
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(%" 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 
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 %" 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 
93 Inode::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 
105 Inode::~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 
114 status_t
115 Inode::InitCheck()
116 {
117 	return fInitStatus;
118 }
119 
120 
121 status_t
122 Inode::UpdateNodeFromDisk()
123 {
124 	DirectoryIterator iterator(this);
125 	iterator.LookupEntry(this);
126 	return B_OK;
127 }
128 
129 
130 cluster_t
131 Inode::NextCluster(cluster_t cluster) const
132 {
133 	if (!IsContiguous() || IsDirectory())
134 		return GetVolume()->NextCluster(cluster);
135 	return cluster + 1;
136 }
137 
138 
139 mode_t
140 Inode::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 
153 status_t
154 Inode::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 	// get node permissions
161 	mode_t mode = Mode();
162 	int userPermissions = (mode & S_IRWXU) >> 6;
163 	int groupPermissions = (mode & S_IRWXG) >> 3;
164 	int otherPermissions = mode & S_IRWXO;
165 
166 	// get the node permissions for this uid/gid
167 	int permissions = 0;
168 	uid_t uid = geteuid();
169 	gid_t gid = getegid();
170 
171 	if (uid == 0) {
172 		// user is root
173 		// root has always read/write permission, but at least one of the
174 		// X bits must be set for execute permission
175 		permissions = userPermissions | groupPermissions | otherPermissions
176 			| R_OK | W_OK;
177 	} else if (uid == (uid_t)UserID()) {
178 		// user is node owner
179 		permissions = userPermissions;
180 	} else if (gid == (gid_t)GroupID()) {
181 		// user is in owning group
182 		permissions = groupPermissions;
183 	} else {
184 		// user is one of the others
185 		permissions = otherPermissions;
186 	}
187 
188 	return (accessMode & ~permissions) == 0 ? B_OK : B_NOT_ALLOWED;
189 	return B_OK;
190 }
191 
192 
193 status_t
194 Inode::FindBlock(off_t pos, off_t& physical, off_t *_length)
195 {
196 	DataStream stream(fVolume, this, Size());
197 	return stream.FindBlock(pos, physical, _length);
198 }
199 
200 
201 status_t
202 Inode::ReadAt(off_t pos, uint8* buffer, size_t* _length)
203 {
204 	size_t length = *_length;
205 
206 	// set/check boundaries for pos/length
207 	if (pos < 0) {
208 		ERROR("inode %" B_PRIdINO ": ReadAt failed(pos %" B_PRIdOFF", length %"
209 			B_PRIuSIZE ")\n", ID(), pos, length);
210 		return B_BAD_VALUE;
211 	}
212 
213 	if (pos >= Size() || length == 0) {
214 		TRACE("inode %" B_PRIdINO ": ReadAt 0 (pos %" B_PRIdOFF", length %"
215 			B_PRIuSIZE ")\n", ID(), pos, length);
216 		*_length = 0;
217 		return B_NO_ERROR;
218 	}
219 
220 	return file_cache_read(FileCache(), NULL, pos, buffer, _length);
221 }
222 
223 
224 bool
225 Inode::VisitFile(struct exfat_entry* entry)
226 {
227 	fFileEntry = *entry;
228 	return false;
229 }
230 
231 
232 bool
233 Inode::VisitFileInfo(struct exfat_entry* entry)
234 {
235 	fFileInfoEntry = *entry;
236 	return false;
237 }
238 
239 
240 void
241 Inode::_Init()
242 {
243 	memset(&fFileEntry, 0, sizeof(fFileEntry));
244 	memset(&fFileInfoEntry, 0, sizeof(fFileInfoEntry));
245 	rw_lock_init(&fLock, "exfat inode");
246 }
247 
248 
249 // If divisible by 4, but not divisible by 100, but divisible by 400, it's a leap year
250 // 1996 is leap, 1900 is not, 2000 is, 2100 is not
251 #define IS_LEAP_YEAR(y) ((((y) % 4) == 0) && (((y) % 100) || ((((y)) % 400) == 0)))
252 
253 /* returns leap days since 1970 */
254 static int leaps(int yr, int mon)
255 {
256 	// yr is 1970-based, mon 0-based
257 	int result = (yr+2)/4 - (yr + 70) / 100;
258 	if((yr+70) >= 100) result++; // correct for 2000
259 	if (IS_LEAP_YEAR(yr + 1970))
260 		if (mon < 2) result--;
261 	return result;
262 }
263 
264 static int daze[] = { 0,0,31,59,90,120,151,181,212,243,273,304,334,0,0,0 };
265 
266 void
267 Inode::_GetTimespec(uint16 date, uint16 time, struct timespec &timespec) const
268 {
269 	static int32 tzoffset = -1; /* in minutes */
270 	if (tzoffset == -1)
271 		tzoffset = get_timezone_offset() / 60;
272 
273 	time_t days = daze[(date>>5)&15] + ((date>>9)+10)*365 + leaps((date>>9)+10,((date>>5)&15)-1)+(date&31)-1;
274 
275 	timespec.tv_sec = ((days * 24 + (time >> 11)) * 60 + ((time>>5)&63) + tzoffset) * 60 + 2*(time&31);
276 	timespec.tv_nsec = 0;
277 }
278 
279 
280