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 17 // debugging 18 //#define DBG(x) 19 #define DBG(x) x 20 #define OUT dprintf 21 22 static const char *kFileDevicesDir = "/dev/disk/virtual/files"; 23 24 25 // constructor 26 KFileDiskDevice::KFileDiskDevice(partition_id id) 27 : KDiskDevice(id), 28 fFilePath(NULL) 29 { 30 } 31 32 // destructor 33 KFileDiskDevice::~KFileDiskDevice() 34 { 35 Unset(); 36 } 37 38 // SetTo 39 status_t 40 KFileDiskDevice::SetTo(const char *filePath, const char *devicePath) 41 { 42 // check params 43 if (!filePath || strlen(filePath) > B_PATH_NAME_LENGTH 44 || (devicePath && strlen(devicePath) > B_PATH_NAME_LENGTH)) { 45 return B_BAD_VALUE; 46 } 47 // normalize the file path 48 // (should actually not be necessary, since this method is only invoked 49 // by the DDM, which has already normalized the path) 50 KPath tmpFilePath; 51 status_t error = tmpFilePath.SetTo(filePath, true); 52 if (error != B_OK) 53 return error; 54 // check the file 55 struct stat st; 56 if (stat(filePath, &st) != 0) 57 return errno; 58 if (!S_ISREG(st.st_mode)) 59 return B_BAD_VALUE; 60 // create the device, if requested 61 KPath tmpDevicePath; 62 if (!devicePath) { 63 // no device path: we shall create a new device entry 64 if (tmpDevicePath.InitCheck() != B_OK) 65 return tmpDevicePath.InitCheck(); 66 // TODO: Cleanup. The directory creation is done automatically by the devfs. 67 // // make the file devices dir 68 // if (mkdir(kFileDevicesDir, 0777) != 0) { 69 // if (errno != B_FILE_EXISTS) 70 // return errno; 71 // } 72 // make the directory 73 status_t error = _GetDirectoryPath(ID(), &tmpDevicePath); 74 if (error != B_OK) 75 return error; 76 // if (mkdir(tmpDevicePath.Path(), 0777) != 0) 77 // return errno; 78 // get the device path name 79 error = tmpDevicePath.Append("raw"); 80 if (error != B_OK) 81 return error; 82 devicePath = tmpDevicePath.Path(); 83 // register the file as virtual disk device 84 error = _RegisterDevice(filePath, devicePath); 85 if (error != B_OK) 86 return error; 87 } 88 error = set_string(fFilePath, filePath); 89 if (error != B_OK) 90 return error; 91 return KDiskDevice::SetTo(devicePath); 92 } 93 94 // Unset 95 void 96 KFileDiskDevice::Unset() 97 { 98 // remove the device and the directory it resides in 99 if (Path() && ID() >= 0) { 100 _UnregisterDevice(Path()); 101 // TODO: Cleanup. The devfs will automatically remove the directory. 102 // KPath dirPath; 103 // if (_GetDirectoryPath(ID(), &dirPath) == B_OK) 104 // rmdir(dirPath.Path()); 105 } 106 // free file path 107 free(fFilePath); 108 fFilePath = NULL; 109 } 110 111 // InitCheck 112 status_t 113 KFileDiskDevice::InitCheck() const 114 { 115 return KDiskDevice::InitCheck(); 116 } 117 118 // FilePath 119 const char * 120 KFileDiskDevice::FilePath() const 121 { 122 return fFilePath; 123 } 124 125 // GetMediaStatus 126 status_t 127 KFileDiskDevice::GetMediaStatus(status_t *mediaStatus) 128 { 129 // check the file 130 struct stat st; 131 if (stat(fFilePath, &st) == 0 && S_ISREG(st.st_mode)) 132 *mediaStatus = B_OK; 133 else 134 *mediaStatus = B_DEV_NO_MEDIA; 135 return B_OK; 136 } 137 138 // GetGeometry 139 status_t 140 KFileDiskDevice::GetGeometry(device_geometry *geometry) 141 { 142 // check the file 143 struct stat st; 144 if (stat(fFilePath, &st) != 0 || !S_ISREG(st.st_mode)) 145 return B_BAD_VALUE; 146 147 // fill in the geometry 148 // default to 512 bytes block size 149 uint32 blockSize = 512; 150 // Optimally we have only 1 block per sector and only one head. 151 // Since we have only a uint32 for the cylinder count, this won't work 152 // for files > 2TB. So, we set the head count to the minimally possible 153 // value. 154 off_t blocks = st.st_size / blockSize; 155 uint32 heads = (blocks + ULONG_MAX - 1) / ULONG_MAX; 156 if (heads == 0) 157 heads = 1; 158 geometry->bytes_per_sector = blockSize; 159 geometry->sectors_per_track = 1; 160 geometry->cylinder_count = blocks / heads; 161 geometry->head_count = heads; 162 geometry->device_type = B_DISK; // TODO: Add a new constant. 163 geometry->removable = false; 164 geometry->read_only = false; 165 geometry->write_once = false; 166 167 return B_OK; 168 } 169 170 // _RegisterDevice 171 status_t 172 KFileDiskDevice::_RegisterDevice(const char *file, const char *device) 173 { 174 return devfs_publish_file_device(device + 5, file); 175 // we need to remove the "/dev/" part from the path 176 } 177 178 // _UnregisterDevice 179 status_t 180 KFileDiskDevice::_UnregisterDevice(const char *_device) 181 { 182 return devfs_unpublish_file_device(_device + 5); 183 // we need to remove the "/dev/" part from the path 184 } 185 186 // _GetDirectoryPath 187 status_t 188 KFileDiskDevice::_GetDirectoryPath(partition_id id, KPath *path) 189 { 190 if (!path || path->InitCheck() != B_OK) 191 return path->InitCheck(); 192 status_t error = path->SetPath(kFileDevicesDir); 193 if (error == B_OK) { 194 char idBuffer[12]; 195 sprintf(idBuffer, "%ld", id); 196 error = path->Append(idBuffer); 197 } 198 return error; 199 } 200 201