2 * Copyright 2003-2013, Axel Dörfler, axeld@pinc-software.de.
3 * This file may be used under the terms of the MIT License.
7 //! Inode stream access functions
11 #include "Directory.h"
26 CachedBlock(Volume
& volume
);
27 CachedBlock(Volume
& volume
, block_run run
);
30 uint8
* SetTo(block_run run
);
31 uint8
* SetTo(off_t offset
);
35 uint8
* Block() const { return fBlock
; }
36 off_t
BlockNumber() const { return fBlockNumber
; }
37 uint32
BlockSize() const { return fVolume
.BlockSize(); }
38 uint32
BlockShift() const
39 { return fVolume
.BlockShift(); }
48 CachedBlock::CachedBlock(Volume
& volume
)
57 CachedBlock::CachedBlock(Volume
&volume
, block_run run
)
67 CachedBlock::~CachedBlock()
81 CachedBlock::SetTo(off_t block
)
83 if (block
== fBlockNumber
)
86 fBlock
= (uint8
*)malloc(BlockSize());
92 if (read_pos(fVolume
.Device(), block
<< BlockShift(), fBlock
, BlockSize())
93 < (ssize_t
)BlockSize())
101 CachedBlock::SetTo(block_run run
)
103 return SetTo(fVolume
.ToBlock(run
));
110 Stream::Stream(Volume
& volume
, block_run run
)
114 if (read_pos(volume
.Device(), volume
.ToOffset(run
), this, sizeof(bfs_inode
))
115 != sizeof(bfs_inode
))
120 Stream::Stream(Volume
& volume
, off_t id
)
124 if (read_pos(volume
.Device(), volume
.ToOffset(id
), this, sizeof(bfs_inode
))
125 != sizeof(bfs_inode
))
138 return bfs_inode::InitCheck(&fVolume
);
143 Stream::GetNextSmallData(const small_data
** _smallData
) const
145 // TODO: Stream derives from bfs_inode and we read only sizeof(bfs_inode)
146 // bytes from disk, i.e. the small data region is not in memory.
147 panic("Stream::GetNextSmallData(): small data region is not loaded!");
149 const small_data
* smallData
= *_smallData
;
151 // begin from the start?
152 if (smallData
== NULL
)
153 smallData
= small_data_start
;
155 smallData
= smallData
->Next();
157 // is already last item?
158 if (smallData
->IsLast(this))
159 return B_ENTRY_NOT_FOUND
;
161 *_smallData
= smallData
;
168 Stream::GetName(char* name
, size_t size
) const
170 const small_data
* smallData
= NULL
;
171 while (GetNextSmallData(&smallData
) == B_OK
) {
172 if (*smallData
->Name() == FILE_NAME_NAME
173 && smallData
->NameSize() == FILE_NAME_NAME_LENGTH
) {
174 strlcpy(name
, (const char*)smallData
->Data(), size
);
183 Stream::ReadLink(char* buffer
, size_t bufferSize
)
185 // link in the stream
187 if (Flags() & INODE_LONG_SYMLINK
)
188 return ReadAt(0, (uint8
*)buffer
, &bufferSize
);
192 strlcpy(buffer
, short_symlink
, bufferSize
);
198 Stream::FindBlockRun(off_t pos
, block_run
& run
, off_t
& offset
)
200 // find matching block run
202 if (data
.MaxDirectRange() > 0 && pos
>= data
.MaxDirectRange()) {
203 if (data
.MaxDoubleIndirectRange() > 0
204 && pos
>= data
.MaxIndirectRange()) {
205 // access to double indirect blocks
207 CachedBlock
cached(fVolume
);
212 get_double_indirect_sizes(data
.double_indirect
.Length(),
213 cached
.BlockSize(), runsPerBlock
, directSize
, indirectSize
);
215 off_t start
= pos
- data
.MaxIndirectRange();
216 int32 index
= start
/ indirectSize
;
218 block_run
* indirect
= (block_run
*)cached
.SetTo(
219 fVolume
.ToBlock(data
.double_indirect
) + index
/ runsPerBlock
);
220 if (indirect
== NULL
)
223 //printf("\tstart = %Ld, indirectSize = %ld, directSize = %ld, index = %ld\n",start,indirectSize,directSize,index);
224 //printf("\tlook for indirect block at %ld,%d\n",indirect[index].allocation_group,indirect[index].start);
226 int32 current
= (start
% indirectSize
) / directSize
;
228 indirect
= (block_run
*)cached
.SetTo(fVolume
.ToBlock(indirect
[
229 index
% runsPerBlock
]) + current
/ runsPerBlock
);
230 if (indirect
== NULL
)
233 run
= indirect
[current
% runsPerBlock
];
234 offset
= data
.MaxIndirectRange() + (index
* indirectSize
)
235 + (current
* directSize
);
236 //printf("\tfCurrent = %ld, fRunFileOffset = %Ld, fRunBlockEnd = %Ld, fRun = %ld,%d\n",fCurrent,fRunFileOffset,fRunBlockEnd,fRun.allocation_group,fRun.start);
238 // access to indirect blocks
240 int32 runsPerBlock
= fVolume
.BlockSize() / sizeof(block_run
);
241 off_t runBlockEnd
= data
.MaxDirectRange();
243 CachedBlock
cached(fVolume
);
244 off_t block
= fVolume
.ToBlock(data
.indirect
);
246 for (int32 i
= 0; i
< data
.indirect
.Length(); i
++) {
247 block_run
* indirect
= (block_run
*)cached
.SetTo(block
+ i
);
248 if (indirect
== NULL
)
252 while (++current
< runsPerBlock
) {
253 if (indirect
[current
].IsZero())
257 += (uint32
)indirect
[current
].Length() << cached
.BlockShift();
258 if (runBlockEnd
> pos
) {
259 run
= indirect
[current
];
261 - ((uint32
)run
.Length() << cached
.BlockShift());
262 //printf("reading from indirect block: %ld,%d\n",fRun.allocation_group,fRun.start);
263 //printf("### indirect-run[%ld] = (%ld,%d,%d), offset = %Ld\n",fCurrent,fRun.allocation_group,fRun.start,fRun.length,fRunFileOffset);
264 return fVolume
.ValidateBlockRun(run
);
271 // access from direct blocks
273 off_t runBlockEnd
= 0LL;
276 while (++current
< NUM_DIRECT_BLOCKS
) {
277 if (data
.direct
[current
].IsZero())
280 runBlockEnd
+= (uint32
)data
.direct
[current
].Length() << fVolume
.BlockShift();
281 if (runBlockEnd
> pos
) {
282 run
= data
.direct
[current
];
283 offset
= runBlockEnd
- ((uint32
)run
.Length() << fVolume
.BlockShift());
284 //printf("### run[%ld] = (%ld,%d,%d), offset = %Ld\n",fCurrent,fRun.allocation_group,fRun.start,fRun.length,fRunFileOffset);
285 return fVolume
.ValidateBlockRun(run
);
288 //PRINT(("FindBlockRun() failed in direct range: size = %Ld, pos = %Ld\n",data.size,pos));
289 return B_ENTRY_NOT_FOUND
;
291 return fVolume
.ValidateBlockRun(run
);
296 Stream::ReadAt(off_t pos
, uint8
* buffer
, size_t* _length
)
298 // set/check boundaries for pos/length
302 if (pos
>= data
.Size()) {
307 size_t length
= *_length
;
309 if (pos
+ (off_t
)length
> data
.Size())
310 length
= data
.Size() - pos
;
314 if (FindBlockRun(pos
, run
, offset
) < B_OK
) {
319 uint32 bytesRead
= 0;
320 uint32 blockSize
= fVolume
.BlockSize();
321 uint32 blockShift
= fVolume
.BlockShift();
324 // the first block_run we read could not be aligned to the block_size boundary
325 // (read partial block at the beginning)
327 // pos % block_size == (pos - offset) % block_size, offset % block_size == 0
328 if (pos
% blockSize
!= 0) {
329 run
.start
= HOST_ENDIAN_TO_BFS_INT16(run
.Start()
330 + ((pos
- offset
) >> blockShift
));
331 run
.length
= HOST_ENDIAN_TO_BFS_INT16(run
.Length()
332 - ((pos
- offset
) >> blockShift
));
334 CachedBlock
cached(fVolume
, run
);
335 if ((block
= cached
.Block()) == NULL
) {
340 bytesRead
= blockSize
- (pos
% blockSize
);
341 if (length
< bytesRead
)
344 memcpy(buffer
, block
+ (pos
% blockSize
), bytesRead
);
349 *_length
= bytesRead
;
353 if (FindBlockRun(pos
, run
, offset
) < B_OK
) {
354 *_length
= bytesRead
;
359 // the first block_run is already filled in at this point
360 // read the following complete blocks using cached_read(),
361 // the last partial block is read using the generic Cache class
363 bool partial
= false;
366 // offset is the offset to the current pos in the block_run
367 run
.start
= HOST_ENDIAN_TO_BFS_INT16(run
.Start()
368 + ((pos
- offset
) >> blockShift
));
369 run
.length
= HOST_ENDIAN_TO_BFS_INT16(run
.Length()
370 - ((pos
- offset
) >> blockShift
));
372 if (uint32(run
.Length() << blockShift
) > length
) {
373 if (length
< blockSize
) {
374 CachedBlock
cached(fVolume
, run
);
375 if ((block
= cached
.Block()) == NULL
) {
376 *_length
= bytesRead
;
379 memcpy(buffer
+ bytesRead
, block
, length
);
383 run
.length
= HOST_ENDIAN_TO_BFS_INT16(length
>> blockShift
);
387 if (read_pos(fVolume
.Device(), fVolume
.ToOffset(run
), buffer
+ bytesRead
,
388 run
.Length() << fVolume
.BlockShift()) < B_OK
) {
389 *_length
= bytesRead
;
393 int32 bytes
= run
.Length() << blockShift
;
402 // if the last block was read only partially, point block_run
403 // to the remaining part
404 run
.start
= HOST_ENDIAN_TO_BFS_INT16(run
.Start() + run
.Length());
407 } else if (FindBlockRun(pos
, run
, offset
) < B_OK
) {
408 *_length
= bytesRead
;
413 *_length
= bytesRead
;
419 Stream::NodeFactory(Volume
& volume
, off_t id
)
421 Stream
stream(volume
, id
);
422 if (stream
.InitCheck() != B_OK
)
425 if (stream
.IsContainer())
426 return new(nothrow
) Directory(stream
);
428 if (stream
.IsSymlink())
429 return new(nothrow
) Link(stream
);
431 return new(nothrow
) File(stream
);
439 bfs_inode::InitCheck(Volume
* volume
) const
441 if ((Flags() & INODE_NOT_READY
) != 0) {
442 // the other fields may not yet contain valid values
446 if (Magic1() != INODE_MAGIC1
447 || !(Flags() & INODE_IN_USE
)
448 || inode_num
.Length() != 1
449 // matches inode size?
450 || (uint32
)InodeSize() != volume
->InodeSize()
451 // parent resides on disk?
452 || parent
.AllocationGroup() > int32(volume
->AllocationGroups())
453 || parent
.AllocationGroup() < 0
454 || parent
.Start() > (1L << volume
->AllocationGroupShift())
455 || parent
.Length() != 1
457 || attributes
.AllocationGroup() > int32(volume
->AllocationGroups())
458 || attributes
.AllocationGroup() < 0
459 || attributes
.Start() > (1L << volume
->AllocationGroupShift()))
462 // TODO: Add some tests to check the integrity of the other stuff here,
463 // especially for the data_stream!