1/* Stream - inode stream access functions
2**
3** Initial version by Axel D��rfler, axeld@pinc-software.de
4** This file may be used under the terms of the OpenBeOS License.
5*/
6
7
8#include "Inode.h"
9
10
11// The classes in the namespace "Access" provide different type of access
12// to the inode's data stream.
13// Uncached accesses the underlaying device directly, Cached uses the
14// standard cache, while Logged directs write accesses through the log.
15//
16// The classes interface is similar to the one of the CachedBlock class,
17// but adds two other (static) functions for reading/writing several
18// blocks at once.
19// We don't use a real pure virtual interface as the class base, but we
20// provide the same mechanism using templates.
21
22namespace Access {
23
24class Uncached {
25	public:
26		Uncached(Volume *volume);
27		Uncached(Volume *volume, off_t block, bool empty = false);
28		Uncached(Volume *volume, block_run run, bool empty = false);
29		~Uncached();
30
31		void Unset();
32		uint8 *SetTo(off_t block, bool empty = false);
33		uint8 *SetTo(block_run run, bool empty = false);
34		status_t WriteBack(Transaction *transaction);
35
36		uint8 *Block() const { return fBlock; }
37		off_t BlockNumber() const { return fBlockNumber; }
38		uint32 BlockSize() const { return fVolume->BlockSize(); }
39		uint32 BlockShift() const { return fVolume->BlockShift(); }
40
41		static status_t Read(Volume *volume, block_run run, uint8 *buffer);
42		static status_t Write(Transaction *transaction, Volume *volume, block_run run, const uint8 *buffer);
43
44	private:
45		Volume	*fVolume;
46		off_t	fBlockNumber;
47		uint8	*fBlock;
48};
49
50class Cached : public CachedBlock {
51	public:
52		Cached(Volume *volume);
53		Cached(Volume *volume, off_t block, bool empty = false);
54		Cached(Volume *volume, block_run run, bool empty = false);
55
56		status_t WriteBack(Transaction *transaction);
57		static status_t Read(Volume *volume, block_run run, uint8 *buffer);
58		static status_t Write(Transaction *transaction, Volume *volume, block_run run, const uint8 *buffer);
59};
60
61class Logged : public CachedBlock {
62	public:
63		Logged(Volume *volume);
64		Logged(Volume *volume,off_t block, bool empty = false);
65		Logged(Volume *volume, block_run run, bool empty = false);
66
67		static status_t Read(Volume *volume, block_run run, uint8 *buffer);
68		static status_t Write(Transaction *transaction, Volume *volume, block_run run, const uint8 *buffer);
69};
70
71
72Uncached::Uncached(Volume *volume)
73	:
74	fVolume(volume),
75	fBlock(NULL)
76{
77}
78
79
80Uncached::Uncached(Volume *volume,off_t block, bool empty)
81	:
82	fVolume(volume),
83	fBlock(NULL)
84{
85	SetTo(block,empty);
86}
87
88
89Uncached::Uncached(Volume *volume,block_run run,bool empty)
90	:
91	fVolume(volume),
92	fBlock(NULL)
93{
94	SetTo(volume->ToBlock(run),empty);
95}
96
97
98Uncached::~Uncached()
99{
100	Unset();
101}
102
103
104void
105Uncached::Unset()
106{
107	if (fBlock != NULL)
108		fVolume->Pool().PutBuffer((void *)fBlock);
109}
110
111
112uint8 *
113Uncached::SetTo(off_t block, bool empty)
114{
115	Unset();
116	fBlockNumber = block;
117	if (fVolume->Pool().GetBuffer((void **)&fBlock) < B_OK)
118		return NULL;
119
120	if (empty)
121		memset(fBlock, 0, BlockSize());
122	else
123		read_pos(fVolume->Device(), fBlockNumber << BlockShift(), fBlock, BlockSize());
124
125	return fBlock;
126}
127
128
129uint8 *
130Uncached::SetTo(block_run run, bool empty)
131{
132	return SetTo(fVolume->ToBlock(run), empty);
133}
134
135
136status_t
137Uncached::WriteBack(Transaction *transaction)
138{
139	if (fBlock == NULL)
140		RETURN_ERROR(B_BAD_VALUE);
141
142	return write_pos(fVolume->Device(), fBlockNumber << BlockShift(), fBlock, BlockSize());
143}
144
145
146status_t
147Uncached::Read(Volume *volume, block_run run, uint8 *buffer)
148{
149	return read_pos(volume->Device(), volume->ToBlock(run) << volume->BlockShift(), buffer, run.Length() << volume->BlockShift());
150}
151
152
153status_t
154Uncached::Write(Transaction *transaction, Volume *volume, block_run run, const uint8 *buffer)
155{
156	return write_pos(volume->Device(), volume->ToBlock(run) << volume->BlockShift(), buffer, run.Length() << volume->BlockShift());
157}
158
159
160//	#pragma mark -
161
162
163Cached::Cached(Volume *volume)
164	: CachedBlock(volume)
165{
166}
167
168
169Cached::Cached(Volume *volume,off_t block,bool empty)
170	: CachedBlock(volume, block, empty)
171{
172}
173
174
175Cached::Cached(Volume *volume,block_run run,bool empty)
176	: CachedBlock(volume, run, empty)
177{
178}
179
180
181status_t
182Cached::WriteBack(Transaction *transaction)
183{
184	if (transaction == NULL || fBlock == NULL)
185		RETURN_ERROR(B_BAD_VALUE);
186
187	return fVolume->WriteBlocks(fBlockNumber, fBlock, 1);
188}
189
190
191status_t
192Cached::Read(Volume *volume, block_run run, uint8 *buffer)
193{
194	return cached_read(volume->Device(), volume->ToBlock(run), buffer, run.Length(), volume->BlockSize());
195}
196
197
198status_t
199Cached::Write(Transaction *transaction, Volume *volume, block_run run, const uint8 *buffer)
200{
201	return volume->WriteBlocks(volume->ToBlock(run), buffer, run.Length());
202}
203
204
205//	#pragma mark -
206
207
208Logged::Logged(Volume *volume)
209	: CachedBlock(volume)
210{
211}
212
213
214Logged::Logged(Volume *volume, off_t block, bool empty)
215	: CachedBlock(volume, block, empty)
216{
217}
218
219
220Logged::Logged(Volume *volume, block_run run, bool empty)
221	: CachedBlock(volume, run, empty)
222{
223}
224
225
226status_t
227Logged::Read(Volume *volume, block_run run, uint8 *buffer)
228{
229	return cached_read(volume->Device(), volume->ToBlock(run), buffer, run.Length(), volume->BlockSize());
230}
231
232
233status_t
234Logged::Write(Transaction *transaction, Volume *volume, block_run run, const uint8 *buffer)
235{
236	return transaction->WriteBlocks(volume->ToBlock(run), buffer, run.Length());
237}
238
239};	// namespace Access
240
241
242//	#pragma mark -
243
244
245// The Stream template class allows to have only one straight-forward
246// implementation of the FindBlockRun(), ReadAt(), and WriteAt() methods.
247// They will access the disk through the given cache class only, which
248// means either uncached, cached, or logged (see above).
249
250template<class Cache>
251class Stream : public Inode {
252	private:
253		// The constructor only exists to make the compiler happy - it
254		// is never called in the code itself
255		Stream() : Inode(NULL, -1) {}
256
257	public:
258		status_t FindBlockRun(off_t pos, block_run &run, off_t &offset);
259		status_t ReadAt(off_t pos, uint8 *buffer, size_t *length);
260		status_t WriteAt(Transaction *transaction, off_t pos, const uint8 *buffer, size_t *length);
261};
262
263
264/** see Inode::FindBlockRun() for the documentation of this method */
265
266template<class Cache>
267status_t
268Stream<Cache>::FindBlockRun(off_t pos, block_run &run, off_t &offset)
269{
270	data_stream *data = &Node()->data;
271
272	// find matching block run
273
274	if (data->MaxDirectRange() > 0 && pos >= data->MaxDirectRange()) {
275		if (data->MaxDoubleIndirectRange() > 0 && pos >= data->MaxIndirectRange()) {
276			// access to double indirect blocks
277
278			Cache cached(fVolume);
279
280			off_t start = pos - data->MaxIndirectRange();
281			int32 indirectSize = (1L << (INDIRECT_BLOCKS_SHIFT + cached.BlockShift()))
282				* (fVolume->BlockSize() / sizeof(block_run));
283			int32 directSize = NUM_ARRAY_BLOCKS << cached.BlockShift();
284			int32 index = start / indirectSize;
285			int32 runsPerBlock = cached.BlockSize() / sizeof(block_run);
286
287			block_run *indirect = (block_run *)cached.SetTo(
288					fVolume->ToBlock(data->double_indirect) + index / runsPerBlock);
289			if (indirect == NULL)
290				RETURN_ERROR(B_ERROR);
291
292			//printf("\tstart = %Ld, indirectSize = %ld, directSize = %ld, index = %ld\n",start,indirectSize,directSize,index);
293			//printf("\tlook for indirect block at %ld,%d\n",indirect[index].allocation_group,indirect[index].start);
294
295			int32 current = (start % indirectSize) / directSize;
296
297			indirect = (block_run *)cached.SetTo(
298					fVolume->ToBlock(indirect[index % runsPerBlock]) + current / runsPerBlock);
299			if (indirect == NULL)
300				RETURN_ERROR(B_ERROR);
301
302			run = indirect[current % runsPerBlock];
303			offset = data->MaxIndirectRange() + (index * indirectSize) + (current * directSize);
304			//printf("\tfCurrent = %ld, fRunFileOffset = %Ld, fRunBlockEnd = %Ld, fRun = %ld,%d\n",fCurrent,fRunFileOffset,fRunBlockEnd,fRun.allocation_group,fRun.start);
305		} else {
306			// access to indirect blocks
307
308			int32 runsPerBlock = fVolume->BlockSize() / sizeof(block_run);
309			off_t runBlockEnd = data->MaxDirectRange();
310
311			Cache cached(fVolume);
312			off_t block = fVolume->ToBlock(data->indirect);
313
314			for (int32 i = 0; i < data->indirect.Length(); i++) {
315				block_run *indirect = (block_run *)cached.SetTo(block + i);
316				if (indirect == NULL)
317					RETURN_ERROR(B_IO_ERROR);
318
319				int32 current = -1;
320				while (++current < runsPerBlock) {
321					if (indirect[current].IsZero())
322						break;
323
324					runBlockEnd += indirect[current].Length() << cached.BlockShift();
325					if (runBlockEnd > pos) {
326						run = indirect[current];
327						offset = runBlockEnd - (run.Length() << cached.BlockShift());
328						//printf("reading from indirect block: %ld,%d\n",fRun.allocation_group,fRun.start);
329						//printf("### indirect-run[%ld] = (%ld,%d,%d), offset = %Ld\n",fCurrent,fRun.allocation_group,fRun.start,fRun.Length(),fRunFileOffset);
330						return fVolume->ValidateBlockRun(run);
331					}
332				}
333			}
334			RETURN_ERROR(B_ERROR);
335		}
336	} else {
337		// access from direct blocks
338
339		off_t runBlockEnd = 0LL;
340		int32 current = -1;
341
342		while (++current < NUM_DIRECT_BLOCKS) {
343			if (data->direct[current].IsZero())
344				break;
345
346			runBlockEnd += data->direct[current].Length() << fVolume->BlockShift();
347			if (runBlockEnd > pos) {
348				run = data->direct[current];
349				offset = runBlockEnd - (run.Length() << fVolume->BlockShift());
350				//printf("### run[%ld] = (%ld,%d,%d), offset = %Ld\n",fCurrent,fRun.allocation_group,fRun.start,fRun.Length(),fRunFileOffset);
351				return fVolume->ValidateBlockRun(run);
352			}
353		}
354		//PRINT(("FindBlockRun() failed in direct range: size = %Ld, pos = %Ld\n",data->size,pos));
355		return B_ENTRY_NOT_FOUND;
356	}
357	return fVolume->ValidateBlockRun(run);
358}
359
360
361template<class Cache>
362status_t
363Stream<Cache>::ReadAt(off_t pos, uint8 *buffer, size_t *_length)
364{
365	size_t length = *_length;
366
367	// set/check boundaries for pos/length
368	if (pos < 0)
369		return B_BAD_VALUE;
370	if (pos >= Node()->data.Size() || length == 0) {
371		*_length = 0;
372		return B_NO_ERROR;
373	}
374
375	if (pos + length > Node()->data.Size())
376		length = Node()->data.Size() - pos;
377
378	block_run run;
379	off_t offset;
380	if (FindBlockRun(pos, run, offset) < B_OK) {
381		*_length = 0;
382		RETURN_ERROR(B_BAD_VALUE);
383	}
384
385	uint32 bytesRead = 0;
386	uint32 blockSize = fVolume->BlockSize();
387	uint32 blockShift = fVolume->BlockShift();
388	uint8 *block;
389
390	// the first block_run we read could not be aligned to the block_size boundary
391	// (read partial block at the beginning)
392
393	// pos % block_size == (pos - offset) % block_size, offset % block_size == 0
394	if (pos % blockSize != 0) {
395		run.start = HOST_ENDIAN_TO_BFS_INT16(run.Start() + ((pos - offset) >> blockShift));
396		run.length = HOST_ENDIAN_TO_BFS_INT16(run.Length() - ((pos - offset) >> blockShift));
397
398		Cache cached(fVolume,run);
399		if ((block = cached.Block()) == NULL) {
400			*_length = 0;
401			RETURN_ERROR(B_BAD_VALUE);
402		}
403
404		bytesRead = blockSize - (pos % blockSize);
405		if (length < bytesRead)
406			bytesRead = length;
407
408		memcpy(buffer, block + (pos % blockSize), bytesRead);
409		pos += bytesRead;
410
411		length -= bytesRead;
412		if (length == 0) {
413			*_length = bytesRead;
414			return B_OK;
415		}
416
417		if (FindBlockRun(pos, run, offset) < B_OK) {
418			*_length = bytesRead;
419			RETURN_ERROR(B_BAD_VALUE);
420		}
421	}
422
423	// the first block_run is already filled in at this point
424	// read the following complete blocks using cached_read(),
425	// the last partial block is read using the generic Cache class
426
427	bool partial = false;
428
429	while (length > 0) {
430		// offset is the offset to the current pos in the block_run
431		run.start = HOST_ENDIAN_TO_BFS_INT16(run.Start() + ((pos - offset) >> blockShift));
432		run.length = HOST_ENDIAN_TO_BFS_INT16(run.Length() - ((pos - offset) >> blockShift));
433
434		if (uint32(run.Length() << blockShift) > length) {
435			if (length < blockSize) {
436				Cache cached(fVolume, run);
437				if ((block = cached.Block()) == NULL) {
438					*_length = bytesRead;
439					RETURN_ERROR(B_BAD_VALUE);
440				}
441				memcpy(buffer + bytesRead, block, length);
442				bytesRead += length;
443				break;
444			}
445			run.length = HOST_ENDIAN_TO_BFS_INT16(length >> blockShift);
446			partial = true;
447		}
448
449		if (Cache::Read(fVolume, run, buffer + bytesRead) < B_OK) {
450			*_length = bytesRead;
451			RETURN_ERROR(B_BAD_VALUE);
452		}
453
454		int32 bytes = run.Length() << blockShift;
455#ifdef DEBUG
456		if ((uint32)bytes > length)
457			DEBUGGER(("bytes greater than length"));
458#endif
459		length -= bytes;
460		bytesRead += bytes;
461		if (length == 0)
462			break;
463
464		pos += bytes;
465
466		if (partial) {
467			// if the last block was read only partially, point block_run
468			// to the remaining part
469			run.start = HOST_ENDIAN_TO_BFS_INT16(run.Start() + run.Length());
470			run.length = HOST_ENDIAN_TO_BFS_INT16(1);
471			offset = pos;
472		} else if (FindBlockRun(pos, run, offset) < B_OK) {
473			*_length = bytesRead;
474			RETURN_ERROR(B_BAD_VALUE);
475		}
476	}
477
478	*_length = bytesRead;
479	return B_OK;
480}
481
482
483template<class Cache>
484status_t
485Stream<Cache>::WriteAt(Transaction *transaction, off_t pos, const uint8 *buffer, size_t *_length)
486{
487	size_t length = *_length;
488
489	// set/check boundaries for pos/length
490	if (pos < 0)
491		return B_BAD_VALUE;
492
493	if (pos + length > Size()) {
494		off_t oldSize = Size();
495
496		// uncached files can't be resized (Inode::SetFileSize() also
497		// doesn't allow this, but this way we don't have to start a
498		// transaction to find out).
499		if (Flags() & INODE_NO_CACHE)
500			return B_BAD_VALUE;
501
502		// the transaction doesn't have to be started already
503		// ToDo: what's that INODE_NO_TRANSACTION flag good for again?
504		if ((Flags() & INODE_NO_TRANSACTION) == 0
505			&& !transaction->IsStarted())
506			transaction->Start(fVolume, BlockNumber());
507
508		// let's grow the data stream to the size needed
509		status_t status = SetFileSize(transaction, pos + length);
510		if (status < B_OK) {
511			*_length = 0;
512			RETURN_ERROR(status);
513		}
514		// If the position of the write was beyond the file size, we
515		// have to fill the gap between that position and the old file
516		// size with zeros.
517		FillGapWithZeros(oldSize, pos);
518	}
519
520	// If we don't want to write anything, we can now return (we may
521	// just have changed the file size using the position parameter)
522	if (length == 0)
523		return B_OK;
524
525	block_run run;
526	off_t offset;
527	if (FindBlockRun(pos, run, offset) < B_OK) {
528		*_length = 0;
529		RETURN_ERROR(B_BAD_VALUE);
530	}
531
532	bool logStream = (Flags() & INODE_LOGGED) == INODE_LOGGED;
533	if (logStream
534		&& !transaction->IsStarted())
535		transaction->Start(fVolume, BlockNumber());
536
537	uint32 bytesWritten = 0;
538	uint32 blockSize = fVolume->BlockSize();
539	uint32 blockShift = fVolume->BlockShift();
540	uint8 *block;
541
542	// the first block_run we write could not be aligned to the block_size boundary
543	// (write partial block at the beginning)
544
545	// pos % block_size == (pos - offset) % block_size, offset % block_size == 0
546	if (pos % blockSize != 0) {
547		run.start = HOST_ENDIAN_TO_BFS_INT16(run.Start() + ((pos - offset) >> blockShift));
548		run.length = HOST_ENDIAN_TO_BFS_INT16(run.Length() - ((pos - offset) >> blockShift));
549
550		Cache cached(fVolume, run);
551		if ((block = cached.Block()) == NULL) {
552			*_length = 0;
553			RETURN_ERROR(B_BAD_VALUE);
554		}
555
556		bytesWritten = blockSize - (pos % blockSize);
557		if (length < bytesWritten)
558			bytesWritten = length;
559
560		memcpy(block + (pos % blockSize),buffer,bytesWritten);
561
562		cached.WriteBack(transaction);
563
564		pos += bytesWritten;
565
566		length -= bytesWritten;
567		if (length == 0) {
568			*_length = bytesWritten;
569			return B_OK;
570		}
571
572		if (FindBlockRun(pos, run, offset) < B_OK) {
573			*_length = bytesWritten;
574			RETURN_ERROR(B_BAD_VALUE);
575		}
576	}
577
578	// the first block_run is already filled in at this point
579	// write the following complete blocks using Volume::WriteBlocks(),
580	// the last partial block is written using the generic Cache class
581
582	bool partial = false;
583
584	while (length > 0) {
585		// offset is the offset to the current pos in the block_run
586		run.start = HOST_ENDIAN_TO_BFS_INT16(run.Start() + ((pos - offset) >> blockShift));
587		run.length = HOST_ENDIAN_TO_BFS_INT16(run.Length() - ((pos - offset) >> blockShift));
588
589		if (uint32(run.Length() << blockShift) > length) {
590			if (length < blockSize) {
591				Cache cached(fVolume,run);
592				if ((block = cached.Block()) == NULL) {
593					*_length = bytesWritten;
594					RETURN_ERROR(B_BAD_VALUE);
595				}
596				memcpy(block, buffer + bytesWritten, length);
597
598				cached.WriteBack(transaction);
599
600				bytesWritten += length;
601				break;
602			}
603			run.length = HOST_ENDIAN_TO_BFS_INT16(length >> blockShift);
604			partial = true;
605		}
606
607		if (Cache::Write(transaction, fVolume, run, buffer + bytesWritten) < B_OK) {
608			*_length = bytesWritten;
609			RETURN_ERROR(B_BAD_VALUE);
610		}
611
612		int32 bytes = run.Length() << blockShift;
613		length -= bytes;
614		bytesWritten += bytes;
615		if (length == 0)
616			break;
617
618		pos += bytes;
619
620		if (partial) {
621			// if the last block was written only partially, point block_run
622			// to the remaining part
623			run.start = HOST_ENDIAN_TO_BFS_INT16(run.Start() + run.Length());
624			run.length = HOST_ENDIAN_TO_BFS_INT16(1);
625			offset = pos;
626		} else if (FindBlockRun(pos, run, offset) < B_OK) {
627			*_length = bytesWritten;
628			RETURN_ERROR(B_BAD_VALUE);
629		}
630	}
631
632	*_length = bytesWritten;
633
634	return B_OK;
635}
636
637