summaryrefslogtreecommitdiff
path: root/src/unstable/myriadfs.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/unstable/myriadfs.cpp')
-rw-r--r--src/unstable/myriadfs.cpp703
1 files changed, 703 insertions, 0 deletions
diff --git a/src/unstable/myriadfs.cpp b/src/unstable/myriadfs.cpp
new file mode 100644
index 0000000..6884a31
--- /dev/null
+++ b/src/unstable/myriadfs.cpp
@@ -0,0 +1,703 @@
1/*
2 * Copyright (C) 2007-2011 Xagasoft, All rights reserved.
3 *
4 * This file is part of the libbu++ library and is released under the
5 * terms of the license contained in the file LICENSE.
6 */
7
8#include "bu/myriadfs.h"
9#include "bu/myriadstream.h"
10
11#include <string.h>
12#include <unistd.h>
13#include <time.h>
14
15#include "bu/sio.h"
16using Bu::sio;
17using Bu::Fmt;
18
19namespace Bu { subExceptionDef( MyriadFsException ) }
20
21#define Myriad_Fs_MAGIC_CODE ((char *)"\xa7\x18\x8b\x39")
22
23Bu::MyriadFs::MyriadFs( Bu::Stream &rStore, int iBlockSize ) :
24 rStore( rStore ),
25 mStore( rStore, iBlockSize ),
26 iUser( 0 ),
27 iGroup( 0 )
28{
29#ifndef WIN32
30 iUser = getuid();
31 iGroup = getgid();
32#endif
33
34 if( mStore.hasStream( 1 ) )
35 {
36 // Check to see if this is a MyriadFs stream.
37 Bu::MyriadStream ms = mStore.openStream( 1 );
38 char sMagic[4];
39 if( ms.read( sMagic, 4 ) < 4 )
40 throw MyriadFsException("The provided stream does not appear to be "
41 "a MyriadFs stream.");
42 if( ::strncmp( sMagic, Myriad_Fs_MAGIC_CODE, 4 ) )
43 throw MyriadFsException("The provided stream does not appear to be "
44 "a MyriadFs stream.");
45
46 int8_t iVer;
47 ms.read( &iVer, 1 );
48
49 int32_t iNumNodes;
50 ms.read( &iNumNodes, 4 );
51 for( int32_t j = 0; j < iNumNodes; j++ )
52 {
53 int32_t iNode;
54 int32_t iPos;
55 ms.read( &iNode, 4 );
56 ms.read( &iPos, 4 );
57 hNodeIndex.insert( iNode, iPos );
58 }
59 }
60 else
61 {
62 // Create initial header stream
63 {
64 mStore.createStream( 1 );
65 Bu::MyriadStream ms = mStore.openStream( 1 );
66 ms.write( Myriad_Fs_MAGIC_CODE, 4 );
67 int8_t iVer = 1;
68 int32_t iTmp = 1;
69 ms.write( &iVer, 1 );
70 ms.write( &iTmp, 4 ); // iNumNodes
71 iTmp = 0;
72 ms.write( &iTmp, 4 ); // iInode
73 ms.write( &iTmp, 4 ); // iPosition
74 hNodeIndex.insert( 0, 0 );
75 }
76
77 // Create initial inode stream, with one root node.
78 {
79 mStore.createStream( 2 );
80 Bu::MyriadStream ms = mStore.openStream( 2 );
81 RawStat rs;
82 rs.iNode = 0;
83 rs.iUser = iUser;
84 rs.iGroup = iGroup;
85 rs.uPerms = 0755|typeDir;
86 rs.iLinks = 1;
87 rs.uStreamIndex = 3;
88 rs.iCTime = rs.iMTime = rs.iATime = time(NULL);
89 ms.write( &rs, sizeof(RawStat) );
90 }
91
92 // Create inode 0's storage stream.
93 {
94 mStore.createStream( 3 );
95 Bu::MyriadStream ms = mStore.openStream( 3 );
96 int32_t iTmp32 = 0;
97 ms.write( &iTmp32, 4 ); // iChildCount
98 }
99 }
100}
101
102Bu::MyriadFs::~MyriadFs()
103{
104 writeHeader();
105}
106
107void Bu::MyriadFs::stat( const Bu::String &sPath, Bu::MyriadFs::Stat &rBuf )
108{
109 int32_t iParent;
110 int32_t iNode = lookupInode( sPath, iParent );
111 Bu::MyriadStream is = mStore.openStream( 2 );
112 stat( iNode, rBuf, is );
113}
114
115Bu::MyriadStream Bu::MyriadFs::open( const Bu::String &sPath, int /*iMode*/,
116 uint16_t uPerms )
117{
118 int32_t iParent = -1;
119 int32_t iNode;
120 try
121 {
122 iNode = lookupInode( sPath, iParent );
123 sio << "File found." << sio.nl;
124 // The file was found
125 return openByInode( iNode );
126 }
127 catch( Bu::MyriadFsException &e )
128 {
129 if( iParent < 0 )
130 throw;
131
132 // This means that an intermediate path component couldn't be found
133 if( e.getErrorCode() == 1 )
134 throw;
135
136 // The file wasn't found, but the path leading up to it was.
137 // first, figure out the final path element...
138 Bu::String sName = filePart( sPath );
139 sio << "End filename: " << sName << sio.nl;
140 sio << "Parent inode: " << iParent << sio.nl;
141 iNode = create( iParent, sName, (uPerms&permMask)|typeRegFile, 0 );
142 sio << "New iNode: " << iNode << sio.nl;
143 return openByInode( iNode );
144 }
145}
146
147void Bu::MyriadFs::create( const Bu::String &sPath, uint16_t iPerms )
148{
149 create( sPath, iPerms, 0 );
150}
151
152void Bu::MyriadFs::create( const Bu::String &sPath, uint16_t iPerms,
153 uint16_t iDevHi, uint16_t iDevLo )
154{
155 create( sPath, iPerms, ((uint32_t)iDevHi<<16)|(uint32_t)iDevLo );
156}
157
158void Bu::MyriadFs::create( const Bu::String &sPath, uint16_t iPerms,
159 uint32_t uSpecial )
160{
161 int32_t iParent = -1;
162 int32_t iNode;
163 try
164 {
165 iNode = lookupInode( sPath, iParent );
166 sio << "File found." << sio.nl;
167 // The file was found
168 throw Bu::MyriadFsException("Path already exists.");
169 }
170 catch( Bu::MyriadFsException &e )
171 {
172 if( iParent < 0 )
173 throw;
174
175 // This means that an intermediate path component couldn't be found
176 if( e.getErrorCode() == 1 )
177 throw;
178
179 // The file wasn't found, but the path leading up to it was.
180 // first, figure out the final path element...
181 Bu::String sName = filePart( sPath );
182 sio << "End filename: " << sName << sio.nl;
183 sio << "Parent inode: " << iParent << sio.nl;
184 iNode = create( iParent, sName, iPerms, uSpecial );
185 sio << "New iNode: " << iNode << sio.nl;
186 }
187}
188
189void Bu::MyriadFs::mkDir( const Bu::String &sPath, uint16_t iPerms )
190{
191 create( sPath, (iPerms&permMask)|typeDir, 0 );
192}
193
194void Bu::MyriadFs::mkSymLink( const Bu::String &sTarget,
195 const Bu::String &sPath )
196{
197 int32_t iParent = -1;
198 int32_t iNode;
199 try
200 {
201 iNode = lookupInode( sPath, iParent );
202 throw Bu::MyriadFsException("Path already exists.");
203 }
204 catch( Bu::MyriadFsException &e )
205 {
206 if( iParent < 0 )
207 throw;
208
209 // This means that an intermediate path component couldn't be found
210 if( e.getErrorCode() == 1 )
211 throw;
212
213 // The file wasn't found, but the path leading up to it was.
214 // first, figure out the final path element...
215 Bu::String sName = filePart( sPath );
216 sio << "End filename: " << sName << sio.nl;
217 sio << "Parent inode: " << iParent << sio.nl;
218 iNode = create( iParent, sName, 0777|typeSymLink, 0 );
219 sio << "New iNode: " << iNode << sio.nl;
220 MyriadStream ms = openByInode( iNode );
221 ms.write( sTarget );
222 }
223}
224
225void Bu::MyriadFs::mkHardLink( const Bu::String &sTarget,
226 const Bu::String &sPath )
227{
228 int32_t iParent = -1;
229 int32_t iNode;
230
231 iNode = lookupInode( sTarget, iParent );
232
233 try
234 {
235 lookupInode( sPath, iParent );
236 throw Bu::MyriadFsException("Path already exists.");
237 }
238 catch( Bu::MyriadFsException &e )
239 {
240 if( iParent < 0 )
241 throw;
242
243 // This means that an intermediate path component couldn't be found
244 if( e.getErrorCode() == 1 )
245 throw;
246
247 // The file wasn't found, but the path leading up to it was.
248 // first, figure out the final path element...
249 Bu::String sName = filePart( sPath );
250 sio << "End filename: " << sName << sio.nl;
251 sio << "Parent inode: " << iParent << sio.nl;
252 addToDir( iParent, iNode, sName );
253 MyriadStream is = mStore.openStream( 2 );
254 RawStat rs;
255 readInode( iNode, rs, is );
256 rs.iLinks++;
257 writeInode( rs, is );
258 }
259}
260
261Bu::String Bu::MyriadFs::readSymLink( const Bu::String &sPath )
262{
263 int32_t iParent = -1;
264 int32_t iNode;
265 iNode = lookupInode( sPath, iParent );
266 MyriadStream ms = openByInode( iNode );
267 Bu::String sRet;
268 sRet.setSize( ms.getSize() );
269 ms.read( sRet.getStr(), ms.getSize() );
270 return sRet;
271}
272
273Bu::MyriadFs::Dir Bu::MyriadFs::readDir( const Bu::String &sPath )
274{
275 int32_t iParent = -1;
276 int32_t iNode = lookupInode( sPath, iParent );
277 return readDir( iNode );
278}
279
280void Bu::MyriadFs::setTimes( const Bu::String &sPath, int64_t iATime,
281 int64_t iMTime )
282{
283 int32_t iParent = -1;
284 int32_t iNode;
285
286 iNode = lookupInode( sPath, iParent );
287
288 setTimes( iNode, iATime, iMTime );
289}
290
291void Bu::MyriadFs::unlink( const Bu::String &sPath )
292{
293 int32_t iParent = -1;
294// int32_t iNode;
295
296 /*iNode =*/ lookupInode( sPath, iParent );
297
298 Dir lDir = readDir( iParent );
299
300 Bu::String sName = filePart( sPath );
301
302 for( Dir::iterator i = lDir.begin(); i; i++ )
303 {
304 if( sName == (*i).sName )
305 {
306 RawStat rs;
307 readInode( (*i).iNode, rs );
308 if( (rs.uPerms&typeMask) == typeDir )
309 {
310 MyriadStream msDir = mStore.openStream( rs.uStreamIndex );
311 int32_t iCount;
312 msDir.read( &iCount, 4 );
313 if( iCount > 0 )
314 {
315 throw Bu::MyriadFsException("Directory not empty.");
316 }
317 }
318 if( --rs.iLinks == 0 )
319 {
320 destroyNode( (*i).iNode );
321 }
322 else
323 {
324 writeInode( rs );
325 }
326 lDir.erase( i );
327 break;
328 }
329 }
330
331 Bu::MyriadStream ms = openByInode( iParent );
332 int32_t iNumChildren = lDir.getSize();
333 ms.write( &iNumChildren, 4 );
334 for( Dir::iterator i = lDir.begin(); i; i++ )
335 {
336 ms.write( &(*i).iNode, 4 );
337 uint8_t iSize = (*i).sName.getSize();
338 ms.write( &iSize, 1 );
339 ms.write( (*i).sName.getStr(), iSize );
340 }
341 ms.setSize( ms.tell() );
342}
343
344void Bu::MyriadFs::setFileSize( const Bu::String &sPath, int32_t iSize )
345{
346 int32_t iParent = -1;
347 int32_t iNode;
348 iNode = lookupInode( sPath, iParent );
349 MyriadStream ms = openByInode( iNode );
350 ms.setSize( iSize );
351}
352
353void Bu::MyriadFs::rename( const Bu::String &sFrom, const Bu::String &sTo )
354{
355 mkHardLink( sFrom, sTo );
356 unlink( sFrom );
357}
358
359dev_t Bu::MyriadFs::devToSys( uint32_t uDev )
360{
361 return (((uDev&0xFFFF0000)>>8)&0xFF00) | ((uDev&0xFF));
362}
363
364uint32_t Bu::MyriadFs::sysToDev( dev_t uDev )
365{
366 return (((uint32_t)uDev&0xFF00)<<8) | ((uint32_t)uDev&0xFF);
367}
368
369int32_t Bu::MyriadFs::lookupInode( const Bu::String &sPath, int32_t &iParent )
370{
371 if( sPath == "/" )
372 {
373 return 0;
374 }
375 if( sPath[0] == '/' )
376 {
377 // Absolute lookup
378 return lookupInode( sPath.begin()+1, 0, iParent );
379 }
380 else
381 {
382 // Relative lookup
383 throw Bu::ExceptionBase(
384 "Relative lookups in MyriadFs are not working yet.");
385 }
386}
387
388int32_t Bu::MyriadFs::lookupInode( Bu::String::const_iterator iStart,
389 int32_t iNode, int32_t &iParent )
390{
391 iParent = iNode;
392
393 Bu::String::const_iterator iEnd = iStart.find('/');
394 Bu::String sTok( iStart, iEnd );
395
396// sio << "Direcotry component: " << sTok << sio.nl;
397
398 Dir lDir = readDir( iNode );
399
400 for( Dir::iterator i = lDir.begin(); i; i++ )
401 {
402 if( (*i).sName == sTok )
403 {
404 // We found an item
405 if( !iEnd )
406 {
407 // It's the last one in the requested path, return it
408 return (*i).iNode;
409 }
410 else
411 {
412 // Not the last one in our path, double check it's a dir
413 if( ((*i).uPerms&typeMask) == typeDir )
414 {
415 return lookupInode( iEnd+1, (*i).iNode, iParent );
416 }
417 else
418 {
419 iParent = -1;
420 throw Bu::MyriadFsException(
421 "Element '%s' in given path is not a directory.",
422 sTok.getStr() );
423 }
424 }
425 }
426 }
427
428 if( iEnd )
429 throw Bu::MyriadFsException( 1, "Path not found");
430 else
431 throw Bu::MyriadFsException( 2, "Path not found");
432}
433
434void Bu::MyriadFs::readInode( int32_t iNode, RawStat &rs, MyriadStream &rIs )
435{
436 rIs.setPos( hNodeIndex.get( iNode )*sizeof(RawStat) );
437 if( rIs.read( &rs, sizeof(RawStat) ) < sizeof(RawStat) )
438 throw Bu::MyriadFsException("Filesystem corruption detected.");
439 if( rs.iNode != iNode )
440 throw Bu::MyriadFsException("Filesystem corruption detected.");
441}
442
443void Bu::MyriadFs::readInode( int32_t iNode, RawStat &rs )
444{
445 MyriadStream ms = mStore.openStream( 2 );
446 readInode( iNode, rs, ms );
447}
448
449void Bu::MyriadFs::writeInode( const RawStat &rs,
450 MyriadStream &rOs )
451{
452 rOs.setSize( hNodeIndex.getSize()*sizeof(RawStat) );
453 rOs.setPos( hNodeIndex.get( rs.iNode )*sizeof(RawStat) );
454 if( rOs.write( &rs, sizeof(RawStat) ) < sizeof(RawStat) )
455 throw Bu::MyriadFsException("Error writing inode to header stream.");
456}
457
458void Bu::MyriadFs::writeInode( const RawStat &rs )
459{
460 MyriadStream ms = mStore.openStream( 2 );
461 writeInode( rs, ms );
462}
463
464Bu::MyriadFs::Dir Bu::MyriadFs::readDir( int32_t iNode )
465{
466 Bu::MyriadStream ms = openByInode( iNode );
467 int32_t iNumChildren = 0;
468 ms.read( &iNumChildren, 4 );
469
470 Bu::MyriadStream is = mStore.openStream( 2 );
471 Dir lDir;
472// sio << "Reading dir, " << iNumChildren << " entries:" << sio.nl;
473 for( int32_t j = 0; j < iNumChildren; j++ )
474 {
475 int32_t iChildNode;
476 ms.read( &iChildNode, 4 );
477 Stat s;
478 stat( iChildNode, s, is );
479 uint8_t uLen;
480 ms.read( &uLen, 1 );
481 s.sName.setSize( uLen );
482 ms.read( s.sName.getStr(), uLen );
483 lDir.append( s );
484
485// sio << " " << s.sName << sio.nl;
486 }
487
488 return lDir;
489}
490
491Bu::MyriadStream Bu::MyriadFs::openByInode( int32_t iNode )
492{
493 RawStat rs;
494 readInode( iNode, rs );
495 switch( (rs.uPerms&typeMask) )
496 {
497 case typeDir:
498 case typeSymLink:
499 case typeRegFile:
500 return mStore.openStream( rs.uStreamIndex );
501
502 default:
503 throw Bu::MyriadFsException(
504 "inode incorrect type for low-level openByInode.");
505 }
506}
507
508void Bu::MyriadFs::addToDir( int32_t iDir, int32_t iNode,
509 const Bu::String &sName )
510{
511 if( sName.getSize() > 255 )
512 {
513 throw Bu::MyriadFsException("Filename too long, max is 255 bytes.");
514 }
515 Bu::MyriadStream ms = openByInode( iDir );
516 int32_t iNumChildren = 0;
517 ms.read( &iNumChildren, 4 );
518 iNumChildren++;
519 ms.setPos( 0 );
520 ms.write( &iNumChildren, 4 );
521 ms.setPosEnd( 0 );
522 ms.write( &iNode, 4 );
523 uint8_t uLen = sName.getSize();
524 ms.write( &uLen, 1 );
525 ms.write( sName.getStr(), uLen );
526}
527
528int32_t Bu::MyriadFs::create( int32_t iParent, const Bu::String &sName,
529 uint16_t uPerms, uint32_t uSpecial )
530{
531 int32_t iNode = allocInode( uPerms, uSpecial );
532 addToDir( iParent, iNode, sName );
533 return iNode;
534}
535
536int32_t Bu::MyriadFs::allocInode( uint16_t uPerms, uint32_t uSpecial )
537{
538 int32_t iNode = 0;
539 for(; iNode < 0xfffffff; iNode++ )
540 {
541 if( !hNodeIndex.has( iNode ) )
542 {
543 hNodeIndex.insert( iNode, hNodeIndex.getSize() );
544 RawStat rs;
545 rs.iNode = iNode;
546 rs.iUser = iUser;
547 rs.iGroup = iGroup;
548 rs.uPerms = uPerms;
549 rs.iLinks = 1;
550 switch( (uPerms&typeMask) )
551 {
552 case typeRegFile:
553 case typeSymLink:
554 rs.uStreamIndex = mStore.createStream();
555 break;
556
557 case typeDir:
558 rs.uStreamIndex = mStore.createStream();
559 sio << "Creating directory node, storage: "
560 << rs.uStreamIndex << sio.nl;
561 {
562 Bu::MyriadStream msDir = mStore.openStream(
563 rs.uStreamIndex
564 );
565 uint32_t uSize = 0;
566 msDir.write( &uSize, 4 );
567 }
568 break;
569
570 case typeChrDev:
571 case typeBlkDev:
572 rs.uStreamIndex = uSpecial;
573 break;
574
575 default:
576 rs.uStreamIndex = 0;
577 break;
578 }
579 rs.iATime = time(NULL);
580 rs.iMTime = time(NULL);
581 rs.iCTime = time(NULL);
582 writeInode( rs );
583
584 return iNode;
585 }
586 }
587
588 throw Bu::MyriadFsException(
589 "No inode could be allocated. You've run out!");
590}
591
592void Bu::MyriadFs::stat( int32_t iNode, Stat &rBuf, MyriadStream &rIs )
593{
594 RawStat rs;
595 readInode( iNode, rs, rIs );
596 rBuf.iNode = iNode;
597 rBuf.iUser = rs.iUser;
598 rBuf.iGroup = rs.iGroup;
599 rBuf.uPerms = rs.uPerms;
600 rBuf.iLinks = rs.iLinks;
601 rBuf.iATime = rs.iATime;
602 rBuf.iMTime = rs.iMTime;
603 rBuf.iCTime = rs.iCTime;
604 rBuf.uDev = 0;
605 rBuf.iSize = 0;
606 switch( (rBuf.uPerms&typeMask) )
607 {
608 case typeRegFile:
609 case typeSymLink:
610 rBuf.iSize = mStore.getStreamSize( rs.uStreamIndex );
611 break;
612
613 case typeChrDev:
614 case typeBlkDev:
615 rBuf.uDev = rs.uStreamIndex;
616 break;
617
618 default:
619 rBuf.iSize = 0;
620 break;
621 }
622}
623
624void Bu::MyriadFs::writeHeader()
625{
626 Bu::MyriadStream ms = mStore.openStream( 1 );
627 ms.write( Myriad_Fs_MAGIC_CODE, 4 );
628 int8_t iVer = 1;
629 int32_t iNumNodes = hNodeIndex.getSize();
630 ms.write( &iVer, 1 );
631 ms.write( &iNumNodes, 4 ); // iNumNodes
632 for( NodeIndex::iterator i = hNodeIndex.begin(); i; i++ )
633 {
634 int32_t iNode = i.getKey();
635 int32_t iPosition = i.getValue();
636 ms.write( &iNode, 4 );
637 ms.write( &iPosition, 4 );
638 }
639
640 // Truncate the stream afterwards so we don't use up too much space.
641 ms.setSize( ms.tell() );
642}
643
644void Bu::MyriadFs::setTimes( int32_t iNode, int64_t iATime, int64_t iMTime )
645{
646 RawStat rs;
647 Bu::MyriadStream is = mStore.openStream( 2 );
648
649 readInode( iNode, rs, is );
650 rs.iATime = iATime;
651 rs.iMTime = iMTime;
652 writeInode( rs, is );
653}
654
655void Bu::MyriadFs::destroyNode( int32_t iNode )
656{
657 if( iNode == 0 )
658 throw Bu::MyriadFsException("You cannot destroy the root.");
659
660 Bu::MyriadStream is = mStore.openStream( 2 );
661
662 // This will be overwritten with the last node
663 uint32_t iPosition = hNodeIndex.get( iNode );
664 RawStat rsOld;
665 readInode( iNode, rsOld, is );
666 switch( (rsOld.uPerms&typeMask) )
667 {
668 case typeRegFile:
669 case typeDir:
670 case typeSymLink:
671 mStore.deleteStream( rsOld.uStreamIndex );
672 break;
673 }
674
675 hNodeIndex.erase( iNode );
676
677 // Read the last node, can't use the helpers, because we don't know the
678 // iNode yet.
679 if( iPosition != hNodeIndex.getSize() )
680 {
681 // If this is the last node, then we don't need to do anything, but
682 // this case handles what to do if we aren't on the last node
683 RawStat rs;
684 is.setPos( (hNodeIndex.getSize())*sizeof(RawStat) );
685 is.read( &rs, sizeof(RawStat) );
686
687 hNodeIndex.get( rs.iNode ) = iPosition;
688 writeInode( rs, is );
689 }
690
691 is.setSize( hNodeIndex.getSize() * sizeof(RawStat) );
692}
693
694Bu::String Bu::MyriadFs::filePart( const Bu::String &sPath )
695{
696 Bu::String::const_iterator iStart = sPath.begin();
697 if( *iStart == '/' )
698 iStart++;
699 for( Bu::String::const_iterator iEnd = iStart.find('/'); iEnd;
700 iStart = iEnd+1, iEnd = iStart.find('/') ) { }
701 return Bu::String( iStart, sPath.end() );
702}
703