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
28static const char* kFileDevicesDir = "/dev/disk/virtual/files";
29
30
31KFileDiskDevice::KFileDiskDevice(partition_id id)
32	:
33	KDiskDevice(id),
34	fFilePath(NULL)
35{
36	SetDeviceFlags(DeviceFlags() | B_DISK_DEVICE_IS_FILE);
37}
38
39
40KFileDiskDevice::~KFileDiskDevice()
41{
42	Unset();
43}
44
45
46status_t
47KFileDiskDevice::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
111void
112KFileDiskDevice::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
128status_t
129KFileDiskDevice::InitCheck() const
130{
131	return KDiskDevice::InitCheck();
132}
133
134
135const char*
136KFileDiskDevice::FilePath() const
137{
138	return fFilePath;
139}
140
141
142status_t
143KFileDiskDevice::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
155status_t
156KFileDiskDevice::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
187status_t
188KFileDiskDevice::_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
195status_t
196KFileDiskDevice::_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
203status_t
204KFileDiskDevice::_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