summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Buland <eichlan@xagasoft.com>2011-02-17 18:02:24 +0000
committerMike Buland <eichlan@xagasoft.com>2011-02-17 18:02:24 +0000
commitbcfb40dde37a69139ef7ea2748ccfa642d3bee53 (patch)
treec8862b8fe4e354a7b4d022eee4e3bb25c19ee7a4
parent5c1dfe87cb4021c905529451e5ff7718c6c94b2f (diff)
downloadlibbu++-bcfb40dde37a69139ef7ea2748ccfa642d3bee53.tar.gz
libbu++-bcfb40dde37a69139ef7ea2748ccfa642d3bee53.tar.bz2
libbu++-bcfb40dde37a69139ef7ea2748ccfa642d3bee53.tar.xz
libbu++-bcfb40dde37a69139ef7ea2748ccfa642d3bee53.zip
Wow, MyriadFs is actually getting workable. You can create files, read, write,
etc. Next up is creating directories, and other special file types.
-rw-r--r--src/myriadfs.cpp292
-rw-r--r--src/myriadfs.h100
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"
16using Bu::sio;
17using Bu::Fmt;
13 18
14namespace Bu { subExceptionDef( MyriadFsException ) } 19namespace Bu { subExceptionDef( MyriadFsException ) }
15 20
@@ -17,8 +22,15 @@ namespace Bu { subExceptionDef( MyriadFsException ) }
17 22
18Bu::MyriadFs::MyriadFs( Bu::Stream &rStore, int iBlockSize ) : 23Bu::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
82Bu::MyriadFs::~MyriadFs() 99Bu::MyriadFs::~MyriadFs()
83{ 100{
101 writeHeader();
102}
103
104void 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
112Bu::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
146int32_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
161int32_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
204Bu::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
227Bu::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
247int32_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
262int32_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
308void 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
336void 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