xref: /haiku/src/add-ons/kernel/file_systems/ext2/Inode.cpp (revision 03187b607b2b5eec7ee059f1ead09bdba14991fb)
1 /*
2  * Copyright 2008, Axel Dörfler, axeld@pinc-software.de.
3  * This file may be used under the terms of the MIT License.
4  */
5 
6 
7 #include "Inode.h"
8 
9 #include <fs_cache.h>
10 
11 #include "CachedBlock.h"
12 
13 
14 //#define TRACE_EXT2
15 #ifdef TRACE_EXT2
16 #	define TRACE(x...) dprintf("\33[34mext2:\33[0m " x)
17 #else
18 #	define TRACE(x...) ;
19 #endif
20 
21 
22 Inode::Inode(Volume* volume, ino_t id)
23 	:
24 	fVolume(volume),
25 	fID(id),
26 	fCache(NULL),
27 	fMap(NULL),
28 	fNode(NULL)
29 {
30 	rw_lock_init(&fLock, "ext2 inode");
31 
32 	uint32 block;
33 	if (volume->GetInodeBlock(id, block) == B_OK) {
34 		TRACE("inode %Ld at block %lu\n", ID(), block);
35 		uint8* inodeBlock = (uint8*)block_cache_get(volume->BlockCache(),
36 			block);
37 		if (inodeBlock != NULL) {
38 			fNode = (ext2_inode*)(inodeBlock + volume->InodeBlockIndex(id)
39 				* volume->InodeSize());
40 		}
41 	}
42 
43 	if (fNode != NULL) {
44 		// TODO: we don't need a cache for short symlinks
45 		fCache = file_cache_create(fVolume->ID(), ID(), Size());
46 		fMap = file_map_create(fVolume->ID(), ID(), Size());
47 	}
48 }
49 
50 
51 Inode::~Inode()
52 {
53 	file_cache_delete(FileCache());
54 	file_map_delete(Map());
55 
56 	if (fNode != NULL) {
57 		uint32 block;
58 		if (fVolume->GetInodeBlock(ID(), block) == B_OK)
59 			block_cache_put(fVolume->BlockCache(), block);
60 	}
61 }
62 
63 
64 status_t
65 Inode::InitCheck()
66 {
67 	return fNode != NULL ? B_OK : B_ERROR;
68 }
69 
70 
71 status_t
72 Inode::CheckPermissions(int accessMode) const
73 {
74 	uid_t user = geteuid();
75 	gid_t group = getegid();
76 
77 	// you never have write access to a read-only volume
78 	if (accessMode & W_OK && fVolume->IsReadOnly())
79 		return B_READ_ONLY_DEVICE;
80 
81 	// root users always have full access (but they can't execute files without
82 	// any execute permissions set)
83 	if (user == 0) {
84 		if (!((accessMode & X_OK) != 0 && (Mode() & S_IXUSR) == 0)
85 			|| S_ISDIR(Mode()))
86 			return B_OK;
87 	}
88 
89 	// shift mode bits, to check directly against accessMode
90 	mode_t mode = Mode();
91 	if (user == (uid_t)fNode->UserID())
92 		mode >>= 6;
93 	else if (group == (gid_t)fNode->GroupID())
94 		mode >>= 3;
95 
96 	if (accessMode & ~(mode & S_IRWXO))
97 		return B_NOT_ALLOWED;
98 
99 	return B_OK;
100 }
101 
102 
103 status_t
104 Inode::FindBlock(off_t offset, uint32& block)
105 {
106 	uint32 perBlock = fVolume->BlockSize() / 4;
107 	uint32 perIndirectBlock = perBlock * perBlock;
108 	uint32 index = offset >> fVolume->BlockShift();
109 
110 	if (offset >= Size())
111 		return B_ENTRY_NOT_FOUND;
112 
113 	// TODO: we could return the size of the sparse range, as this might be more
114 	// than just a block
115 
116 	if (index < EXT2_DIRECT_BLOCKS) {
117 		// direct blocks
118 		block = B_LENDIAN_TO_HOST_INT32(Node().stream.direct[index]);
119 	} else if ((index -= EXT2_DIRECT_BLOCKS) < perBlock) {
120 		// indirect blocks
121 		CachedBlock cached(fVolume);
122 		uint32* indirectBlocks = (uint32*)cached.SetTo(B_LENDIAN_TO_HOST_INT32(
123 			Node().stream.indirect));
124 		if (indirectBlocks == NULL)
125 			return B_IO_ERROR;
126 
127 		block = B_LENDIAN_TO_HOST_INT32(indirectBlocks[index]);
128 	} else if ((index -= perBlock) < perIndirectBlock) {
129 		// double indirect blocks
130 		CachedBlock cached(fVolume);
131 		uint32* indirectBlocks = (uint32*)cached.SetTo(B_LENDIAN_TO_HOST_INT32(
132 			Node().stream.double_indirect));
133 		if (indirectBlocks == NULL)
134 			return B_IO_ERROR;
135 
136 		uint32 indirectIndex
137 			= B_LENDIAN_TO_HOST_INT32(indirectBlocks[index / perBlock]);
138 		if (indirectIndex == 0) {
139 			// a sparse indirect block
140 			block = 0;
141 		} else {
142 			indirectBlocks = (uint32*)cached.SetTo(indirectIndex);
143 			if (indirectBlocks == NULL)
144 				return B_IO_ERROR;
145 
146 			block = B_LENDIAN_TO_HOST_INT32(
147 				indirectBlocks[index & (perBlock - 1)]);
148 		}
149 	} else if ((index -= perIndirectBlock) / perBlock < perIndirectBlock) {
150 		// triple indirect blocks
151 		CachedBlock cached(fVolume);
152 		uint32* indirectBlocks = (uint32*)cached.SetTo(B_LENDIAN_TO_HOST_INT32(
153 			Node().stream.triple_indirect));
154 		if (indirectBlocks == NULL)
155 			return B_IO_ERROR;
156 
157 		uint32 indirectIndex
158 			= B_LENDIAN_TO_HOST_INT32(indirectBlocks[index / perIndirectBlock]);
159 		if (indirectIndex == 0) {
160 			// a sparse indirect block
161 			block = 0;
162 		} else {
163 			indirectBlocks = (uint32*)cached.SetTo(indirectIndex);
164 			if (indirectBlocks == NULL)
165 				return B_IO_ERROR;
166 
167 			indirectIndex = B_LENDIAN_TO_HOST_INT32(
168 				indirectBlocks[(index / perBlock) & (perBlock - 1)]);
169 			if (indirectIndex == 0) {
170 				// a sparse indirect block
171 				block = 0;
172 			} else {
173 				indirectBlocks = (uint32*)cached.SetTo(indirectIndex);
174 				if (indirectBlocks == NULL)
175 					return B_IO_ERROR;
176 
177 				block = B_LENDIAN_TO_HOST_INT32(
178 					indirectBlocks[index & (perBlock - 1)]);
179 			}
180 		}
181 	} else {
182 		// outside of the possible data stream
183 		dprintf("ext2: block outside datastream!\n");
184 		return B_ERROR;
185 	}
186 
187 	TRACE("inode %Ld: FindBlock(offset %Ld): %lu\n", ID(), offset, block);
188 	return B_OK;
189 }
190 
191 
192 status_t
193 Inode::ReadAt(off_t pos, uint8* buffer, size_t* _length)
194 {
195 	size_t length = *_length;
196 
197 	// set/check boundaries for pos/length
198 	if (pos < 0)
199 		return B_BAD_VALUE;
200 
201 	if (pos >= Size() || length == 0) {
202 		*_length = 0;
203 		return B_NO_ERROR;
204 	}
205 
206 	return file_cache_read(FileCache(), NULL, pos, buffer, _length);
207 }
208 
209