xref: /haiku/src/add-ons/kernel/file_systems/xfs/Volume.cpp (revision cb837539f5d245cedff238f8894c0ae326c2eaf5)
1 /*
2  * Copyright 2001 - 2017, Axel Dörfler, axeld @pinc - software.de.
3  * Copyright 2020, Shubham Bhagat, shubhambhagat111@yahoo.com
4  * All rights reserved. Distributed under the terms of the MIT License.
5  */
6 #include "Volume.h"
7 
8 
9 class DeviceOpener
10 {
11 public:
12 							DeviceOpener(int fd, int mode);
13 							DeviceOpener(const char *device, int mode);
14 							~DeviceOpener();
15 
16 			int				Open(const char *device, int mode);
17 			int				Open(int fd, int mode);
18 			void*			InitCache(off_t numBlocks, uint32 blockSize);
19 			void			RemoveCache(bool allowWrites);
20 
21 			void			Keep();
22 
23 			int				Device() const { return fDevice; }
24 			int				Mode() const { return fMode; }
25 			bool			IsReadOnly() const
26 								{ return _IsReadOnly(fMode); }
27 
28 			status_t		GetSize(off_t* _size, uint32* _blockSize = NULL);
29 
30 private:
31 	static	bool 			_IsReadOnly(int mode)
32 								{ return (mode & O_RWMASK) == O_RDONLY; }
33 	static	bool			_IsReadWrite(int mode)
34 								{ return (mode & O_RWMASK) == O_RDWR; }
35 
36 			int				fDevice;
37 			int				fMode;
38 			void*			fBlockCache;
39 };
40 
41 
42 DeviceOpener::DeviceOpener(const char *device, int mode)
43 	: fBlockCache(NULL)
44 {
45 	Open(device, mode);
46 }
47 
48 
49 DeviceOpener::DeviceOpener(int fd, int mode)
50 	: fBlockCache(NULL)
51 {
52 	Open(fd, mode);
53 }
54 
55 
56 DeviceOpener::~DeviceOpener()
57 {
58 	if (fDevice >= 0) {
59 		RemoveCache(false);
60 		close(fDevice);
61 	}
62 }
63 
64 
65 int
66 DeviceOpener::Open(const char *device, int mode)
67 {
68 	fDevice = open(device, mode | O_NOCACHE);
69 	if (fDevice < 0)
70 		fDevice = errno;
71 
72 	if (fDevice < 0 && _IsReadWrite(mode)){
73 		// try again to open read-only (don't rely on a specific error code)
74 		return Open(device, O_RDONLY | O_NOCACHE);
75 	}
76 
77 	if (fDevice >= 0) {
78 		// opening succeeded
79 		fMode = mode;
80 		if (_IsReadWrite(mode)) {
81 			// check out if the device really allows for read/write access
82 			device_geometry geometry;
83 			if (!ioctl(fDevice, B_GET_GEOMETRY, &geometry)) {
84 
85 				if (geometry.read_only) {
86 					// reopen device read-only
87 					close(fDevice);
88 					return Open(device, O_RDONLY | O_NOCACHE);
89 				}
90 			}
91 		}
92 	}
93 
94 	return fDevice;
95 }
96 
97 
98 int
99 DeviceOpener::Open(int fd, int mode)
100 {
101 	fDevice = dup(fd);
102 	if (fDevice < 0)
103 		return errno;
104 
105 	fMode = mode;
106 
107 	return fDevice;
108 }
109 
110 
111 void*
112 DeviceOpener::InitCache(off_t numBlocks, uint32 blockSize)
113 {
114 	return fBlockCache = block_cache_create(fDevice, numBlocks, blockSize,
115 											IsReadOnly());
116 }
117 
118 
119 void
120 DeviceOpener::RemoveCache(bool allowWrites)
121 {
122 	if (fBlockCache == NULL)
123 		return;
124 
125 	block_cache_delete(fBlockCache, allowWrites);
126 	fBlockCache = NULL;
127 }
128 
129 
130 void
131 DeviceOpener::Keep()
132 {
133 	fDevice = -1;
134 }
135 
136 
137 /*!	Returns the size of the device in bytes. It uses B_GET_GEOMETRY
138 	to compute the size, or fstat() if that failed.
139 */
140 status_t
141 DeviceOpener::GetSize(off_t *_size, uint32 *_blockSize)
142 {
143 	device_geometry geometry;
144 	if (ioctl(fDevice, B_GET_GEOMETRY, &geometry) < 0) {
145 		// maybe it's just a file
146 		struct stat stat;
147 		if (fstat(fDevice, &stat) < 0)
148 			return B_ERROR;
149 
150 		if (_size)
151 			*_size = stat.st_size;
152 		if (_blockSize)				// that shouldn't cause us any problems
153 			*_blockSize = 512;
154 
155 		return B_OK;
156 	}
157 
158 	if (_size) {
159 		*_size = 1LL * geometry.head_count * geometry.cylinder_count
160 					* geometry.sectors_per_track * geometry.bytes_per_sector;
161 	}
162 	if (_blockSize)
163 		*_blockSize = geometry.bytes_per_sector;
164 
165 	return B_OK;
166 }
167 
168 
169 Volume::Volume(fs_volume *volume)
170     : fFSVolume(volume)
171 {
172 	fFlags = 0;
173 	mutex_init(&fLock, "xfs volume");
174 	TRACE("Volume::Volume() : Initialising volume");
175 }
176 
177 
178 Volume::~Volume()
179 {
180 	mutex_destroy(&fLock);
181 	TRACE("Volume::Destructor : Removing Volume");
182 }
183 
184 
185 bool
186 Volume::IsValidSuperBlock()
187 {
188 	return fSuperBlock.IsValid();
189 }
190 
191 
192 status_t
193 Volume::Identify(int fd, XfsSuperBlock *superBlock)
194 {
195 
196 	TRACE("Volume::Identify() : Identifying Volume in progress");
197 
198 	if (read_pos(fd, 0, superBlock, sizeof(XfsSuperBlock))
199 		!= sizeof(XfsSuperBlock))
200 			return B_IO_ERROR;
201 
202 	superBlock->SwapEndian();
203 
204 	if (!superBlock->IsValid()) {
205 		ERROR("Volume::Identify(): Invalid Superblock!\n");
206 		return B_BAD_VALUE;
207 	}
208 	return B_OK;
209 }
210 
211 
212 status_t
213 Volume::Mount(const char *deviceName, uint32 flags)
214 {
215 	TRACE("Volume::Mount() : Mounting in progress");
216 
217 	flags |= B_MOUNT_READ_ONLY;
218 
219 	if ((flags & B_MOUNT_READ_ONLY) != 0) {
220 		TRACE("Volume::Mount(): Read only\n");
221 	} else {
222 		TRACE("Volume::Mount(): Read write\n");
223 	}
224 
225 	DeviceOpener opener(deviceName, (flags & B_MOUNT_READ_ONLY) != 0
226 										? O_RDONLY
227  										: O_RDWR);
228 	fDevice = opener.Device();
229 	if (fDevice < B_OK) {
230 		ERROR("Volume::Mount(): couldn't open device\n");
231 		return fDevice;
232 	}
233 
234 	if (opener.IsReadOnly())
235 		fFlags |= VOLUME_READ_ONLY;
236 
237 	// read the superblock
238 	status_t status = Identify(fDevice, &fSuperBlock);
239 	if (status != B_OK) {
240 		ERROR("Volume::Mount(): Invalid super block!\n");
241 		return B_BAD_VALUE;
242 	}
243 
244 	TRACE("Volume::Mount(): Valid SuperBlock.\n");
245 
246 	// check if the device size is large enough to hold the file system
247 	off_t diskSize;
248 	if (opener.GetSize(&diskSize) != B_OK) {
249 		ERROR("Volume:Mount() Unable to get diskSize");
250 		return B_ERROR;
251 	}
252 
253 	opener.Keep();
254 	return B_OK;
255 }
256 
257 
258 status_t
259 Volume::Unmount()
260 {
261 	TRACE("Volume::Unmount(): Unmounting");
262 
263 	TRACE("Volume::Unmount(): Closing device");
264 	close(fDevice);
265 
266 	return B_OK;
267 }
268