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 // debugging 23 //#define DBG(x) 24 #define DBG(x) x 25 #define OUT dprintf 26 27 28 static const char* kFileDevicesDir = "/dev/disk/virtual/files"; 29 30 31 KFileDiskDevice::KFileDiskDevice(partition_id id) 32 : 33 KDiskDevice(id), 34 fFilePath(NULL) 35 { 36 SetDeviceFlags(DeviceFlags() | B_DISK_DEVICE_IS_FILE); 37 } 38 39 40 KFileDiskDevice::~KFileDiskDevice() 41 { 42 Unset(); 43 } 44 45 46 status_t 47 KFileDiskDevice::SetTo(const char* filePath, const char* devicePath) 48 { 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; 53 } 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, true); 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; 94 } 95 error = set_string(fFilePath, filePath); 96 if (error != B_OK) 97 return error; 98 99 error = KDiskDevice::SetTo(devicePath); 100 if (error != B_OK) 101 return error; 102 103 // reset the B_DISK_DEVICE_IS_FILE flag -- KDiskDevice::SetTo() has cleared 104 // it 105 SetDeviceFlags(DeviceFlags() | B_DISK_DEVICE_IS_FILE); 106 107 return B_OK; 108 } 109 110 111 void 112 KFileDiskDevice::Unset() 113 { 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()); 121 } 122 // free file path 123 free(fFilePath); 124 fFilePath = NULL; 125 } 126 127 128 status_t 129 KFileDiskDevice::InitCheck() const 130 { 131 return KDiskDevice::InitCheck(); 132 } 133 134 135 const char* 136 KFileDiskDevice::FilePath() const 137 { 138 return fFilePath; 139 } 140 141 142 status_t 143 KFileDiskDevice::GetMediaStatus(status_t* mediaStatus) 144 { 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; 152 } 153 154 155 status_t 156 KFileDiskDevice::GetGeometry(device_geometry* geometry) 157 { 158 // check the file 159 struct stat st; 160 if (stat(fFilePath, &st) != 0 || !S_ISREG(st.st_mode)) 161 return B_BAD_VALUE; 162 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; 182 183 return B_OK; 184 } 185 186 187 status_t 188 KFileDiskDevice::_RegisterDevice(const char* file, const char* device) 189 { 190 return devfs_publish_file_device(device + 5, file); 191 // we need to remove the "/dev/" part from the path 192 } 193 194 195 status_t 196 KFileDiskDevice::_UnregisterDevice(const char* _device) 197 { 198 return devfs_unpublish_file_device(_device + 5); 199 // we need to remove the "/dev/" part from the path 200 } 201 202 203 status_t 204 KFileDiskDevice::_GetDirectoryPath(partition_id id, KPath* path) 205 { 206 if (path == NULL) 207 return B_BAD_VALUE; 208 209 if (path->InitCheck() != B_OK) 210 return path->InitCheck(); 211 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); 217 } 218 return error; 219 } 220 221