xref: /haiku/src/add-ons/kernel/file_systems/ext2/Inode.cpp (revision 0562493379cd52eb7103531f895f10bb8e77c085)
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 	if (index < EXT2_DIRECT_BLOCKS) {
114 		// direct blocks
115 		block = B_LENDIAN_TO_HOST_INT32(Node().stream.direct[index]);
116 	} else if ((index -= EXT2_DIRECT_BLOCKS) < perBlock) {
117 		// indirect blocks
118 		CachedBlock cached(fVolume);
119 		uint32* indirectBlocks = (uint32*)cached.SetTo(B_LENDIAN_TO_HOST_INT32(
120 			Node().stream.indirect));
121 		if (indirectBlocks == NULL)
122 			return B_IO_ERROR;
123 
124 		block = B_LENDIAN_TO_HOST_INT32(indirectBlocks[index]);
125 	} else if ((index -= perBlock) < perIndirectBlock) {
126 		// double indirect blocks
127 		CachedBlock cached(fVolume);
128 		uint32* indirectBlocks = (uint32*)cached.SetTo(B_LENDIAN_TO_HOST_INT32(
129 			Node().stream.double_indirect));
130 		if (indirectBlocks == NULL)
131 			return B_IO_ERROR;
132 
133 		indirectBlocks = (uint32*)cached.SetTo(B_LENDIAN_TO_HOST_INT32(
134 			indirectBlocks[index / perBlock]));
135 		if (indirectBlocks == NULL)
136 			return B_IO_ERROR;
137 
138 		block = B_LENDIAN_TO_HOST_INT32(indirectBlocks[index & (perBlock - 1)]);
139 	} else if ((index -= perIndirectBlock) / perBlock < perIndirectBlock) {
140 		// triple indirect blocks
141 		CachedBlock cached(fVolume);
142 		uint32* indirectBlocks = (uint32*)cached.SetTo(B_LENDIAN_TO_HOST_INT32(
143 			Node().stream.triple_indirect));
144 		if (indirectBlocks == NULL)
145 			return B_IO_ERROR;
146 
147 		indirectBlocks = (uint32*)cached.SetTo(B_LENDIAN_TO_HOST_INT32(
148 			indirectBlocks[index / perIndirectBlock]));
149 		if (indirectBlocks == NULL)
150 			return B_IO_ERROR;
151 
152 		indirectBlocks = (uint32*)cached.SetTo(B_LENDIAN_TO_HOST_INT32(
153 			indirectBlocks[(index / perBlock) & (perBlock - 1)]));
154 		if (indirectBlocks == NULL)
155 			return B_IO_ERROR;
156 
157 		block = B_LENDIAN_TO_HOST_INT32(indirectBlocks[index & (perBlock - 1)]);
158 	} else {
159 		// outside of the possible data stream
160 		dprintf("ext2: block outside datastream!\n");
161 		return B_ERROR;
162 	}
163 
164 	TRACE("inode %Ld: FindBlock(offset %Ld): %lu\n", ID(), offset, block);
165 	return B_OK;
166 }
167 
168 
169 status_t
170 Inode::ReadAt(off_t pos, uint8* buffer, size_t* _length)
171 {
172 	size_t length = *_length;
173 
174 	// set/check boundaries for pos/length
175 	if (pos < 0)
176 		return B_BAD_VALUE;
177 
178 	if (pos >= Size() || length == 0) {
179 		*_length = 0;
180 		return B_NO_ERROR;
181 	}
182 
183 	return file_cache_read(FileCache(), NULL, pos, buffer, _length);
184 }
185 
186