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