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