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 &timespec) 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