headers/bsd: Add sys/queue.h.
[haiku.git] / src / system / kernel / disk_device_manager / KFileDiskDevice.cpp
blob9255905b7111caeaa6e16a85bb3ed67b0163b7dd
1 /*
2 * Copyright 2003-2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
7 #include <KFileDiskDevice.h>
9 #include <errno.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <unistd.h>
14 #include <devfs.h>
15 #include <Drivers.h>
16 #include <KernelExport.h>
18 #include <KDiskDeviceUtils.h>
19 #include <KPath.h>
22 // debugging
23 //#define DBG(x)
24 #define DBG(x) x
25 #define OUT dprintf
28 static const char* kFileDevicesDir = "/dev/disk/virtual/files";
31 KFileDiskDevice::KFileDiskDevice(partition_id id)
33 KDiskDevice(id),
34 fFilePath(NULL)
36 SetDeviceFlags(DeviceFlags() | B_DISK_DEVICE_IS_FILE);
40 KFileDiskDevice::~KFileDiskDevice()
42 Unset();
46 status_t
47 KFileDiskDevice::SetTo(const char* filePath, const char* devicePath)
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;
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, KPath::NORMALIZE);
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;
95 error = set_string(fFilePath, filePath);
96 if (error != B_OK)
97 return error;
99 error = KDiskDevice::SetTo(devicePath);
100 if (error != B_OK)
101 return error;
103 // reset the B_DISK_DEVICE_IS_FILE flag -- KDiskDevice::SetTo() has cleared
104 // it
105 SetDeviceFlags(DeviceFlags() | B_DISK_DEVICE_IS_FILE);
107 return B_OK;
111 void
112 KFileDiskDevice::Unset()
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());
122 // free file path
123 free(fFilePath);
124 fFilePath = NULL;
128 status_t
129 KFileDiskDevice::InitCheck() const
131 return KDiskDevice::InitCheck();
135 const char*
136 KFileDiskDevice::FilePath() const
138 return fFilePath;
142 status_t
143 KFileDiskDevice::GetMediaStatus(status_t* mediaStatus)
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;
155 status_t
156 KFileDiskDevice::GetGeometry(device_geometry* geometry)
158 // check the file
159 struct stat st;
160 if (stat(fFilePath, &st) != 0 || !S_ISREG(st.st_mode))
161 return B_BAD_VALUE;
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;
183 return B_OK;
187 status_t
188 KFileDiskDevice::_RegisterDevice(const char* file, const char* device)
190 return devfs_publish_file_device(device + 5, file);
191 // we need to remove the "/dev/" part from the path
195 status_t
196 KFileDiskDevice::_UnregisterDevice(const char* _device)
198 return devfs_unpublish_file_device(_device + 5);
199 // we need to remove the "/dev/" part from the path
203 status_t
204 KFileDiskDevice::_GetDirectoryPath(partition_id id, KPath* path)
206 if (path == NULL)
207 return B_BAD_VALUE;
209 if (path->InitCheck() != B_OK)
210 return path->InitCheck();
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);
218 return error;