1 /* 2 * Copyright 2003-2013, Axel Dörfler, axeld@pinc-software.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "File.h" 8 9 #include <errno.h> 10 #include <sys/stat.h> 11 #include <unistd.h> 12 13 14 using std::nothrow; 15 16 17 namespace FFS { 18 19 20 class Stream { 21 public: 22 Stream(int device, FileBlock &node); 23 ~Stream(); 24 25 status_t InitCheck(); 26 ssize_t ReadAt(off_t offset, uint8 *buffer, size_t size); 27 28 private: 29 int32 BlockOffset(off_t offset) const; 30 int32 BlockIndex(off_t offset) const; 31 int32 ExtensionBlockOffset(off_t offset) const; 32 status_t ReadNextExtension(); 33 34 int fDevice; 35 FileBlock &fNode; 36 FileBlock fBlock; 37 int32 fExtensionBlockOffset; 38 }; 39 40 41 Stream::Stream(int device, FileBlock &node) 42 : 43 fDevice(device), 44 fNode(node) 45 { 46 void *buffer = malloc(fNode.BlockSize()); 47 if (buffer == NULL) 48 return; 49 50 fExtensionBlockOffset = 0; 51 fBlock.SetTo(buffer, fNode.BlockSize()); 52 } 53 54 55 Stream::~Stream() 56 { 57 free(fBlock.BlockData()); 58 } 59 60 61 status_t 62 Stream::InitCheck() 63 { 64 return fBlock.BlockData() != NULL ? B_OK : B_NO_MEMORY; 65 } 66 67 68 int32 69 Stream::BlockOffset(off_t offset) const 70 { 71 return offset % fNode.BlockSize(); 72 } 73 74 75 int32 76 Stream::BlockIndex(off_t offset) const 77 { 78 return (offset % (fNode.BlockSize() * fNode.NumDataBlocks())) / fNode.BlockSize(); 79 } 80 81 82 int32 83 Stream::ExtensionBlockOffset(off_t offset) const 84 { 85 return offset / (fNode.BlockSize() * fNode.NumDataBlocks()); 86 } 87 88 89 status_t 90 Stream::ReadNextExtension() 91 { 92 int32 next; 93 if (fExtensionBlockOffset == 0) 94 next = fNode.NextExtension(); 95 else 96 next = fBlock.NextExtension(); 97 98 if (read_pos(fDevice, next * fNode.BlockSize(), fBlock.BlockData(), fNode.BlockSize()) < B_OK) 99 return B_ERROR; 100 101 return fBlock.ValidateCheckSum(); 102 } 103 104 105 ssize_t 106 Stream::ReadAt(off_t offset, uint8 *buffer, size_t size) 107 { 108 if (offset < 0) 109 return B_BAD_VALUE; 110 if (offset + (off_t)size > fNode.Size()) 111 size = fNode.Size() - offset; 112 113 ssize_t bytesLeft = (ssize_t)size; 114 115 while (bytesLeft != 0) { 116 int32 extensionBlock = ExtensionBlockOffset(offset); 117 118 // get the right extension block 119 120 if (extensionBlock < fExtensionBlockOffset) 121 fExtensionBlockOffset = 1; 122 123 while (fExtensionBlockOffset < extensionBlock) { 124 if (ReadNextExtension() != B_OK) 125 return B_ERROR; 126 127 fExtensionBlockOffset++; 128 } 129 130 // read the data block into memory 131 132 int32 block; 133 if (extensionBlock == 0) 134 block = fNode.DataBlock(BlockIndex(offset)); 135 else 136 block = fBlock.DataBlock(BlockIndex(offset)); 137 138 int32 blockOffset = BlockOffset(offset); 139 int32 toRead = fNode.BlockSize() - blockOffset; 140 if (toRead > bytesLeft) 141 toRead = bytesLeft; 142 143 ssize_t bytesRead = read_pos(fDevice, block * fNode.BlockSize() + blockOffset, 144 buffer, toRead); 145 if (bytesRead < 0) 146 return errno; 147 148 bytesLeft -= bytesRead; 149 buffer += bytesRead; 150 offset += fNode.BlockSize() - blockOffset; 151 } 152 153 return size; 154 } 155 156 157 // #pragma mark - 158 159 160 File::File(Volume &volume, int32 block) 161 : 162 fVolume(volume) 163 { 164 void *data = malloc(volume.BlockSize()); 165 if (data == NULL) 166 return; 167 168 if (read_pos(volume.Device(), block * volume.BlockSize(), data, volume.BlockSize()) == volume.BlockSize()) 169 fNode.SetTo(data, volume.BlockSize()); 170 } 171 172 173 File::~File() 174 { 175 } 176 177 178 status_t 179 File::InitCheck() 180 { 181 if (!fNode.IsFile()) 182 return B_BAD_TYPE; 183 184 return fNode.ValidateCheckSum(); 185 } 186 187 188 status_t 189 File::Open(void **_cookie, int mode) 190 { 191 Stream *stream = new(nothrow) Stream(fVolume.Device(), fNode); 192 if (stream == NULL) 193 return B_NO_MEMORY; 194 195 if (stream->InitCheck() != B_OK) { 196 delete stream; 197 return B_NO_MEMORY; 198 } 199 200 *_cookie = (void *)stream; 201 return B_OK; 202 } 203 204 205 status_t 206 File::Close(void *cookie) 207 { 208 Stream *stream = (Stream *)cookie; 209 210 delete stream; 211 return B_OK; 212 } 213 214 215 ssize_t 216 File::ReadAt(void *cookie, off_t pos, void *buffer, size_t bufferSize) 217 { 218 Stream *stream = (Stream *)cookie; 219 if (stream == NULL) 220 return B_BAD_VALUE; 221 222 return stream->ReadAt(pos, (uint8 *)buffer, bufferSize); 223 } 224 225 226 ssize_t 227 File::WriteAt(void *cookie, off_t pos, const void *buffer, size_t bufferSize) 228 { 229 return EROFS; 230 } 231 232 233 status_t 234 File::GetName(char *nameBuffer, size_t bufferSize) const 235 { 236 return fNode.GetName(nameBuffer, bufferSize); 237 } 238 239 240 int32 241 File::Type() const 242 { 243 return S_IFREG; 244 } 245 246 247 off_t 248 File::Size() const 249 { 250 return fNode.Size(); 251 } 252 253 254 ino_t 255 File::Inode() const 256 { 257 return fNode.HeaderKey(); 258 } 259 260 261 } // namespace FFS 262