xref: /haiku/src/system/kernel/disk_device_manager/KFileDiskDevice.cpp (revision d3d8b26997fac34a84981e6d2b649521de2cc45a)
1 // KFileDiskDevice.cpp
2 
3 #include <errno.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <unistd.h>
7 
8 #include <KernelExport.h>
9 #include <Drivers.h>
10 #include <devfs.h>
11 
12 #include <KDiskDeviceUtils.h>
13 #include <KFileDiskDevice.h>
14 #include <KPath.h>
15 
16 #include "virtualdrive.h"
17 
18 // debugging
19 //#define DBG(x)
20 #define DBG(x) x
21 #define OUT dprintf
22 
23 static const char *kFileDevicesDir = "/dev/disk/virtual/files";
24 
25 
26 // constructor
27 KFileDiskDevice::KFileDiskDevice(partition_id id)
28 	: KDiskDevice(id),
29 	  fFilePath(NULL)
30 {
31 }
32 
33 // destructor
34 KFileDiskDevice::~KFileDiskDevice()
35 {
36 	Unset();
37 }
38 
39 // SetTo
40 status_t
41 KFileDiskDevice::SetTo(const char *filePath, const char *devicePath)
42 {
43 	// check params
44 	if (!filePath || strlen(filePath) > B_PATH_NAME_LENGTH
45 		|| (devicePath && strlen(devicePath) > B_PATH_NAME_LENGTH)) {
46 		return B_BAD_VALUE;
47 	}
48 	// normalize the file path
49 	// (should actually not be necessary, since this method is only invoked
50 	// by the DDM, which has already normalized the path)
51 	KPath tmpFilePath;
52 	status_t error = tmpFilePath.SetTo(filePath, true);
53 	if (error != B_OK)
54 		return error;
55 	// check the file
56 	struct stat st;
57 	if (stat(filePath, &st) != 0)
58 		return errno;
59 	if (!S_ISREG(st.st_mode))
60 		return B_BAD_VALUE;
61 	// create the device, if requested
62 	KPath tmpDevicePath;
63 	if (!devicePath) {
64 		// no device path: we shall create a new device entry
65 		if (tmpDevicePath.InitCheck() != B_OK)
66 			return tmpDevicePath.InitCheck();
67 // TODO: Cleanup. The directory creation is done automatically by the devfs.
68 //		// make the file devices dir
69 //		if (mkdir(kFileDevicesDir, 0777) != 0) {
70 //			if (errno != B_FILE_EXISTS)
71 //				return errno;
72 //		}
73 		// make the directory
74 		status_t error = _GetDirectoryPath(ID(), &tmpDevicePath);
75 		if (error != B_OK)
76 			return error;
77 //		if (mkdir(tmpDevicePath.Path(), 0777) != 0)
78 //			return errno;
79 		// get the device path name
80 		error = tmpDevicePath.Append("raw");
81 		if (error != B_OK)
82 			return error;
83 		devicePath = tmpDevicePath.Path();
84 		// register the file as virtual disk device
85 		error = _RegisterDevice(filePath, devicePath);
86 		if (error != B_OK)
87 			return error;
88 	}
89 	error = set_string(fFilePath, filePath);
90 	if (error != B_OK)
91 		return error;
92 	return KDiskDevice::SetTo(devicePath);
93 }
94 
95 // Unset
96 void
97 KFileDiskDevice::Unset()
98 {
99 	// remove the device and the directory it resides in
100 	if (Path() && ID() >= 0) {
101 		_UnregisterDevice(Path());
102 // TODO: Cleanup. The devfs will automatically remove the directory.
103 //		KPath dirPath;
104 //		if (_GetDirectoryPath(ID(), &dirPath) == B_OK)
105 //			rmdir(dirPath.Path());
106 	}
107 	// free file path
108 	free(fFilePath);
109 	fFilePath = NULL;
110 }
111 
112 // InitCheck
113 status_t
114 KFileDiskDevice::InitCheck() const
115 {
116 	return KDiskDevice::InitCheck();
117 }
118 
119 // FilePath
120 const char *
121 KFileDiskDevice::FilePath() const
122 {
123 	return fFilePath;
124 }
125 
126 // GetMediaStatus
127 status_t
128 KFileDiskDevice::GetMediaStatus(status_t *mediaStatus)
129 {
130 	// check the file
131 	struct stat st;
132 	if (stat(fFilePath, &st) == 0 && S_ISREG(st.st_mode))
133 		*mediaStatus = B_OK;
134 	else
135 		*mediaStatus = B_DEV_NO_MEDIA;
136 	return B_OK;
137 }
138 
139 // GetGeometry
140 status_t
141 KFileDiskDevice::GetGeometry(device_geometry *geometry)
142 {
143 	// check the file
144 	struct stat st;
145 	if (stat(fFilePath, &st) != 0 || !S_ISREG(st.st_mode))
146 		return B_BAD_VALUE;
147 
148 	// fill in the geometry
149 	// default to 512 bytes block size
150 	uint32 blockSize = 512;
151 	// Optimally we have only 1 block per sector and only one head.
152 	// Since we have only a uint32 for the cylinder count, this won't work
153 	// for files > 2TB. So, we set the head count to the minimally possible
154 	// value.
155 	off_t blocks = st.st_size / blockSize;
156 	uint32 heads = (blocks + ULONG_MAX - 1) / ULONG_MAX;
157 	if (heads == 0)
158 		heads = 1;
159 	geometry->bytes_per_sector = blockSize;
160 	geometry->sectors_per_track = 1;
161 	geometry->cylinder_count = blocks / heads;
162 	geometry->head_count = heads;
163 	geometry->device_type = B_DISK;	// TODO: Add a new constant.
164 	geometry->removable = false;
165 	geometry->read_only = false;
166 	geometry->write_once = false;
167 
168 	return B_OK;
169 
170 }
171 
172 // _RegisterDevice
173 status_t
174 KFileDiskDevice::_RegisterDevice(const char *file, const char *device)
175 {
176 	return devfs_publish_file_device(device + 5, file);
177 		// we need to remove the "/dev/" part from the path
178 
179 	// TODO: For now we use the virtualdrive driver to register a file
180 	// as a device and then simply symlink the assigned device to the
181 	// desired device location. Replace that with the
182 	// respective kernel magic for the OBOS kernel!
183 	// -> Well, we could simply symlink the file there. Doesn't work for R5,
184 	// but we should be able to deal with it properly.
185 //
186 //	// open the virtualdrive control device
187 //	int fd = open(VIRTUAL_DRIVE_CONTROL_DEVICE, O_RDONLY);
188 //	if (fd < 0) {
189 //		DBG(OUT("Failed to open virtualdrive control device: %s\n",
190 //				strerror(errno)));
191 //		return errno;
192 //	}
193 //	// set up the info
194 //	virtual_drive_info info;
195 //	strcpy(info.file_name, file);
196 //	info.use_geometry = false;
197 //	status_t error = B_OK;
198 //	if (ioctl(fd, VIRTUAL_DRIVE_REGISTER_FILE, &info) != 0) {
199 //		error = errno;
200 //		DBG(OUT("Failed to install file device: %s\n", strerror(error)));
201 //	}
202 //	// close the control device
203 //	close(fd);
204 //	// create a symlink
205 //	if (error == B_OK) {
206 //		if (symlink(info.device_name, device) != 0) {
207 //			DBG(OUT("Failed to create file device symlink: %s\n",
208 //					strerror(error)));
209 //			error = errno;
210 //			// unregister the file device
211 //			// open the device
212 //			int deviceFD = open(info.device_name, O_RDONLY);
213 //			if (deviceFD >= 0) {
214 //				ioctl(deviceFD, VIRTUAL_DRIVE_UNREGISTER_FILE);
215 //				close(deviceFD);
216 //			}
217 //		}
218 //	}
219 //	return error;
220 }
221 
222 // _UnregisterDevice
223 status_t
224 KFileDiskDevice::_UnregisterDevice(const char *_device)
225 {
226 	return devfs_unpublish_file_device(_device + 5);
227 		// we need to remove the "/dev/" part from the path
228 
229 //	// read the symlink to get the path of the virtualdrive device
230 //	char device[B_PATH_NAME_LENGTH];
231 //	ssize_t bytesRead = readlink(_device, device, sizeof(device) - 1);
232 //	if (bytesRead < 0)
233 //		return errno;
234 //	device[bytesRead] = '\0';
235 //	// open the device
236 //	int fd = open(device, O_RDONLY);
237 //	if (fd < 0)
238 //		return errno;
239 //	// issue the ioctl
240 //	status_t error = B_OK;
241 //	if (ioctl(fd, VIRTUAL_DRIVE_UNREGISTER_FILE) != 0)
242 //		error = errno;
243 //	// close the control device
244 //	close(fd);
245 //	// remove the symlink
246 //	if (error == B_OK) {
247 //		if (remove(_device) < 0)
248 //			error = errno;
249 //	}
250 //	return error;
251 }
252 
253 // _GetDirectoryPath
254 status_t
255 KFileDiskDevice::_GetDirectoryPath(partition_id id, KPath *path)
256 {
257 	if (!path || path->InitCheck() != B_OK)
258 		return path->InitCheck();
259 	status_t error = path->SetPath(kFileDevicesDir);
260 	if (error == B_OK) {
261 		char idBuffer[12];
262 		sprintf(idBuffer, "%ld", id);
263 		error = path->Append(idBuffer);
264 	}
265 	return error;
266 }
267 
268