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 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 34 KFileDiskDevice::~KFileDiskDevice() 35 { 36 Unset(); 37 } 38 39 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, 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 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 123 KFileDiskDevice::InitCheck() const 124 { 125 return KDiskDevice::InitCheck(); 126 } 127 128 129 const char* 130 KFileDiskDevice::FilePath() const 131 { 132 return fFilePath; 133 } 134 135 136 status_t 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 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 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 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 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