1/* Inode - inode access functions
2 *
3 * Copyright 2001-2008, Axel Dörfler, axeld@pinc-software.de
4 * This file may be used under the terms of the MIT License.
5 */
6#ifndef INODE_H
7#define INODE_H
8
9
10#include <KernelExport.h>
11#ifdef USER
12#	include <stdio.h>
13#endif
14
15#include "cache.h"
16#include "fsproto.h"
17
18#include <string.h>
19#include <unistd.h>
20
21#include "Volume.h"
22#include "Journal.h"
23#include "Lock.h"
24#include "Chain.h"
25#include "Debug.h"
26
27
28class BPlusTree;
29class TreeIterator;
30class AttributeIterator;
31class InodeAllocator;
32
33
34enum inode_type {
35	S_DIRECTORY		= S_IFDIR,
36	S_FILE			= S_IFREG,
37	S_SYMLINK		= S_IFLNK,
38
39	S_INDEX_TYPES	= (S_STR_INDEX | S_INT_INDEX | S_UINT_INDEX | S_LONG_LONG_INDEX
40						| S_ULONG_LONG_INDEX | S_FLOAT_INDEX | S_DOUBLE_INDEX)
41};
42
43
44// The CachedBlock class is completely implemented as inlines.
45// It should be used when cache single blocks to make sure they
46// will be properly released after use (and it's also very
47// convenient to use them).
48
49class CachedBlock {
50	public:
51		CachedBlock(Volume *volume);
52		CachedBlock(Volume *volume, off_t block, bool empty = false);
53		CachedBlock(Volume *volume, block_run run, bool empty = false);
54		CachedBlock(CachedBlock *cached);
55		~CachedBlock();
56
57		inline void Keep();
58		inline void Unset();
59		inline uint8 *SetTo(off_t block, bool empty = false);
60		inline uint8 *SetTo(block_run run, bool empty = false);
61		inline status_t WriteBack(Transaction *transaction);
62
63		uint8 *Block() const { return fBlock; }
64		off_t BlockNumber() const { return fBlockNumber; }
65		uint32 BlockSize() const { return fVolume->BlockSize(); }
66		uint32 BlockShift() const { return fVolume->BlockShift(); }
67
68	private:
69		CachedBlock(const CachedBlock &);
70		CachedBlock &operator=(const CachedBlock &);
71			// no implementation
72
73	protected:
74		Volume	*fVolume;
75		off_t	fBlockNumber;
76		uint8	*fBlock;
77};
78
79//--------------------------------------
80
81class Inode : public CachedBlock {
82	public:
83		Inode(Volume *volume, vnode_id id, bool empty = false, uint8 reenter = 0);
84		Inode(CachedBlock *cached);
85		~Inode();
86
87		bfs_inode *Node() const { return (bfs_inode *)fBlock; }
88		vnode_id ID() const { return fVolume->ToVnode(fBlockNumber); }
89
90		ReadWriteLock &Lock() { return fLock; }
91		SimpleLock &SmallDataLock() { return fSmallDataLock; }
92
93		mode_t Mode() const { return Node()->Mode(); }
94		uint32 Type() const { return Node()->Type(); }
95		int32 Flags() const { return Node()->Flags(); }
96		bool IsContainer() const { return Mode() & (S_DIRECTORY | S_INDEX_DIR | S_ATTR_DIR); }
97			// note, that this test will also be true for S_IFBLK (not that it's used in the fs :)
98		bool IsDirectory() const { return (Mode() & (S_DIRECTORY | S_INDEX_DIR | S_ATTR_DIR)) == S_DIRECTORY; }
99		bool IsIndex() const { return (Mode() & (S_INDEX_DIR | 0777)) == S_INDEX_DIR; }
100			// that's a stupid check, but AFAIK the only possible method...
101
102		bool IsDeleted() const { return (Flags() & INODE_DELETED) != 0; }
103
104		bool IsAttributeDirectory() const { return (Mode() & S_ATTR_DIR) != 0; }
105		bool IsAttribute() const { return Mode() & S_ATTR; }
106		bool IsFile() const { return (Mode() & (S_IFMT | S_ATTR)) == S_FILE; }
107		bool IsRegularNode() const { return (Mode() & (S_ATTR_DIR | S_INDEX_DIR | S_ATTR)) == 0; }
108			// a regular node in the standard namespace (i.e. not an index or attribute)
109		bool IsSymLink() const { return S_ISLNK(Mode()); }
110		bool HasUserAccessableStream() const { return IsFile(); }
111			// currently only files can be accessed with bfs_read()/bfs_write()
112
113		off_t Size() const { return Node()->data.Size(); }
114		off_t LastModified() const { return Node()->last_modified_time; }
115
116		block_run &BlockRun() const { return Node()->inode_num; }
117		block_run &Parent() const { return Node()->parent; }
118		block_run &Attributes() const { return Node()->attributes; }
119		Volume *GetVolume() const { return fVolume; }
120
121		status_t InitCheck(bool checkNode = true);
122
123		status_t CheckPermissions(int accessMode) const;
124
125		// small_data access methods
126		status_t MakeSpaceForSmallData(Transaction *transaction, const char *name, int32 length);
127		status_t RemoveSmallData(Transaction *transaction, const char *name);
128		status_t AddSmallData(Transaction *transaction, const char *name, uint32 type,
129					const uint8 *data, size_t length, bool force = false);
130		status_t GetNextSmallData(small_data **_smallData) const;
131		small_data *FindSmallData(const char *name) const;
132		const char *Name() const;
133		status_t GetName(char *buffer) const;
134		status_t SetName(Transaction *transaction, const char *name);
135
136		// high-level attribute methods
137		status_t ReadAttribute(const char *name, int32 type, off_t pos, uint8 *buffer, size_t *_length);
138		status_t WriteAttribute(Transaction *transaction, const char *name, int32 type, off_t pos, const uint8 *buffer, size_t *_length);
139		status_t RemoveAttribute(Transaction *transaction, const char *name);
140
141		// attribute methods
142		status_t GetAttribute(const char *name, Inode **attribute);
143		void ReleaseAttribute(Inode *attribute);
144		status_t CreateAttribute(Transaction *transaction, const char *name, uint32 type, Inode **attribute);
145
146		// for directories only:
147		status_t GetTree(BPlusTree **);
148		bool IsEmpty();
149
150		// manipulating the data stream
151		status_t FindBlockRun(off_t pos, block_run &run, off_t &offset);
152
153		status_t ReadAt(off_t pos, uint8 *buffer, size_t *length);
154		status_t WriteAt(Transaction *transaction, off_t pos, const uint8 *buffer, size_t *length);
155		status_t FillGapWithZeros(off_t oldSize, off_t newSize);
156
157		status_t SetFileSize(Transaction *transaction, off_t size);
158		status_t Append(Transaction *transaction, off_t bytes);
159		status_t Trim(Transaction *transaction);
160
161		status_t Free(Transaction *transaction);
162		status_t Sync();
163
164		// create/remove inodes
165		status_t Remove(Transaction *transaction, const char *name, off_t *_id = NULL,
166					bool isDirectory = false);
167		static status_t Create(Transaction *transaction, Inode *parent, const char *name,
168					int32 mode, int omode, uint32 type, off_t *_id = NULL, Inode **_inode = NULL);
169
170		// index maintaining helper
171		void UpdateOldSize() { fOldSize = Size(); }
172		void UpdateOldLastModified() { fOldLastModified = Node()->LastModifiedTime(); }
173		off_t OldSize() { return fOldSize; }
174		off_t OldLastModified() { return fOldLastModified; }
175
176		// file cache
177		void *FileCache() const { return fCache; }
178		void SetFileCache(void *cache) { fCache = cache; }
179
180	private:
181		Inode(const Inode &);
182		Inode &operator=(const Inode &);
183			// no implementation
184
185		friend void dump_inode(Inode &inode);
186		friend class AttributeIterator;
187		friend class InodeAllocator;
188
189		void Initialize();
190
191		status_t RemoveSmallData(small_data *item, int32 index);
192
193		void AddIterator(AttributeIterator *iterator);
194		void RemoveIterator(AttributeIterator *iterator);
195
196		status_t FreeStaticStreamArray(Transaction *transaction, int32 level, block_run run,
197					off_t size, off_t offset, off_t &max);
198		status_t FreeStreamArray(Transaction *transaction, block_run *array, uint32 arrayLength,
199					off_t size, off_t &offset, off_t &max);
200		status_t AllocateBlockArray(Transaction *transaction, block_run &run);
201		status_t GrowStream(Transaction *transaction, off_t size);
202		status_t ShrinkStream(Transaction *transaction, off_t size);
203
204		BPlusTree		*fTree;
205		Inode			*fAttributes;
206		ReadWriteLock	fLock;
207		off_t			fOldSize;			// we need those values to ensure we will remove
208		off_t			fOldLastModified;	// the correct keys from the indices
209		void			*fCache;
210
211		mutable SimpleLock	fSmallDataLock;
212		Chain<AttributeIterator> fIterators;
213};
214
215
216// The Vnode class provides a convenience layer upon get_vnode(), so that
217// you don't have to call put_vnode() anymore, which may make code more
218// readable in some cases
219
220class Vnode {
221	public:
222		Vnode(Volume *volume, vnode_id id)
223			:
224			fVolume(volume),
225			fID(id)
226		{
227		}
228
229		Vnode(Volume *volume, block_run run)
230			:
231			fVolume(volume),
232			fID(volume->ToVnode(run))
233		{
234		}
235
236		~Vnode()
237		{
238			Put();
239		}
240
241		status_t Get(Inode **inode)
242		{
243			// should we check inode against NULL here? it should not be necessary
244#ifdef UNSAFE_GET_VNODE
245			RecursiveLocker locker(fVolume->Lock());
246#endif
247			return get_vnode(fVolume->ID(), fID, (void **)inode);
248		}
249
250		void Put()
251		{
252			if (fVolume)
253				put_vnode(fVolume->ID(), fID);
254			fVolume = NULL;
255		}
256
257		void Keep()
258		{
259			fVolume = NULL;
260		}
261
262	private:
263		Volume		*fVolume;
264		vnode_id	fID;
265};
266
267
268class AttributeIterator {
269	public:
270		AttributeIterator(Inode *inode);
271		~AttributeIterator();
272
273		status_t Rewind();
274		status_t GetNext(char *name, size_t *length, uint32 *type, vnode_id *id);
275
276	private:
277		int32		fCurrentSmallData;
278		Inode		*fInode, *fAttributes;
279		TreeIterator *fIterator;
280		void		*fBuffer;
281
282	private:
283		friend class Chain<AttributeIterator>;
284		friend class Inode;
285
286		void Update(uint16 index, int8 change);
287		AttributeIterator *fNext;
288};
289
290
291//--------------------------------------
292// inlines
293
294
295inline
296CachedBlock::CachedBlock(Volume *volume)
297	:
298	fVolume(volume),
299	fBlockNumber(-1),
300	fBlock(NULL)
301{
302}
303
304
305inline
306CachedBlock::CachedBlock(Volume *volume, off_t block, bool empty)
307	:
308	fVolume(volume),
309	fBlock(NULL)
310{
311	SetTo(block, empty);
312}
313
314
315inline
316CachedBlock::CachedBlock(Volume *volume, block_run run, bool empty)
317	:
318	fVolume(volume),
319	fBlock(NULL)
320{
321	SetTo(volume->ToBlock(run), empty);
322}
323
324
325inline
326CachedBlock::CachedBlock(CachedBlock *cached)
327	:
328	fVolume(cached->fVolume),
329	fBlockNumber(cached->BlockNumber()),
330	fBlock(cached->fBlock)
331{
332	cached->Keep();
333}
334
335
336inline
337CachedBlock::~CachedBlock()
338{
339	Unset();
340}
341
342
343inline void
344CachedBlock::Keep()
345{
346	fBlock = NULL;
347}
348
349
350inline void
351CachedBlock::Unset()
352{
353	if (fBlock != NULL)
354		release_block(fVolume->Device(), fBlockNumber);
355}
356
357
358inline uint8 *
359CachedBlock::SetTo(off_t block, bool empty)
360{
361	Unset();
362	fBlockNumber = block;
363	return fBlock = empty ? (uint8 *)get_empty_block(fVolume->Device(), block, BlockSize())
364						  : (uint8 *)get_block(fVolume->Device(), block, BlockSize());
365}
366
367
368inline uint8 *
369CachedBlock::SetTo(block_run run, bool empty)
370{
371	return SetTo(fVolume->ToBlock(run), empty);
372}
373
374
375inline status_t
376CachedBlock::WriteBack(Transaction *transaction)
377{
378	if (transaction == NULL || fBlock == NULL)
379		RETURN_ERROR(B_BAD_VALUE);
380
381	return transaction->WriteBlocks(fBlockNumber, fBlock);
382}
383
384
385/**	Converts the "omode", the open flags given to bfs_open(), into
386 *	access modes, e.g. since O_RDONLY requires read access to the
387 *	file, it will be converted to R_OK.
388 */
389
390inline int
391oModeToAccess(int omode)
392{
393	omode &= O_RWMASK;
394	if (omode == O_RDONLY)
395		return R_OK;
396	else if (omode == O_WRONLY)
397		return W_OK;
398
399	return R_OK | W_OK;
400}
401
402#endif	/* INODE_H */
403