xref: /haiku/src/system/kernel/disk_device_manager/KFileDiskDevice.cpp (revision 220d04022750f40f8bac8f01fa551211e28d04f2)
1 /*
2  * Copyright 2003-2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include <KFileDiskDevice.h>
8 
9 #include <errno.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <unistd.h>
13 
14 #include <devfs.h>
15 #include <Drivers.h>
16 #include <KernelExport.h>
17 
18 #include <KDiskDeviceUtils.h>
19 #include <KPath.h>
20 
21 
22 // debugging
23 //#define DBG(x)
24 #define DBG(x) x
25 #define OUT dprintf
26 
27 
28 static const char* kFileDevicesDir = "/dev/disk/virtual/files";
29 
30 
31 KFileDiskDevice::KFileDiskDevice(partition_id id)
32 	:
33 	KDiskDevice(id),
34 	fFilePath(NULL)
35 {
36 	SetDeviceFlags(DeviceFlags() | B_DISK_DEVICE_IS_FILE);
37 }
38 
39 
40 KFileDiskDevice::~KFileDiskDevice()
41 {
42 	Unset();
43 }
44 
45 
46 status_t
47 KFileDiskDevice::SetTo(const char* filePath, const char* devicePath)
48 {
49 	// check params
50 	if (!filePath || strlen(filePath) > B_PATH_NAME_LENGTH
51 		|| (devicePath && strlen(devicePath) > B_PATH_NAME_LENGTH)) {
52 		return B_BAD_VALUE;
53 	}
54 	// normalize the file path
55 	// (should actually not be necessary, since this method is only invoked
56 	// by the DDM, which has already normalized the path)
57 	KPath tmpFilePath;
58 	status_t error = tmpFilePath.SetTo(filePath, true);
59 	if (error != B_OK)
60 		return error;
61 	// check the file
62 	struct stat st;
63 	if (stat(filePath, &st) != 0)
64 		return errno;
65 	if (!S_ISREG(st.st_mode))
66 		return B_BAD_VALUE;
67 	// create the device, if requested
68 	KPath tmpDevicePath;
69 	if (devicePath == NULL) {
70 		// no device path: we shall create a new device entry
71 		if (tmpDevicePath.InitCheck() != B_OK)
72 			return tmpDevicePath.InitCheck();
73 // TODO: Cleanup. The directory creation is done automatically by the devfs.
74 //		// make the file devices dir
75 //		if (mkdir(kFileDevicesDir, 0777) != 0) {
76 //			if (errno != B_FILE_EXISTS)
77 //				return errno;
78 //		}
79 		// make the directory
80 		status_t error = _GetDirectoryPath(ID(), &tmpDevicePath);
81 		if (error != B_OK)
82 			return error;
83 //		if (mkdir(tmpDevicePath.Path(), 0777) != 0)
84 //			return errno;
85 		// get the device path name
86 		error = tmpDevicePath.Append("raw");
87 		if (error != B_OK)
88 			return error;
89 		devicePath = tmpDevicePath.Path();
90 		// register the file as virtual disk device
91 		error = _RegisterDevice(filePath, devicePath);
92 		if (error != B_OK)
93 			return error;
94 	}
95 	error = set_string(fFilePath, filePath);
96 	if (error != B_OK)
97 		return error;
98 
99 	error = KDiskDevice::SetTo(devicePath);
100 	if (error != B_OK)
101 		return error;
102 
103 	// reset the B_DISK_DEVICE_IS_FILE flag -- KDiskDevice::SetTo() has cleared
104 	// it
105 	SetDeviceFlags(DeviceFlags() | B_DISK_DEVICE_IS_FILE);
106 
107 	return B_OK;
108 }
109 
110 
111 void
112 KFileDiskDevice::Unset()
113 {
114 	// remove the device and the directory it resides in
115 	if (Path() && ID() >= 0) {
116 		_UnregisterDevice(Path());
117 // TODO: Cleanup. The devfs will automatically remove the directory.
118 //		KPath dirPath;
119 //		if (_GetDirectoryPath(ID(), &dirPath) == B_OK)
120 //			rmdir(dirPath.Path());
121 	}
122 	// free file path
123 	free(fFilePath);
124 	fFilePath = NULL;
125 }
126 
127 
128 status_t
129 KFileDiskDevice::InitCheck() const
130 {
131 	return KDiskDevice::InitCheck();
132 }
133 
134 
135 const char*
136 KFileDiskDevice::FilePath() const
137 {
138 	return fFilePath;
139 }
140 
141 
142 status_t
143 KFileDiskDevice::GetMediaStatus(status_t* mediaStatus)
144 {
145 	// check the file
146 	struct stat st;
147 	if (stat(fFilePath, &st) == 0 && S_ISREG(st.st_mode))
148 		*mediaStatus = B_OK;
149 	else
150 		*mediaStatus = B_DEV_NO_MEDIA;
151 	return B_OK;
152 }
153 
154 
155 status_t
156 KFileDiskDevice::GetGeometry(device_geometry* geometry)
157 {
158 	// check the file
159 	struct stat st;
160 	if (stat(fFilePath, &st) != 0 || !S_ISREG(st.st_mode))
161 		return B_BAD_VALUE;
162 
163 	// fill in the geometry
164 	// default to 512 bytes block size
165 	uint32 blockSize = 512;
166 	// Optimally we have only 1 block per sector and only one head.
167 	// Since we have only a uint32 for the cylinder count, this won't work
168 	// for files > 2TB. So, we set the head count to the minimally possible
169 	// value.
170 	off_t blocks = st.st_size / blockSize;
171 	uint32 heads = (blocks + ULONG_MAX - 1) / ULONG_MAX;
172 	if (heads == 0)
173 		heads = 1;
174 	geometry->bytes_per_sector = blockSize;
175 	geometry->sectors_per_track = 1;
176 	geometry->cylinder_count = blocks / heads;
177 	geometry->head_count = heads;
178 	geometry->device_type = B_DISK;	// TODO: Add a new constant.
179 	geometry->removable = false;
180 	geometry->read_only = false;
181 	geometry->write_once = false;
182 
183 	return B_OK;
184 }
185 
186 
187 status_t
188 KFileDiskDevice::_RegisterDevice(const char* file, const char* device)
189 {
190 	return devfs_publish_file_device(device + 5, file);
191 		// we need to remove the "/dev/" part from the path
192 }
193 
194 
195 status_t
196 KFileDiskDevice::_UnregisterDevice(const char* _device)
197 {
198 	return devfs_unpublish_file_device(_device + 5);
199 		// we need to remove the "/dev/" part from the path
200 }
201 
202 
203 status_t
204 KFileDiskDevice::_GetDirectoryPath(partition_id id, KPath* path)
205 {
206 	if (path == NULL)
207 		return B_BAD_VALUE;
208 
209 	if (path->InitCheck() != B_OK)
210 		return path->InitCheck();
211 
212 	status_t error = path->SetPath(kFileDevicesDir);
213 	if (error == B_OK) {
214 		char idBuffer[12];
215 		snprintf(idBuffer, sizeof(idBuffer), "%" B_PRId32, id);
216 		error = path->Append(idBuffer);
217 	}
218 	return error;
219 }
220 
221