diff options
Diffstat (limited to '')
-rw-r--r-- | src/myriadfs.cpp | 292 | ||||
-rw-r--r-- | src/myriadfs.h | 100 |
2 files changed, 375 insertions, 17 deletions
diff --git a/src/myriadfs.cpp b/src/myriadfs.cpp index c8d23b6..4e2a0f6 100644 --- a/src/myriadfs.cpp +++ b/src/myriadfs.cpp | |||
@@ -10,6 +10,11 @@ | |||
10 | 10 | ||
11 | #include <string.h> | 11 | #include <string.h> |
12 | #include <unistd.h> | 12 | #include <unistd.h> |
13 | #include <time.h> | ||
14 | |||
15 | #include "bu/sio.h" | ||
16 | using Bu::sio; | ||
17 | using Bu::Fmt; | ||
13 | 18 | ||
14 | namespace Bu { subExceptionDef( MyriadFsException ) } | 19 | namespace Bu { subExceptionDef( MyriadFsException ) } |
15 | 20 | ||
@@ -17,8 +22,15 @@ namespace Bu { subExceptionDef( MyriadFsException ) } | |||
17 | 22 | ||
18 | Bu::MyriadFs::MyriadFs( Bu::Stream &rStore, int iBlockSize ) : | 23 | Bu::MyriadFs::MyriadFs( Bu::Stream &rStore, int iBlockSize ) : |
19 | rStore( rStore ), | 24 | rStore( rStore ), |
20 | mStore( rStore, iBlockSize ) | 25 | mStore( rStore, iBlockSize ), |
26 | iUser( 0 ), | ||
27 | iGroup( 0 ) | ||
21 | { | 28 | { |
29 | #ifndef WIN32 | ||
30 | iUser = getuid(); | ||
31 | iGroup = getgid(); | ||
32 | #endif | ||
33 | |||
22 | if( mStore.hasStream( 1 ) ) | 34 | if( mStore.hasStream( 1 ) ) |
23 | { | 35 | { |
24 | // Check to see if this is a MyriadFs stream. | 36 | // Check to see if this is a MyriadFs stream. |
@@ -32,6 +44,8 @@ Bu::MyriadFs::MyriadFs( Bu::Stream &rStore, int iBlockSize ) : | |||
32 | "a MyriadFs stream."); | 44 | "a MyriadFs stream."); |
33 | 45 | ||
34 | int8_t iVer; | 46 | int8_t iVer; |
47 | ms.read( &iVer, 1 ); | ||
48 | throw MyriadFsException("You totally have a MyriadFs stream, version %d, but they can't be loaded yet.", iVer ); | ||
35 | } | 49 | } |
36 | else | 50 | else |
37 | { | 51 | { |
@@ -43,43 +57,299 @@ Bu::MyriadFs::MyriadFs( Bu::Stream &rStore, int iBlockSize ) : | |||
43 | int8_t iVer = 1; | 57 | int8_t iVer = 1; |
44 | int32_t iTmp = 1; | 58 | int32_t iTmp = 1; |
45 | ms.write( &iVer, 1 ); | 59 | ms.write( &iVer, 1 ); |
46 | ms.write( &iBlockSize, 4 ); | ||
47 | ms.write( &iTmp, 4 ); // iNumNodes | 60 | ms.write( &iTmp, 4 ); // iNumNodes |
48 | iTmp = 0; | 61 | iTmp = 0; |
49 | ms.write( &iTmp, 4 ); // iInode | 62 | ms.write( &iTmp, 4 ); // iInode |
50 | ms.write( &iTmp, 0 ); // iPosition | 63 | ms.write( &iTmp, 4 ); // iPosition |
64 | hNodeIndex.insert( 0, 0 ); | ||
51 | } | 65 | } |
52 | 66 | ||
53 | // Create initial inode stream, with one root node. | 67 | // Create initial inode stream, with one root node. |
54 | { | 68 | { |
55 | mStore.createStream( 2 ); | 69 | mStore.createStream( 2 ); |
56 | Bu::MyriadStream ms = mStore.openStream( 2 ); | 70 | Bu::MyriadStream ms = mStore.openStream( 2 ); |
57 | int32_t iUser = 0, iGroup = 0; | ||
58 | #ifndef WIN32 | ||
59 | iUser = getuid(); | ||
60 | iGroup = getgid(); | ||
61 | #endif | ||
62 | int32_t iTmp32 = 0; | 71 | int32_t iTmp32 = 0; |
63 | int16_t iTmp16 = 0; | 72 | int16_t iTmp16 = 0; |
73 | uint16_t uPerms = 775|typeDir; | ||
64 | ms.write( &iUser, 4 ); // User | 74 | ms.write( &iUser, 4 ); // User |
65 | ms.write( &iGroup, 4 ); // Group | 75 | ms.write( &iGroup, 4 ); // Group |
66 | ms.write( &iTmp16, 2 ); // Meta? I...don't remember this | 76 | ms.write( &uPerms, 2 ); // Permissions/types |
67 | ms.write( &iTmp16, 2 ); // Permissions/types | 77 | ms.write( &iTmp16, 2 ); // Link count |
68 | iTmp32 = 3; | 78 | iTmp32 = 3; |
69 | ms.write( &iTmp32, 4 ); // Stream index | 79 | ms.write( &iTmp32, 4 ); // Stream index |
70 | iTmp32 = 0; | 80 | iTmp32 = 0; |
71 | ms.write( &iTmp32, 4 ); // Parent inode | 81 | ms.write( &iTmp32, 4 ); // Parent inode (root's it's own parent) |
82 | int64_t iTime = time(NULL); | ||
83 | ms.write( &iTime, 8 ); // atime | ||
84 | ms.write( &iTime, 8 ); // mtime | ||
85 | ms.write( &iTime, 8 ); // ctime | ||
72 | ms.write( &iTmp16, 2 ); // Name size | 86 | ms.write( &iTmp16, 2 ); // Name size |
73 | } | 87 | } |
74 | 88 | ||
75 | // Create inode 0's storage stream. | 89 | // Create inode 0's storage stream. |
76 | { | 90 | { |
77 | mStore.createStream( 3 ); | 91 | mStore.createStream( 3 ); |
92 | Bu::MyriadStream ms = mStore.openStream( 3 ); | ||
93 | int32_t iTmp32 = 0; | ||
94 | ms.write( &iTmp32, 4 ); // iChildCount | ||
78 | } | 95 | } |
79 | } | 96 | } |
80 | } | 97 | } |
81 | 98 | ||
82 | Bu::MyriadFs::~MyriadFs() | 99 | Bu::MyriadFs::~MyriadFs() |
83 | { | 100 | { |
101 | writeHeader(); | ||
102 | } | ||
103 | |||
104 | void Bu::MyriadFs::stat( const Bu::String &sPath, Bu::MyriadFs::Stat &rBuf ) | ||
105 | { | ||
106 | int32_t iParent; | ||
107 | int32_t iNode = lookupInode( sPath, iParent ); | ||
108 | Bu::MyriadStream is = mStore.openStream( 2 ); | ||
109 | stat( iNode, rBuf, is ); | ||
110 | } | ||
111 | |||
112 | Bu::MyriadStream Bu::MyriadFs::open( const Bu::String &sPath, int iMode ) | ||
113 | { | ||
114 | int32_t iParent = -1; | ||
115 | int32_t iNode; | ||
116 | try | ||
117 | { | ||
118 | iNode = lookupInode( sPath, iParent ); | ||
119 | sio << "File found." << sio.nl; | ||
120 | // The file was found | ||
121 | return openByInode( iNode ); | ||
122 | } | ||
123 | catch( Bu::MyriadFsException &e ) | ||
124 | { | ||
125 | if( iParent < 0 ) | ||
126 | throw; | ||
127 | |||
128 | // The file wasn't found, but the path leading up to it was. | ||
129 | // first, figure out the final path element... | ||
130 | Bu::String::const_iterator iStart = sPath.begin(); | ||
131 | if( *iStart == '/' ) | ||
132 | iStart++; | ||
133 | sio << "Scanning for filename:" << sio.nl; | ||
134 | for( Bu::String::const_iterator iEnd = iStart.find('/')+1; iEnd; iStart = iEnd ) { } | ||
135 | Bu::String sName( iStart, sPath.end() ); | ||
136 | sio << "End filename: " << sName << sio.nl; | ||
137 | sio << "Parent inode: " << iParent << sio.nl; | ||
138 | iNode = create( iParent, sName, 0664|typeRegFile ); | ||
139 | sio << "New iNode: " << iNode << sio.nl; | ||
140 | return openByInode( iNode ); | ||
141 | } | ||
142 | |||
143 | return mStore.openStream( 2 ); | ||
144 | } | ||
145 | |||
146 | int32_t Bu::MyriadFs::lookupInode( const Bu::String &sPath, int32_t &iParent ) | ||
147 | { | ||
148 | if( sPath[0] == '/' ) | ||
149 | { | ||
150 | // Absolute lookup | ||
151 | return lookupInode( sPath.begin()+1, 0, iParent ); | ||
152 | } | ||
153 | else | ||
154 | { | ||
155 | // Relative lookup | ||
156 | throw Bu::ExceptionBase( | ||
157 | "Relative lookups in MyriadFs are not working yet."); | ||
158 | } | ||
159 | } | ||
160 | |||
161 | int32_t Bu::MyriadFs::lookupInode( Bu::String::const_iterator iStart, | ||
162 | int32_t iNode, int32_t &iParent ) | ||
163 | { | ||
164 | iParent = iNode; | ||
165 | |||
166 | Bu::String::const_iterator iEnd = iStart.find('/'); | ||
167 | Bu::String sTok( iStart, iEnd ); | ||
168 | |||
169 | sio << "Direcotry component: " << sTok << sio.nl; | ||
170 | |||
171 | Dir lDir = readDir( iNode ); | ||
172 | |||
173 | for( Dir::iterator i = lDir.begin(); i; i++ ) | ||
174 | { | ||
175 | if( (*i).sName == sTok ) | ||
176 | { | ||
177 | // We found an item | ||
178 | if( !iEnd ) | ||
179 | { | ||
180 | // It's the last one in the requested path, return it | ||
181 | return (*i).iNode; | ||
182 | } | ||
183 | else | ||
184 | { | ||
185 | // Not the last one in our path, double check it's a dir | ||
186 | if( ((*i).uPerms&typeMask) == typeDir ) | ||
187 | { | ||
188 | return lookupInode( iEnd+1, (*i).iNode ); | ||
189 | } | ||
190 | else | ||
191 | { | ||
192 | iParent = -1; | ||
193 | throw Bu::MyriadFsException( | ||
194 | "Element '%s' in given path is not a directory.", | ||
195 | sTok.getStr() ); | ||
196 | } | ||
197 | } | ||
198 | } | ||
199 | } | ||
200 | |||
201 | throw Bu::MyriadFsException( 1, "Path not found"); | ||
202 | } | ||
203 | |||
204 | Bu::MyriadFs::Dir Bu::MyriadFs::readDir( int32_t iNode ) | ||
205 | { | ||
206 | Bu::MyriadStream ms = openByInode( iNode ); | ||
207 | int32_t iNumChildren; | ||
208 | ms.read( &iNumChildren, 4 ); | ||
209 | |||
210 | Bu::MyriadStream is = mStore.openStream( 2 ); | ||
211 | Dir lDir; | ||
212 | sio << "Reading dir, " << iNumChildren << " entries:" << sio.nl; | ||
213 | for( int32_t j = 0; j < iNumChildren; j++ ) | ||
214 | { | ||
215 | int32_t iChildNode; | ||
216 | ms.read( &iChildNode, 4 ); | ||
217 | Stat s; | ||
218 | stat( iChildNode, s, is ); | ||
219 | lDir.append( s ); | ||
220 | |||
221 | sio << " " << s.sName << sio.nl; | ||
222 | } | ||
223 | |||
224 | return lDir; | ||
225 | } | ||
226 | |||
227 | Bu::MyriadStream Bu::MyriadFs::openByInode( int32_t iNode ) | ||
228 | { | ||
229 | int32_t iIndex = hNodeIndex.get( iNode ); | ||
230 | Bu::MyriadStream ms = mStore.openStream( 2 ); | ||
231 | ms.setPos( mStore.getBlockSize()*iIndex ); | ||
232 | RawStat rs; | ||
233 | ms.read( &rs, sizeof(RawStat) ); | ||
234 | switch( (rs.uPerms&typeMask) ) | ||
235 | { | ||
236 | case typeDir: | ||
237 | case typeSymLink: | ||
238 | case typeRegFile: | ||
239 | return mStore.openStream( rs.iStreamIndex ); | ||
240 | |||
241 | default: | ||
242 | throw Bu::MyriadFsException( | ||
243 | "inode incorrect type for low-level openByInode."); | ||
244 | } | ||
245 | } | ||
246 | |||
247 | int32_t Bu::MyriadFs::create( int32_t iParent, const Bu::String &sName, | ||
248 | uint16_t uPerms ) | ||
249 | { | ||
250 | Bu::MyriadStream ms = openByInode( iParent ); | ||
251 | int32_t iNumChildren; | ||
252 | ms.read( &iNumChildren, 4 ); | ||
253 | iNumChildren++; | ||
254 | ms.setPos( 0 ); | ||
255 | ms.write( &iNumChildren, 4 ); | ||
256 | ms.setPos( iNumChildren*4 ); // Actually 4+(iNumChildren-1)*4 :-P | ||
257 | int32_t iNode = allocInode( sName, iParent, uPerms ); | ||
258 | ms.write( &iNode, 4 ); | ||
259 | return iNode; | ||
260 | } | ||
261 | |||
262 | int32_t Bu::MyriadFs::allocInode( const Bu::String &sName, int32_t iParent, | ||
263 | uint16_t uPerms ) | ||
264 | { | ||
265 | int32_t iNode = 0; | ||
266 | for(; iNode < 0xfffffff; iNode++ ) | ||
267 | { | ||
268 | if( !hNodeIndex.has( iNode ) ) | ||
269 | { | ||
270 | Bu::MyriadStream is = mStore.openStream( 2 ); | ||
271 | is.setSize( (hNodeIndex.getSize()+1)*mStore.getBlockSize() ); | ||
272 | is.setPos( hNodeIndex.getSize()*mStore.getBlockSize() ); | ||
273 | |||
274 | hNodeIndex.insert( iNode, hNodeIndex.getSize() ); | ||
275 | RawStat rs; | ||
276 | rs.iUser = iUser; | ||
277 | rs.iGroup = iGroup; | ||
278 | rs.uPerms = uPerms; | ||
279 | rs.iLinks = 1; | ||
280 | switch( (uPerms&typeMask) ) | ||
281 | { | ||
282 | case typeDir: | ||
283 | case typeRegFile: | ||
284 | case typeSymLink: | ||
285 | rs.iStreamIndex = mStore.createStream(); | ||
286 | break; | ||
287 | |||
288 | default: | ||
289 | rs.iStreamIndex = 0; | ||
290 | break; | ||
291 | } | ||
292 | rs.iParentNode = iParent; | ||
293 | rs.iATime = time(NULL); | ||
294 | rs.iMTime = time(NULL); | ||
295 | rs.iCTime = time(NULL); | ||
296 | rs.iNameSize = sName.getSize(); | ||
297 | is.write( &rs, sizeof(RawStat) ); | ||
298 | is.write( sName.getStr(), sName.getSize() ); | ||
299 | |||
300 | return iNode; | ||
301 | } | ||
302 | } | ||
303 | |||
304 | throw Bu::MyriadFsException( | ||
305 | "No inode could be allocated. You've run out!"); | ||
306 | } | ||
307 | |||
308 | void Bu::MyriadFs::stat( int32_t iNode, Stat &rBuf, MyriadStream &rIs ) | ||
309 | { | ||
310 | rIs.setPos( hNodeIndex.get( iNode )*mStore.getBlockSize() ); | ||
311 | RawStat rs; | ||
312 | rIs.read( &rs, sizeof(RawStat) ); | ||
313 | rBuf.sName.setSize( rs.iNameSize ); | ||
314 | rIs.read( rBuf.sName.getStr(), rs.iNameSize ); | ||
315 | rBuf.iNode = iNode; | ||
316 | rBuf.iUser = rs.iUser; | ||
317 | rBuf.iGroup = rs.iGroup; | ||
318 | rBuf.uPerms = rs.uPerms; | ||
319 | rBuf.iLinks = rs.iLinks; | ||
320 | rBuf.iATime = rs.iATime; | ||
321 | rBuf.iMTime = rs.iMTime; | ||
322 | rBuf.iCTime = rs.iCTime; | ||
323 | switch( (rBuf.uPerms&typeMask) ) | ||
324 | { | ||
325 | case typeRegFile: | ||
326 | case typeSymLink: | ||
327 | rBuf.iSize = mStore.getStreamSize( rs.iStreamIndex ); | ||
328 | break; | ||
329 | |||
330 | default: | ||
331 | rBuf.iSize = 0; | ||
332 | break; | ||
333 | } | ||
334 | } | ||
335 | |||
336 | void Bu::MyriadFs::writeHeader() | ||
337 | { | ||
338 | Bu::MyriadStream ms = mStore.openStream( 1 ); | ||
339 | ms.write( Myriad_Fs_MAGIC_CODE, 4 ); | ||
340 | int8_t iVer = 1; | ||
341 | int32_t iNumNodes = hNodeIndex.getSize(); | ||
342 | ms.write( &iVer, 1 ); | ||
343 | ms.write( &iNumNodes, 4 ); // iNumNodes | ||
344 | for( NodeIndex::iterator i = hNodeIndex.begin(); i; i++ ) | ||
345 | { | ||
346 | int32_t iNode = i.getKey(); | ||
347 | int32_t iPosition = i.getValue(); | ||
348 | ms.write( &iNode, 4 ); | ||
349 | ms.write( &iPosition, 4 ); | ||
350 | } | ||
351 | |||
352 | // Truncate the stream afterwards so we don't use up too much space. | ||
353 | ms.setSize( ms.tell() ); | ||
84 | } | 354 | } |
85 | 355 | ||
diff --git a/src/myriadfs.h b/src/myriadfs.h index 856137c..808444b 100644 --- a/src/myriadfs.h +++ b/src/myriadfs.h | |||
@@ -19,13 +19,10 @@ namespace Bu | |||
19 | /** | 19 | /** |
20 | * A POSIX compliant, node based filesystem built on top of Myriad. | 20 | * A POSIX compliant, node based filesystem built on top of Myriad. |
21 | * | 21 | * |
22 | * Think about putting this all in one stream, on block boundaries. | ||
23 | * | ||
24 | * A header is placed into stream 1. | 22 | * A header is placed into stream 1. |
25 | * Header format: | 23 | * Header format: |
26 | * int32_t iMagicHeader (A7188B39) | 24 | * int32_t iMagicHeader (A7188B39) |
27 | * int8_t iVersion (1) | 25 | * int8_t iVersion (1) |
28 | * int32_t iNodeSize | ||
29 | * int32_t iNumNodes | 26 | * int32_t iNumNodes |
30 | * NodeLookup[iNumNodes] nNode | 27 | * NodeLookup[iNumNodes] nNode |
31 | * | 28 | * |
@@ -33,10 +30,10 @@ namespace Bu | |||
33 | * int32_t iInode | 30 | * int32_t iInode |
34 | * int32_t iPosition | 31 | * int32_t iPosition |
35 | * | 32 | * |
36 | * The node headers or inode structures have a base size of 22 bytes. | 33 | * The node headers or inode structures have a base size of 46 bytes. |
37 | * Everything else in the block is used for the name. I.e. if you have | 34 | * Everything else in the block is used for the name. I.e. if you have |
38 | * a blocksize of 512 bytes then you wind up with a max name size of | 35 | * a blocksize of 512 bytes then you wind up with a max name size of |
39 | * 512-22=490 characters, or a blocksize of 256 gives you 234 chraacters | 36 | * 512-46=466 characters, or a blocksize of 256 gives you 210 chraacters |
40 | * as a max. The node headers are all stored in stream 2. | 37 | * as a max. The node headers are all stored in stream 2. |
41 | * Basic node header format: | 38 | * Basic node header format: |
42 | * int32_t iUser | 39 | * int32_t iUser |
@@ -50,6 +47,27 @@ namespace Bu | |||
50 | * int64_t iCTime | 47 | * int64_t iCTime |
51 | * int16_t iNameSize | 48 | * int16_t iNameSize |
52 | * char[iNameSize] sName | 49 | * char[iNameSize] sName |
50 | * | ||
51 | * Some types get special formats for their assosiated data stream, or | ||
52 | * other special considerations, here's a list: | ||
53 | * | ||
54 | * - typeFifo: No stream, iStreamIndex unused (probably) | ||
55 | * - typeChrDev: No stream, iStreamIndex is device hi/lo | ||
56 | * - typeDir: The stream contains a directory contents listing, described | ||
57 | * below | ||
58 | * - typeBlkDev: No stream, iStreamIndex is device hi/lo | ||
59 | * - typeRegFile: The stream is the file data | ||
60 | * - typeSymLink: The stream is the destination of the symlink | ||
61 | * - typeSocket: No steram, iStreamIndex unused (probably) | ||
62 | * | ||
63 | * Directory streams have this simple listing format. They contain a list | ||
64 | * of all child elements, with no particular order at the moment. The . and | ||
65 | * .. entries are not listed, they are implicit: | ||
66 | * int32_t iNumNodes | ||
67 | * NodeTable[iNumNodes] nChildren | ||
68 | * | ||
69 | * NodeTable: | ||
70 | * int32_t iInode | ||
53 | */ | 71 | */ |
54 | class MyriadFs | 72 | class MyriadFs |
55 | { | 73 | { |
@@ -79,11 +97,81 @@ namespace Bu | |||
79 | typeSymLink = 0120000, | 97 | typeSymLink = 0120000, |
80 | typeSocket = 0140000, | 98 | typeSocket = 0140000, |
81 | typeMask = 0170000 | 99 | typeMask = 0170000 |
82 | } | 100 | }; |
101 | |||
102 | enum | ||
103 | { | ||
104 | Read = 0x01, ///< Open file for reading | ||
105 | Write = 0x02, ///< Open file for writing | ||
106 | Create = 0x04, ///< Create file if it doesn't exist | ||
107 | Truncate = 0x08, ///< Truncate file if it does exist | ||
108 | Append = 0x10, ///< Always append on every write | ||
109 | NonBlock = 0x20, ///< Open file in non-blocking mode | ||
110 | Exclusive = 0x44, ///< Create file, if it exists then fail | ||
111 | |||
112 | // Helpful mixes | ||
113 | ReadWrite = 0x03, ///< Open for reading and writing | ||
114 | WriteNew = 0x0E ///< Create a file (or truncate) for writing. | ||
115 | /// Same as Write|Create|Truncate | ||
116 | }; | ||
117 | |||
118 | class Stat | ||
119 | { | ||
120 | public: | ||
121 | int32_t iNode; | ||
122 | int32_t iUser; | ||
123 | int32_t iGroup; | ||
124 | uint16_t uPerms; | ||
125 | int16_t iLinks; | ||
126 | int64_t iATime; | ||
127 | int64_t iMTime; | ||
128 | int64_t iCTime; | ||
129 | int32_t iSize; | ||
130 | Bu::String sName; | ||
131 | }; | ||
132 | typedef Bu::List<Stat> Dir; | ||
133 | |||
134 | void stat( const Bu::String &sPath, Stat &rBuf ); | ||
135 | MyriadStream open( const Bu::String &sPath, int iMode ); | ||
136 | // void create( const Bu::String &sPath, uint16_t iPerms ); | ||
137 | |||
138 | |||
139 | private: | ||
140 | class RawStat | ||
141 | { | ||
142 | public: | ||
143 | int32_t iUser; | ||
144 | int32_t iGroup; | ||
145 | uint16_t uPerms; | ||
146 | int16_t iLinks; | ||
147 | int32_t iStreamIndex; | ||
148 | int32_t iParentNode; | ||
149 | int64_t iATime; | ||
150 | int64_t iMTime; | ||
151 | int64_t iCTime; | ||
152 | int16_t iNameSize; | ||
153 | }; | ||
154 | typedef Bu::Hash<int32_t, int32_t> NodeIndex; | ||
155 | |||
156 | private: | ||
157 | int32_t lookupInode( const Bu::String &sPath, int32_t &iParent ); | ||
158 | int32_t lookupInode( Bu::String::const_iterator iStart, | ||
159 | int32_t iNode, int32_t &iParent ); | ||
160 | Dir readDir( int32_t iNode ); | ||
161 | MyriadStream openByInode( int32_t iNode ); | ||
162 | int32_t create( int32_t iParent, const Bu::String &sName, | ||
163 | uint16_t uPerms ); | ||
164 | int32_t allocInode( const Bu::String &sName, int32_t iParent, | ||
165 | uint16_t uPerms ); | ||
166 | void stat( int32_t iNode, Stat &rBuf, MyriadStream &rIs ); | ||
167 | void writeHeader(); | ||
83 | 168 | ||
84 | private: | 169 | private: |
85 | Bu::Stream &rStore; | 170 | Bu::Stream &rStore; |
86 | Bu::Myriad mStore; | 171 | Bu::Myriad mStore; |
172 | NodeIndex hNodeIndex; | ||
173 | int32_t iUser; | ||
174 | int32_t iGroup; | ||
87 | }; | 175 | }; |
88 | }; | 176 | }; |
89 | 177 | ||