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