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
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(%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
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 %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
92Inode::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
104Inode::~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
113status_t
114Inode::InitCheck()
115{
116	return fInitStatus;
117}
118
119
120status_t
121Inode::UpdateNodeFromDisk()
122{
123	DirectoryIterator iterator(this);
124	iterator.LookupEntry(this);
125	return B_OK;
126}
127
128
129cluster_t
130Inode::NextCluster(cluster_t cluster) const
131{
132	if (!IsContiguous() || IsDirectory())
133		return GetVolume()->NextCluster(cluster);
134	return cluster + 1;
135}
136
137
138mode_t
139Inode::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
152status_t
153Inode::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
192status_t
193Inode::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
200status_t
201Inode::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
223bool
224Inode::VisitFile(struct exfat_entry* entry)
225{
226	fFileEntry = *entry;
227	return false;
228}
229
230
231bool
232Inode::VisitFileInfo(struct exfat_entry* entry)
233{
234	fFileInfoEntry = *entry;
235	return false;
236}
237
238
239void
240Inode::_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 */
253static 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
263static int daze[] = { 0,0,31,59,90,120,151,181,212,243,273,304,334,0,0,0 };
264
265void
266Inode::_GetTimespec(uint16 date, uint16 time, struct timespec &timespec) 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