1/*
2 * Copyright 2011, J��r��me Duval, korli@users.berlios.de.
3 * Copyright 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 <fs_cache.h>
11#include <lock.h>
12#include <string.h>
13
14#include "ext2.h"
15#include "Volume.h"
16
17
18//#define TRACE_EXT2
19#ifdef TRACE_EXT2
20#	define TRACEI(x...) dprintf("\33[34mext2:\33[0m " x)
21#else
22#	define TRACEI(x...) ;
23#endif
24
25
26class Inode : public TransactionListener {
27public:
28						Inode(Volume* volume, ino_t id);
29						~Inode();
30
31			status_t	InitCheck();
32
33			ino_t		ID() const { return fID; }
34
35			rw_lock*	Lock() { return &fLock; }
36			void		WriteLockInTransaction(Transaction& transaction);
37
38			status_t	UpdateNodeFromDisk();
39			status_t	WriteBack(Transaction& transaction);
40
41			recursive_lock&	SmallDataLock() { return fSmallDataLock; }
42
43			bool		IsDirectory() const
44							{ return S_ISDIR(Mode()); }
45			bool		IsFile() const
46							{ return S_ISREG(Mode()); }
47			bool		IsSymLink() const
48							{ return S_ISLNK(Mode()); }
49			status_t	CheckPermissions(int accessMode) const;
50
51			bool		IsDeleted() const { return fUnlinked; }
52			bool		HasExtraAttributes() const
53							{ return fHasExtraAttributes; }
54			bool		IsIndexed() const
55						{ return fVolume->IndexedDirectories()
56							&& (Flags() & EXT2_INODE_INDEXED) != 0; }
57
58			mode_t		Mode() const { return fNode.Mode(); }
59			int32		Flags() const { return fNode.Flags(); }
60
61			off_t		Size() const { return fNode.Size(); }
62			uint64		NumBlocks();
63			void		GetChangeTime(struct timespec *timespec) const
64						{ fNode.GetChangeTime(timespec, fHasExtraAttributes); }
65			void		GetModificationTime(struct timespec *timespec) const
66						{ fNode.GetModificationTime(timespec,
67							fHasExtraAttributes); }
68			void		GetCreationTime(struct timespec *timespec) const
69						{ fNode.GetCreationTime(timespec,
70							fHasExtraAttributes); }
71			void		GetAccessTime(struct timespec *timespec) const
72						{ fNode.GetAccessTime(timespec, fHasExtraAttributes); }
73			void		SetChangeTime(const struct timespec *timespec)
74						{ fNode.SetChangeTime(timespec, fHasExtraAttributes); }
75			void		SetModificationTime(const struct timespec *timespec)
76						{ fNode.SetModificationTime(timespec,
77							fHasExtraAttributes); }
78			void		SetCreationTime(const struct timespec *timespec)
79						{ fNode.SetCreationTime(timespec,
80							fHasExtraAttributes); }
81			void		SetAccessTime(const struct timespec *timespec)
82						{ fNode.SetAccessTime(timespec, fHasExtraAttributes); }
83			void		IncrementNumLinks(Transaction& transaction);
84
85			//::Volume* _Volume() const { return fVolume; }
86			Volume*		GetVolume() const { return fVolume; }
87
88			status_t	FindBlock(off_t offset, fsblock_t& block,
89							uint32 *_count = NULL);
90			status_t	ReadAt(off_t pos, uint8 *buffer, size_t *length);
91			status_t	WriteAt(Transaction& transaction, off_t pos,
92							const uint8* buffer, size_t* length);
93			status_t	FillGapWithZeros(off_t start, off_t end);
94
95			status_t	Resize(Transaction& transaction, off_t size);
96
97			ext2_inode&	Node() { return fNode; }
98
99			status_t	InitDirectory(Transaction& transaction, Inode* parent);
100
101			status_t	Unlink(Transaction& transaction);
102
103	static	status_t	Create(Transaction& transaction, Inode* parent,
104							const char* name, int32 mode, int openMode,
105							uint8 type, bool* _created = NULL,
106							ino_t* _id = NULL, Inode** _inode = NULL,
107							fs_vnode_ops* vnodeOps = NULL,
108							uint32 publishFlags = 0);
109	static 	void		_BigtimeToTimespec(bigtime_t time,
110							struct timespec *timespec)
111						{ timespec->tv_sec = time / 1000000LL;
112							timespec->tv_nsec = (time % 1000000LL) * 1000; }
113
114			void*		FileCache() const { return fCache; }
115			void*		Map() const { return fMap; }
116			status_t	CreateFileCache();
117			void		DeleteFileCache();
118			bool		HasFileCache() { return fCache != NULL; }
119			status_t	EnableFileCache();
120			status_t	DisableFileCache();
121
122			status_t	Sync();
123
124			void		SetDirEntryChecksum(uint8* block, uint32 id, uint32 gen);
125			void		SetDirEntryChecksum(uint8* block);
126
127			void		SetExtentChecksum(ext2_extent_stream* stream);
128			bool		VerifyExtentChecksum(ext2_extent_stream* stream);
129
130protected:
131	virtual	void		TransactionDone(bool success);
132	virtual	void		RemovedFromTransaction();
133
134
135private:
136						Inode(Volume* volume);
137						Inode(const Inode&);
138						Inode &operator=(const Inode&);
139							// no implementation
140
141			status_t	_EnlargeDataStream(Transaction& transaction,
142							off_t size);
143			status_t	_ShrinkDataStream(Transaction& transaction,
144							off_t size);
145
146			status_t	_SetNumBlocks(uint64 numBlocks);
147
148			uint32		_InodeChecksum(ext2_inode* inode);
149
150			ext2_dir_entry_tail*	_DirEntryTail(uint8* block) const;
151			uint32		_DirEntryChecksum(uint8* block, uint32 id,
152							uint32 gen) const;
153
154			uint32		_ExtentLength(ext2_extent_stream* stream) const;
155			uint32		_ExtentChecksum(ext2_extent_stream* stream) const;
156
157			rw_lock		fLock;
158			::Volume*	fVolume;
159			ino_t		fID;
160			void*		fCache;
161			void*		fMap;
162			bool		fUnlinked;
163			bool		fHasExtraAttributes;
164			ext2_inode	fNode;
165			uint32		fNodeSize;
166				// Inodes have a variable size, but the important
167				// information is always the same size (except in ext4)
168			status_t	fInitStatus;
169
170			mutable recursive_lock fSmallDataLock;
171};
172
173
174// The Vnode class provides a convenience layer upon get_vnode(), so that
175// you don't have to call put_vnode() anymore, which may make code more
176// readable in some cases
177
178class Vnode {
179public:
180	Vnode(Volume* volume, ino_t id)
181		:
182		fInode(NULL)
183	{
184		SetTo(volume, id);
185	}
186
187	Vnode()
188		:
189		fStatus(B_NO_INIT),
190		fInode(NULL)
191	{
192	}
193
194	~Vnode()
195	{
196		Unset();
197	}
198
199	status_t InitCheck()
200	{
201		return fStatus;
202	}
203
204	void Unset()
205	{
206		if (fInode != NULL) {
207			put_vnode(fInode->GetVolume()->FSVolume(), fInode->ID());
208			fInode = NULL;
209			fStatus = B_NO_INIT;
210		}
211	}
212
213	status_t SetTo(Volume* volume, ino_t id)
214	{
215		Unset();
216
217		return fStatus = get_vnode(volume->FSVolume(), id, (void**)&fInode);
218	}
219
220	status_t Get(Inode** _inode)
221	{
222		*_inode = fInode;
223		return fStatus;
224	}
225
226	void Keep()
227	{
228		TRACEI("Vnode::Keep()\n");
229		fInode = NULL;
230	}
231
232	status_t Publish(Transaction& transaction, Inode* inode,
233		fs_vnode_ops* vnodeOps, uint32 publishFlags)
234	{
235		TRACEI("Vnode::Publish()\n");
236		Volume* volume = transaction.GetVolume();
237
238		status_t status = B_OK;
239
240		if (!inode->IsSymLink() && volume->ID() >= 0) {
241			TRACEI("Vnode::Publish(): Publishing volume: %p, %" B_PRIdINO
242				", %p, %p, %" B_PRIu16 ", %" B_PRIx32 "\n", volume->FSVolume(),
243				inode->ID(), inode, vnodeOps != NULL ? vnodeOps : &gExt2VnodeOps,
244				inode->Mode(), publishFlags);
245			status = publish_vnode(volume->FSVolume(), inode->ID(), inode,
246				vnodeOps != NULL ? vnodeOps : &gExt2VnodeOps, inode->Mode(),
247				publishFlags);
248			TRACEI("Vnode::Publish(): Result: %s\n", strerror(status));
249		}
250
251		if (status == B_OK) {
252			TRACEI("Vnode::Publish(): Preparing internal data\n");
253			fInode = inode;
254			fStatus = B_OK;
255
256			cache_add_transaction_listener(volume->BlockCache(),
257				transaction.ID(), TRANSACTION_ABORTED, &_TransactionListener,
258				inode);
259		}
260
261		return status;
262	}
263
264private:
265	status_t	fStatus;
266	Inode*		fInode;
267
268	// TODO: How to apply coding style here?
269	static void _TransactionListener(int32 id, int32 event, void* _inode)
270	{
271		Inode* inode = (Inode*)_inode;
272
273		if (event == TRANSACTION_ABORTED) {
274			// TODO: Unpublish?
275			panic("Transaction %d aborted, inode %p still exists!\n", (int)id,
276				inode);
277		}
278	}
279};
280
281#endif	// INODE_H
282