xref: /haiku/src/system/kernel/disk_device_manager/KFileDiskDevice.cpp (revision 5d78c2d7e03d20c54b9c854da8a8c6dac74418be)
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 static const char* kFileDevicesDir = "/dev/disk/virtual/files";
23 
24 
KFileDiskDevice(partition_id id)25 KFileDiskDevice::KFileDiskDevice(partition_id id)
26 	:
27 	KDiskDevice(id),
28 	fFilePath(NULL)
29 {
30 	SetDeviceFlags(DeviceFlags() | B_DISK_DEVICE_IS_FILE);
31 }
32 
33 
~KFileDiskDevice()34 KFileDiskDevice::~KFileDiskDevice()
35 {
36 	Unset();
37 }
38 
39 
40 status_t
SetTo(const char * filePath,const char * devicePath)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, KPath::NORMALIZE);
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 == NULL) {
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 
93 	error = KDiskDevice::SetTo(devicePath);
94 	if (error != B_OK)
95 		return error;
96 
97 	// reset the B_DISK_DEVICE_IS_FILE flag -- KDiskDevice::SetTo() has cleared
98 	// it
99 	SetDeviceFlags(DeviceFlags() | B_DISK_DEVICE_IS_FILE);
100 
101 	return B_OK;
102 }
103 
104 
105 void
Unset()106 KFileDiskDevice::Unset()
107 {
108 	// remove the device and the directory it resides in
109 	if (Path() && ID() >= 0) {
110 		_UnregisterDevice(Path());
111 // TODO: Cleanup. The devfs will automatically remove the directory.
112 //		KPath dirPath;
113 //		if (_GetDirectoryPath(ID(), &dirPath) == B_OK)
114 //			rmdir(dirPath.Path());
115 	}
116 	// free file path
117 	free(fFilePath);
118 	fFilePath = NULL;
119 }
120 
121 
122 status_t
InitCheck() const123 KFileDiskDevice::InitCheck() const
124 {
125 	return KDiskDevice::InitCheck();
126 }
127 
128 
129 const char*
FilePath() const130 KFileDiskDevice::FilePath() const
131 {
132 	return fFilePath;
133 }
134 
135 
136 status_t
GetMediaStatus(status_t * mediaStatus)137 KFileDiskDevice::GetMediaStatus(status_t* mediaStatus)
138 {
139 	// check the file
140 	struct stat st;
141 	if (stat(fFilePath, &st) == 0 && S_ISREG(st.st_mode))
142 		*mediaStatus = B_OK;
143 	else
144 		*mediaStatus = B_DEV_NO_MEDIA;
145 	return B_OK;
146 }
147 
148 
149 status_t
GetGeometry(device_geometry * geometry)150 KFileDiskDevice::GetGeometry(device_geometry* geometry)
151 {
152 	// check the file
153 	struct stat st;
154 	if (stat(fFilePath, &st) != 0 || !S_ISREG(st.st_mode))
155 		return B_BAD_VALUE;
156 
157 	// fill in the geometry
158 	// default to 512 bytes block size
159 	uint32 blockSize = 512;
160 	// Optimally we have only 1 block per sector and only one head.
161 	// Since we have only a uint32 for the cylinder count, this won't work
162 	// for files > 2TB. So, we set the head count to the minimally possible
163 	// value.
164 	off_t blocks = st.st_size / blockSize;
165 	uint32 heads = (blocks + ULONG_MAX - 1) / ULONG_MAX;
166 	if (heads == 0)
167 		heads = 1;
168 	geometry->bytes_per_sector = blockSize;
169 	geometry->sectors_per_track = 1;
170 	geometry->cylinder_count = blocks / heads;
171 	geometry->head_count = heads;
172 	geometry->device_type = B_DISK;	// TODO: Add a new constant.
173 	geometry->removable = false;
174 	geometry->read_only = false;
175 	geometry->write_once = false;
176 
177 	return B_OK;
178 }
179 
180 
181 status_t
_RegisterDevice(const char * file,const char * device)182 KFileDiskDevice::_RegisterDevice(const char* file, const char* device)
183 {
184 	return devfs_publish_file_device(device + 5, file);
185 		// we need to remove the "/dev/" part from the path
186 }
187 
188 
189 status_t
_UnregisterDevice(const char * _device)190 KFileDiskDevice::_UnregisterDevice(const char* _device)
191 {
192 	return devfs_unpublish_file_device(_device + 5);
193 		// we need to remove the "/dev/" part from the path
194 }
195 
196 
197 status_t
_GetDirectoryPath(partition_id id,KPath * path)198 KFileDiskDevice::_GetDirectoryPath(partition_id id, KPath* path)
199 {
200 	if (path == NULL)
201 		return B_BAD_VALUE;
202 
203 	if (path->InitCheck() != B_OK)
204 		return path->InitCheck();
205 
206 	status_t error = path->SetPath(kFileDevicesDir);
207 	if (error == B_OK) {
208 		char idBuffer[12];
209 		snprintf(idBuffer, sizeof(idBuffer), "%" B_PRId32, id);
210 		error = path->Append(idBuffer);
211 	}
212 	return error;
213 }
214 
215