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.
10 #include <real_time_clock.h>
14 #include "CachedBlock.h"
15 #include "DataStream.h"
22 # define TRACE(x...) dprintf("\33[34mexfat:\33[0m " x)
23 # define ASSERT(x) { if (!(x)) kernel_debugger("exfat: assert failed: " #x "\n"); }
25 # define TRACE(x...) ;
28 #define ERROR(x...) dprintf("\33[34mexfat:\33[0m " x)
31 Inode::Inode(Volume
* volume
, cluster_t cluster
, uint32 offset
)
34 fID(volume
->GetIno(cluster
, offset
, 0)),
40 TRACE("Inode::Inode(%" B_PRIu32
", %" B_PRIu32
") inode %" B_PRIdINO
"\n",
41 Cluster(), Offset(), ID());
45 fFileEntry
.file
.SetAttribs(EXFAT_ENTRY_ATTRIB_SUBDIR
);
46 fFileEntry
.file_info
.SetStartCluster(Cluster());
47 fFileInfoEntry
.file_info
.SetFlag(0);
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
)
67 fInitStatus(B_NO_INIT
)
69 struct node_key
*key
= volume
->GetNode(ino
, fParent
);
71 fCluster
= key
->cluster
;
72 fOffset
= key
->offset
;
75 TRACE("Inode::Inode(%" B_PRIdINO
") cluster %" B_PRIu32
"\n", ID(),
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
)
99 fInitStatus(B_NO_INIT
)
107 TRACE("Inode destructor\n");
108 file_cache_delete(FileCache());
109 file_map_delete(Map());
110 TRACE("Inode destructor: Done\n");
122 Inode::UpdateNodeFromDisk()
124 DirectoryIterator
iterator(this);
125 iterator
.LookupEntry(this);
131 Inode::NextCluster(cluster_t cluster
) const
133 if (!IsContiguous() || IsDirectory())
134 return GetVolume()->NextCluster(cluster
);
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
;
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(),
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
);
174 Inode::ReadAt(off_t pos
, uint8
* buffer
, size_t* _length
)
176 size_t length
= *_length
;
178 // set/check boundaries for pos/length
180 ERROR("inode %" B_PRIdINO
": ReadAt failed(pos %" B_PRIdOFF
", length %"
181 B_PRIuSIZE
")\n", ID(), pos
, length
);
185 if (pos
>= Size() || length
== 0) {
186 TRACE("inode %" B_PRIdINO
": ReadAt 0 (pos %" B_PRIdOFF
", length %"
187 B_PRIuSIZE
")\n", ID(), pos
, length
);
192 return file_cache_read(FileCache(), NULL
, pos
, buffer
, _length
);
197 Inode::VisitFile(struct exfat_entry
* entry
)
205 Inode::VisitFileInfo(struct exfat_entry
* entry
)
207 fFileInfoEntry
= *entry
;
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
--;
236 static int daze
[] = { 0,0,31,59,90,120,151,181,212,243,273,304,334,0,0,0 };
239 Inode::_GetTimespec(uint16 date
, uint16 time
, struct timespec
×pec
) const
241 static int32 tzoffset
= -1; /* in minutes */
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;