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 #include "virtualdrive.h" 17 18 // debugging 19 //#define DBG(x) 20 #define DBG(x) x 21 #define OUT dprintf 22 23 static const char *kFileDevicesDir = "/dev/disk/virtual/files"; 24 25 26 // constructor 27 KFileDiskDevice::KFileDiskDevice(partition_id id) 28 : KDiskDevice(id), 29 fFilePath(NULL) 30 { 31 } 32 33 // destructor 34 KFileDiskDevice::~KFileDiskDevice() 35 { 36 Unset(); 37 } 38 39 // SetTo 40 status_t 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, true); 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) { 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 return KDiskDevice::SetTo(devicePath); 93 } 94 95 // Unset 96 void 97 KFileDiskDevice::Unset() 98 { 99 // remove the device and the directory it resides in 100 if (Path() && ID() >= 0) { 101 _UnregisterDevice(Path()); 102 // TODO: Cleanup. The devfs will automatically remove the directory. 103 // KPath dirPath; 104 // if (_GetDirectoryPath(ID(), &dirPath) == B_OK) 105 // rmdir(dirPath.Path()); 106 } 107 // free file path 108 free(fFilePath); 109 fFilePath = NULL; 110 } 111 112 // InitCheck 113 status_t 114 KFileDiskDevice::InitCheck() const 115 { 116 return KDiskDevice::InitCheck(); 117 } 118 119 // FilePath 120 const char * 121 KFileDiskDevice::FilePath() const 122 { 123 return fFilePath; 124 } 125 126 // GetMediaStatus 127 status_t 128 KFileDiskDevice::GetMediaStatus(status_t *mediaStatus) 129 { 130 // check the file 131 struct stat st; 132 if (stat(fFilePath, &st) == 0 && S_ISREG(st.st_mode)) 133 *mediaStatus = B_OK; 134 else 135 *mediaStatus = B_DEV_NO_MEDIA; 136 return B_OK; 137 } 138 139 // GetGeometry 140 status_t 141 KFileDiskDevice::GetGeometry(device_geometry *geometry) 142 { 143 // check the file 144 struct stat st; 145 if (stat(fFilePath, &st) != 0 || !S_ISREG(st.st_mode)) 146 return B_BAD_VALUE; 147 148 // fill in the geometry 149 // default to 512 bytes block size 150 uint32 blockSize = 512; 151 // Optimally we have only 1 block per sector and only one head. 152 // Since we have only a uint32 for the cylinder count, this won't work 153 // for files > 2TB. So, we set the head count to the minimally possible 154 // value. 155 off_t blocks = st.st_size / blockSize; 156 uint32 heads = (blocks + ULONG_MAX - 1) / ULONG_MAX; 157 if (heads == 0) 158 heads = 1; 159 geometry->bytes_per_sector = blockSize; 160 geometry->sectors_per_track = 1; 161 geometry->cylinder_count = blocks / heads; 162 geometry->head_count = heads; 163 geometry->device_type = B_DISK; // TODO: Add a new constant. 164 geometry->removable = false; 165 geometry->read_only = false; 166 geometry->write_once = false; 167 168 return B_OK; 169 170 } 171 172 // _RegisterDevice 173 status_t 174 KFileDiskDevice::_RegisterDevice(const char *file, const char *device) 175 { 176 return devfs_publish_file_device(device + 5, file); 177 // we need to remove the "/dev/" part from the path 178 179 // TODO: For now we use the virtualdrive driver to register a file 180 // as a device and then simply symlink the assigned device to the 181 // desired device location. Replace that with the 182 // respective kernel magic for the OBOS kernel! 183 // -> Well, we could simply symlink the file there. Doesn't work for R5, 184 // but we should be able to deal with it properly. 185 // 186 // // open the virtualdrive control device 187 // int fd = open(VIRTUAL_DRIVE_CONTROL_DEVICE, O_RDONLY); 188 // if (fd < 0) { 189 // DBG(OUT("Failed to open virtualdrive control device: %s\n", 190 // strerror(errno))); 191 // return errno; 192 // } 193 // // set up the info 194 // virtual_drive_info info; 195 // strcpy(info.file_name, file); 196 // info.use_geometry = false; 197 // status_t error = B_OK; 198 // if (ioctl(fd, VIRTUAL_DRIVE_REGISTER_FILE, &info) != 0) { 199 // error = errno; 200 // DBG(OUT("Failed to install file device: %s\n", strerror(error))); 201 // } 202 // // close the control device 203 // close(fd); 204 // // create a symlink 205 // if (error == B_OK) { 206 // if (symlink(info.device_name, device) != 0) { 207 // DBG(OUT("Failed to create file device symlink: %s\n", 208 // strerror(error))); 209 // error = errno; 210 // // unregister the file device 211 // // open the device 212 // int deviceFD = open(info.device_name, O_RDONLY); 213 // if (deviceFD >= 0) { 214 // ioctl(deviceFD, VIRTUAL_DRIVE_UNREGISTER_FILE); 215 // close(deviceFD); 216 // } 217 // } 218 // } 219 // return error; 220 } 221 222 // _UnregisterDevice 223 status_t 224 KFileDiskDevice::_UnregisterDevice(const char *_device) 225 { 226 return devfs_unpublish_file_device(_device + 5); 227 // we need to remove the "/dev/" part from the path 228 229 // // read the symlink to get the path of the virtualdrive device 230 // char device[B_PATH_NAME_LENGTH]; 231 // ssize_t bytesRead = readlink(_device, device, sizeof(device) - 1); 232 // if (bytesRead < 0) 233 // return errno; 234 // device[bytesRead] = '\0'; 235 // // open the device 236 // int fd = open(device, O_RDONLY); 237 // if (fd < 0) 238 // return errno; 239 // // issue the ioctl 240 // status_t error = B_OK; 241 // if (ioctl(fd, VIRTUAL_DRIVE_UNREGISTER_FILE) != 0) 242 // error = errno; 243 // // close the control device 244 // close(fd); 245 // // remove the symlink 246 // if (error == B_OK) { 247 // if (remove(_device) < 0) 248 // error = errno; 249 // } 250 // return error; 251 } 252 253 // _GetDirectoryPath 254 status_t 255 KFileDiskDevice::_GetDirectoryPath(partition_id id, KPath *path) 256 { 257 if (!path || path->InitCheck() != B_OK) 258 return path->InitCheck(); 259 status_t error = path->SetPath(kFileDevicesDir); 260 if (error == B_OK) { 261 char idBuffer[12]; 262 sprintf(idBuffer, "%ld", id); 263 error = path->Append(idBuffer); 264 } 265 return error; 266 } 267 268