1 /* ----> DO NOT REMOVE THE FOLLOWING NOTICE <---- 2 * 3 * Copyright (c) 2014-2015 Datalight, Inc. 4 * All Rights Reserved Worldwide. 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; use version 2 of the License. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty 12 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License along 16 * with this program; if not, write to the Free Software Foundation, Inc., 17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 */ 19 20 /* Businesses and individuals that for commercial or other reasons cannot 21 * comply with the terms of the GPLv2 license may obtain a commercial license 22 * before incorporating Reliance Edge into proprietary software for 23 * distribution in any form. Visit http://www.datalight.com/reliance-edge for 24 * more information. 25 */ 26 27 /** @file 28 * @brief Implements routines for the external imap. 29 * 30 * The external imap is used on volumes that are too big for the imap bitmap 31 * to be stored entirely in the metaroot, so instead the bitmap is stored in 32 * imap nodes on disk, and the metaroot bitmap is used to toggle between imap 33 * nodes. 34 */ 35 #include <redfs.h> 36 37 #if REDCONF_IMAP_EXTERNAL == 1 38 39 #include <redcore.h> 40 41 42 #if REDCONF_READ_ONLY == 0 43 static REDSTATUS ImapNodeBranch( uint32_t ulImapNode, 44 IMAPNODE ** ppImap ); 45 static bool ImapNodeIsBranched( uint32_t ulImapNode ); 46 #endif 47 48 49 /** @brief Get the allocation bit of a block from the imap as it exists in 50 * either metaroot. 51 * 52 * @param bMR The metaroot index: either 0 or 1. 53 * @param ulBlock The block number to query. 54 * @param pfAllocated On successful exit, populated with the allocation bit 55 * of the block. 56 * 57 * @return A negated ::REDSTATUS code indicating the operation result. 58 * 59 * @retval 0 Operation was successful. 60 * @retval -RED_EINVAL @p bMR is out of range; or @p ulBlock is out of range; 61 * or @p pfAllocated is `NULL`. 62 * @retval -RED_EIO A disk I/O error occurred. 63 */ RedImapEBlockGet(uint8_t bMR,uint32_t ulBlock,bool * pfAllocated)64 REDSTATUS RedImapEBlockGet( uint8_t bMR, 65 uint32_t ulBlock, 66 bool * pfAllocated ) 67 { 68 REDSTATUS ret; 69 70 if( gpRedCoreVol->fImapInline || 71 ( bMR > 1U ) || 72 ( ulBlock < gpRedCoreVol->ulInodeTableStartBN ) || 73 ( ulBlock >= gpRedVolume->ulBlockCount ) || 74 ( pfAllocated == NULL ) ) 75 { 76 REDERROR(); 77 ret = -RED_EINVAL; 78 } 79 else 80 { 81 uint32_t ulOffset = ulBlock - gpRedCoreVol->ulInodeTableStartBN; 82 uint32_t ulImapNode = ulOffset / IMAPNODE_ENTRIES; 83 uint8_t bMRToRead = bMR; 84 IMAPNODE * pImap; 85 86 #if REDCONF_READ_ONLY == 0 87 88 /* If the imap node is not branched, then both copies of the imap are 89 * identical. If the old metaroot copy is requested, use the current 90 * copy instead, since it is more likely to be buffered. 91 */ 92 if( bMR == ( 1U - gpRedCoreVol->bCurMR ) ) 93 { 94 if( !ImapNodeIsBranched( ulImapNode ) ) 95 { 96 bMRToRead = 1U - bMR; 97 } 98 } 99 #endif 100 101 ret = RedBufferGet( RedImapNodeBlock( bMRToRead, ulImapNode ), BFLAG_META_IMAP, CAST_VOID_PTR_PTR( &pImap ) ); 102 103 if( ret == 0 ) 104 { 105 *pfAllocated = RedBitGet( pImap->abEntries, ulOffset % IMAPNODE_ENTRIES ); 106 107 RedBufferPut( pImap ); 108 } 109 } 110 111 return ret; 112 } 113 114 115 #if REDCONF_READ_ONLY == 0 116 117 /** @brief Set the allocation bit of a block in the working-state imap. 118 * 119 * @param ulBlock The block number to allocate or free. 120 * @param fAllocated Whether to allocate the block (true) or free it (false). 121 * 122 * @return A negated ::REDSTATUS code indicating the operation result. 123 * 124 * @retval 0 Operation was successful. 125 * @retval -RED_EINVAL @p ulBlock is out of range. 126 * @retval -RED_EIO A disk I/O error occurred. 127 */ RedImapEBlockSet(uint32_t ulBlock,bool fAllocated)128 REDSTATUS RedImapEBlockSet( uint32_t ulBlock, 129 bool fAllocated ) 130 { 131 REDSTATUS ret; 132 133 if( gpRedCoreVol->fImapInline || 134 ( ulBlock < gpRedCoreVol->ulInodeTableStartBN ) || 135 ( ulBlock >= gpRedVolume->ulBlockCount ) ) 136 { 137 REDERROR(); 138 ret = -RED_EINVAL; 139 } 140 else 141 { 142 uint32_t ulOffset = ulBlock - gpRedCoreVol->ulInodeTableStartBN; 143 uint32_t ulImapNode = ulOffset / IMAPNODE_ENTRIES; 144 IMAPNODE * pImap; 145 146 ret = ImapNodeBranch( ulImapNode, &pImap ); 147 148 if( ret == 0 ) 149 { 150 uint32_t ulImapEntry = ulOffset % IMAPNODE_ENTRIES; 151 152 if( RedBitGet( pImap->abEntries, ulImapEntry ) == fAllocated ) 153 { 154 /* The driver shouldn't ever set a bit in the imap to its 155 * current value. That shouldn't ever be needed, and it 156 * indicates that the driver is doing unnecessary I/O, or 157 * that the imap is corrupt. 158 */ 159 CRITICAL_ERROR(); 160 ret = -RED_EFUBAR; 161 } 162 else if( fAllocated ) 163 { 164 RedBitSet( pImap->abEntries, ulImapEntry ); 165 } 166 else 167 { 168 RedBitClear( pImap->abEntries, ulImapEntry ); 169 } 170 171 RedBufferPut( pImap ); 172 } 173 } 174 175 return ret; 176 } 177 178 179 /** @brief Branch an imap node and get a buffer for it. 180 * 181 * If the imap node is already branched, it can be overwritten in its current 182 * location, and this function just gets it buffered dirty. If the node is not 183 * already branched, the metaroot must be updated to toggle the imap node to 184 * its alternate location, thereby preserving the committed state copy of the 185 * imap node. 186 * 187 * @param ulImapNode The imap node to branch and buffer. 188 * @param ppImap On successful return, populated with the imap node 189 * buffer, which will be marked dirty. 190 * 191 * @return A negated ::REDSTATUS code indicating the operation result. 192 * 193 * @retval 0 Operation was successful. 194 * @retval -RED_EINVAL @p ulImapNode is out of range; or @p ppImap is `NULL`. 195 * @retval -RED_EIO A disk I/O error occurred. 196 */ ImapNodeBranch(uint32_t ulImapNode,IMAPNODE ** ppImap)197 static REDSTATUS ImapNodeBranch( uint32_t ulImapNode, 198 IMAPNODE ** ppImap ) 199 { 200 REDSTATUS ret; 201 202 if( ( ulImapNode >= gpRedCoreVol->ulImapNodeCount ) || ( ppImap == NULL ) ) 203 { 204 REDERROR(); 205 ret = -RED_EINVAL; 206 } 207 else if( ImapNodeIsBranched( ulImapNode ) ) 208 { 209 /* Imap node is already branched, so just get it buffered dirty. 210 */ 211 ret = RedBufferGet( RedImapNodeBlock( gpRedCoreVol->bCurMR, ulImapNode ), BFLAG_META_IMAP | BFLAG_DIRTY, CAST_VOID_PTR_PTR( ppImap ) ); 212 } 213 else 214 { 215 uint32_t ulBlockCurrent; 216 uint32_t ulBlockOld; 217 218 /* The metaroot currently points to the committed state imap node. 219 * Toggle the metaroot to point at the alternate, writeable location. 220 */ 221 if( RedBitGet( gpRedMR->abEntries, ulImapNode ) ) 222 { 223 RedBitClear( gpRedMR->abEntries, ulImapNode ); 224 } 225 else 226 { 227 RedBitSet( gpRedMR->abEntries, ulImapNode ); 228 } 229 230 ulBlockCurrent = RedImapNodeBlock( gpRedCoreVol->bCurMR, ulImapNode ); 231 ulBlockOld = RedImapNodeBlock( 1U - gpRedCoreVol->bCurMR, ulImapNode ); 232 233 ret = RedBufferDiscardRange( ulBlockCurrent, 1U ); 234 235 /* Buffer the committed copy then reassign the block number to the 236 * writeable location. This also dirties the buffer. 237 */ 238 if( ret == 0 ) 239 { 240 ret = RedBufferGet( ulBlockOld, BFLAG_META_IMAP, CAST_VOID_PTR_PTR( ppImap ) ); 241 242 if( ret == 0 ) 243 { 244 RedBufferBranch( *ppImap, ulBlockCurrent ); 245 } 246 } 247 } 248 249 return ret; 250 } 251 252 253 /** @brief Determine whether an imap node is branched. 254 * 255 * If the imap node is branched, it can be overwritten in its current location. 256 * 257 * @param ulImapNode The imap node to examine. 258 * 259 * @return Whether the imap node is branched. 260 */ ImapNodeIsBranched(uint32_t ulImapNode)261 static bool ImapNodeIsBranched( uint32_t ulImapNode ) 262 { 263 bool fNodeBitSetInMetaroot0 = RedBitGet( gpRedCoreVol->aMR[ 0U ].abEntries, ulImapNode ); 264 bool fNodeBitSetInMetaroot1 = RedBitGet( gpRedCoreVol->aMR[ 1U ].abEntries, ulImapNode ); 265 266 /* If the imap node is not branched, both metaroots will point to the same 267 * copy of the node. 268 */ 269 return fNodeBitSetInMetaroot0 != fNodeBitSetInMetaroot1; 270 } 271 #endif /* REDCONF_READ_ONLY == 0 */ 272 273 274 /** @brief Calculate the block number of the imap node location indicated by the 275 * given metaroot. 276 * 277 * An imap node has two locations on disk. A bit in the metaroot bitmap 278 * indicates which location is the valid one, according to that metaroot. This 279 * function returns the block number of the imap node which is valid in the 280 * given metaroot. 281 * 282 * @param bMR Which metaroot to examine. 283 * @param ulImapNode The imap node for which to calculate the block number. 284 * 285 * @return Block number of the imap node, as indicated by the given metaroot. 286 */ RedImapNodeBlock(uint8_t bMR,uint32_t ulImapNode)287 uint32_t RedImapNodeBlock( uint8_t bMR, 288 uint32_t ulImapNode ) 289 { 290 uint32_t ulBlock; 291 292 REDASSERT( ulImapNode < gpRedCoreVol->ulImapNodeCount ); 293 294 ulBlock = gpRedCoreVol->ulImapStartBN + ( ulImapNode * 2U ); 295 296 if( bMR > 1U ) 297 { 298 REDERROR(); 299 } 300 else if( RedBitGet( gpRedCoreVol->aMR[ bMR ].abEntries, ulImapNode ) ) 301 { 302 /* Bit is set, so point ulBlock at the second copy of the node. 303 */ 304 ulBlock++; 305 } 306 else 307 { 308 /* ulBlock already points at the first copy of the node. 309 */ 310 } 311 312 return ulBlock; 313 } 314 315 #endif /* REDCONF_IMAP_EXTERNAL == 1 */ 316