1 /*
2 * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7 #include <dirent.h>
8 #include <string.h>
9 #include <sys/stat.h>
10 #include <unistd.h>
11
12 #include <new>
13
14 #include <fs_interface.h>
15 #include <io_requests.h>
16 #include <NodeMonitor.h>
17
18 #include <AutoDeleter.h>
19 #include <AutoLocker.h>
20
21 #include <debug.h>
22 #include <util/AutoLock.h>
23
24 #include "checksumfs.h"
25 #include "checksumfs_private.h"
26 #include "DebugSupport.h"
27 #include "Directory.h"
28 #include "File.h"
29 #include "Notifications.h"
30 #include "SuperBlock.h"
31 #include "SymLink.h"
32 #include "Transaction.h"
33 #include "Volume.h"
34
35
36 static const char* const kCheckSumFSModuleName = "file_systems/checksumfs"
37 B_CURRENT_FS_API_VERSION;
38 static const char* const kCheckSumFSShortName = "checksumfs";
39
40 static const bigtime_t kModifiedInterimUpdateInterval = 500000;
41 // wait at least 0.5s between interim modified updates
42
43
44 // #pragma mark -
45
46
47 struct FileCookie {
48 mutex lock;
49 int openMode;
50 bigtime_t lastModifiedUpdate;
51 bool modifiedNeedsUpdate;
52 bool sizeChangedSinceUpdate;
53 bool modifiedNeedsFinalUpdate;
54 bool finalSizeChanged;
55
56
FileCookieFileCookie57 FileCookie(int openMode)
58 :
59 openMode(openMode),
60 lastModifiedUpdate(0),
61 modifiedNeedsUpdate(false),
62 sizeChangedSinceUpdate(false),
63 modifiedNeedsFinalUpdate(false),
64 finalSizeChanged(false)
65 {
66 mutex_init(&lock, "checksumfs file cookie");
67 }
68
~FileCookieFileCookie69 ~FileCookie()
70 {
71 mutex_destroy(&lock);
72 }
73
UpdateModifiedIfNecessaryFileCookie74 status_t UpdateModifiedIfNecessary(Node* node, bool finalUpdate)
75 {
76 MutexLocker locker(lock);
77
78 return _UpdateModifiedIfNecessary(node, finalUpdate);
79 }
80
FileModifiedFileCookie81 status_t FileModified(Node* node, bool sizeChanged)
82 {
83 MutexLocker locker(lock);
84
85 modifiedNeedsUpdate = true;
86 modifiedNeedsFinalUpdate = true;
87 sizeChangedSinceUpdate |= sizeChanged;
88 finalSizeChanged |= sizeChanged;
89
90 return _UpdateModifiedIfNecessary(node, false);
91 }
92
93 private:
_UpdateModifiedIfNecessaryFileCookie94 status_t _UpdateModifiedIfNecessary(Node* node, bool finalUpdate)
95 {
96 uint32 statFlags = B_STAT_MODIFICATION_TIME | B_STAT_CHANGE_TIME;
97
98 if (finalUpdate) {
99 if (!modifiedNeedsFinalUpdate)
100 return B_OK;
101 if (finalSizeChanged)
102 statFlags |= B_STAT_SIZE;
103 } else {
104 if (!modifiedNeedsUpdate)
105 return B_OK;
106
107 if (system_time()
108 < lastModifiedUpdate + kModifiedInterimUpdateInterval) {
109 // not enough time passed -- postpone update
110 return B_OK;
111 }
112
113 statFlags |= B_STAT_INTERIM_UPDATE
114 | (sizeChangedSinceUpdate ? B_STAT_SIZE : 0);
115 }
116
117 // do the update -- start a transaction, lock the node, and update
118 Transaction transaction(node->GetVolume());
119 status_t error = transaction.StartAndAddNode(node);
120 if (error != B_OK)
121 return error;
122
123 node->Touched(NODE_MODIFIED);
124
125 error = transaction.Commit(StatChangedNotification(node, statFlags));
126 if (error != B_OK)
127 return error;
128
129 modifiedNeedsUpdate = false;
130 lastModifiedUpdate = system_time();
131
132 return B_OK;
133 }
134 };
135
136
137 struct DirCookie {
DirCookieDirCookie138 DirCookie(Directory* directory)
139 :
140 fDirectory(directory)
141 {
142 Rewind();
143 }
144
GetDirectoryDirCookie145 Directory* GetDirectory() const
146 {
147 return fDirectory;
148 }
149
SetToDirCookie150 void SetTo(Directory* directory, bool skipArtificialEntries)
151 {
152 fDirectory = directory;
153 Rewind(skipArtificialEntries);
154 }
155
ReadNextEntryDirCookie156 status_t ReadNextEntry(struct dirent* buffer, size_t size,
157 uint32& _countRead)
158 {
159 const char* name;
160 size_t nameLength;
161 uint64 blockIndex;
162
163 int nextIterationState = OTHERS;
164 switch (fIterationState) {
165 case DOT:
166 name = ".";
167 nameLength = 1;
168 blockIndex = fDirectory->BlockIndex();
169 nextIterationState = DOT_DOT;
170 break;
171 case DOT_DOT:
172 name = "..";
173 nameLength = 2;
174 blockIndex = fDirectory->ParentDirectory();
175 break;
176 default:
177 {
178 status_t error = fDirectory->LookupNextEntry(fEntryName,
179 fEntryName, nameLength, blockIndex);
180 if (error != B_OK) {
181 if (error != B_ENTRY_NOT_FOUND)
182 return error;
183
184 _countRead = 0;
185 return B_OK;
186 }
187
188 name = fEntryName;
189 break;
190 }
191 }
192
193 size_t entrySize = sizeof(dirent) + nameLength + 1;
194 if (entrySize > size)
195 return B_BUFFER_OVERFLOW;
196
197 buffer->d_dev = fDirectory->GetVolume()->ID();
198 buffer->d_ino = blockIndex;
199 buffer->d_reclen = entrySize;
200 strcpy(buffer->d_name, name);
201
202 fIterationState = nextIterationState;
203
204 _countRead = 1;
205 return B_OK;
206 }
207
RewindDirCookie208 void Rewind(bool skipArtificialEntries = false)
209 {
210 fIterationState = skipArtificialEntries ? OTHERS : DOT;
211 fEntryName[0] = '\0';
212 }
213
214 private:
215 enum {
216 DOT,
217 DOT_DOT,
218 OTHERS
219 };
220
221 Directory* fDirectory;
222 int fIterationState;
223 char fEntryName[kCheckSumFSNameLength + 1];
224 };
225
226
227 struct AttrDirCookie {
AttrDirCookieAttrDirCookie228 AttrDirCookie(Node* node)
229 :
230 fNode(node),
231 fAttributeDirectory(NULL),
232 fDirCookie(NULL)
233 {
234 }
235
~AttrDirCookieAttrDirCookie236 ~AttrDirCookie()
237 {
238 if (fAttributeDirectory != NULL)
239 fNode->GetVolume()->PutNode(fAttributeDirectory);
240 }
241
ReadNextEntryAttrDirCookie242 status_t ReadNextEntry(struct dirent* buffer, size_t size,
243 uint32& _countRead)
244 {
245 status_t error = _UpdateAttributeDirectory();
246 if (error != B_OK)
247 RETURN_ERROR(error);
248
249 if (fAttributeDirectory == NULL) {
250 _countRead = 0;
251 return B_OK;
252 }
253
254 return fDirCookie.ReadNextEntry(buffer, size, _countRead);
255 }
256
RewindAttrDirCookie257 void Rewind()
258 {
259 fDirCookie.Rewind(true);
260 }
261
262 private:
_UpdateAttributeDirectoryAttrDirCookie263 status_t _UpdateAttributeDirectory()
264 {
265 uint64 blockIndex = fNode->AttributeDirectory();
266 if (blockIndex == 0) {
267 // no (empty) attribute directory
268 if (fAttributeDirectory != NULL) {
269 fNode->GetVolume()->PutNode(fAttributeDirectory);
270 fAttributeDirectory = NULL;
271 }
272
273 return B_OK;
274 }
275
276 if (fAttributeDirectory != NULL) {
277 if (blockIndex == fAttributeDirectory->BlockIndex())
278 return B_OK;
279
280 // The attribute directory has changed in the meantime -- get rid
281 // of the old one.
282 fNode->GetVolume()->PutNode(fAttributeDirectory);
283 fAttributeDirectory = NULL;
284 }
285
286 // get the attribute directory node
287 Node* node;
288 status_t error = fNode->GetVolume()->GetNode(blockIndex, node);
289 if (error != B_OK)
290 RETURN_ERROR(error);
291
292 fAttributeDirectory = dynamic_cast<Directory*>(node);
293 if (fAttributeDirectory == NULL) {
294 fNode->GetVolume()->PutNode(node);
295 ERROR("checksumfs: attribute directory (%" B_PRIu64 ") of node %"
296 B_PRIu64 " is not a directory!\n", blockIndex,
297 fNode->BlockIndex());
298 RETURN_ERROR(B_BAD_DATA);
299 }
300
301 fDirCookie.SetTo(fAttributeDirectory, true);
302
303 return B_OK;
304 }
305
306 private:
307 Node* fNode;
308 Directory* fAttributeDirectory;
309 DirCookie fDirCookie;
310 };
311
312
313 struct AttributeCookie {
314 char* name;
315 Node* attribute;
316 FileCookie* fileCookie;
317
AttributeCookieAttributeCookie318 AttributeCookie(const char* name)
319 :
320 name(strdup(name)),
321 attribute(NULL),
322 fileCookie(NULL)
323 {
324 }
325
~AttributeCookieAttributeCookie326 ~AttributeCookie()
327 {
328 if (attribute != NULL)
329 attribute->GetVolume()->PutNode(attribute);
330 delete fileCookie;
331 free(name);
332 }
333 };
334
335
336 // #pragma mark -
337
338
339 static void
set_timespec(timespec & time,uint64 nanos)340 set_timespec(timespec& time, uint64 nanos)
341 {
342 time.tv_sec = nanos / 1000000000;
343 time.tv_nsec = nanos % 1000000000;
344 }
345
346
347 static uint64
timespec_to_nsecs(const timespec & time)348 timespec_to_nsecs(const timespec& time)
349 {
350 return (uint64)time.tv_sec * 1000000000 + time.tv_nsec;
351 }
352
353
354 struct PutNode {
operator ()PutNode355 inline void operator()(Node* node)
356 {
357 if (node != NULL)
358 node->GetVolume()->PutNode(node);
359 }
360 };
361
362 typedef BPrivate::AutoDeleter<Node, PutNode> NodePutter;
363
364
365 static bool
is_user_in_group(gid_t gid)366 is_user_in_group(gid_t gid)
367 {
368 gid_t groups[NGROUPS_MAX];
369 int groupCount = getgroups(NGROUPS_MAX, groups);
370 for (int i = 0; i < groupCount; i++) {
371 if (gid == groups[i])
372 return true;
373 }
374
375 return gid == getegid();
376 }
377
378
379 static status_t
check_access(Node * node,uint32 accessFlags)380 check_access(Node* node, uint32 accessFlags)
381 {
382 // Note: we assume that the access flags are compatible with the permission
383 // bits.
384 STATIC_ASSERT(R_OK == S_IROTH && W_OK == S_IWOTH && X_OK == S_IXOTH);
385
386 // get node permissions
387 int userPermissions = (node->Mode() & S_IRWXU) >> 6;
388 int groupPermissions = (node->Mode() & S_IRWXG) >> 3;
389 int otherPermissions = node->Mode() & S_IRWXO;
390
391 // get the permissions for this uid/gid
392 int permissions = 0;
393 uid_t uid = geteuid();
394
395 if (uid == 0) {
396 // user is root
397 // root has always read/write permission, but at least one of the
398 // X bits must be set for execute permission
399 permissions = userPermissions | groupPermissions | otherPermissions
400 | R_OK | W_OK;
401 } else if (uid == node->UID()) {
402 // user is node owner
403 permissions = userPermissions;
404 } else if (is_user_in_group(node->GID())) {
405 // user is in owning group
406 permissions = groupPermissions;
407 } else {
408 // user is one of the others
409 permissions = otherPermissions;
410 }
411
412 return (accessFlags & ~permissions) == 0 ? B_OK : B_NOT_ALLOWED;
413 }
414
415
416 static status_t
remove_entry(fs_volume * fsVolume,fs_vnode * parent,const char * name,bool removeDirectory)417 remove_entry(fs_volume* fsVolume, fs_vnode* parent, const char* name,
418 bool removeDirectory)
419 {
420 Volume* volume = (Volume*)fsVolume->private_volume;
421 Directory* directory
422 = dynamic_cast<Directory*>((Node*)parent->private_node);
423 if (directory == NULL)
424 return B_NOT_A_DIRECTORY;
425
426 if (volume->IsReadOnly())
427 return B_READ_ONLY_DEVICE;
428
429 // Since we need to lock both nodes (the directory and the entry's), this
430 // is a bit cumbersome. We first look up the entry while having the
431 // directory read-locked, then drop the read lock, write-lock both nodes
432 // and check whether anything has changed.
433 Transaction transaction(volume);
434 Node* childNode;
435 NodePutter childNodePutter;
436
437 while (true) {
438 // look up the entry
439 NodeReadLocker directoryLocker(directory);
440
441 uint64 blockIndex;
442 status_t error = directory->LookupEntry(name, blockIndex);
443 if (error != B_OK)
444 RETURN_ERROR(error);
445
446 directoryLocker.Unlock();
447
448 // get the entry's node
449 error = volume->GetNode(blockIndex, childNode);
450 if (error != B_OK)
451 RETURN_ERROR(error);
452 childNodePutter.SetTo(childNode);
453
454 // start the transaction
455 error = transaction.Start();
456 if (error != B_OK)
457 RETURN_ERROR(error);
458
459 // write-lock the nodes
460 error = transaction.AddNodes(directory, childNode);
461 if (error != B_OK)
462 RETURN_ERROR(error);
463
464 // check the situation again
465 error = directory->LookupEntry(name, blockIndex);
466 if (error != B_OK)
467 RETURN_ERROR(error);
468 if (blockIndex != childNode->BlockIndex()) {
469 transaction.Abort();
470 continue;
471 }
472
473 break;
474 }
475
476 // check permissions
477 status_t error = check_access(directory, W_OK);
478 if (error != B_OK)
479 return error;
480
481 // check whether the child node type agrees with our caller
482 if (removeDirectory) {
483 if (!S_ISDIR(childNode->Mode()))
484 RETURN_ERROR(B_NOT_A_DIRECTORY);
485
486 // directory must be empty
487 if (childNode->Size() > 0)
488 RETURN_ERROR(B_DIRECTORY_NOT_EMPTY);
489 } else if (S_ISDIR(childNode->Mode()))
490 RETURN_ERROR(B_IS_A_DIRECTORY);
491
492 // remove the entry
493 error = directory->RemoveEntry(name, transaction);
494 if (error != B_OK)
495 RETURN_ERROR(error);
496
497 // update stat data
498 childNode->SetHardLinks(childNode->HardLinks() - 1);
499
500 directory->Touched(NODE_MODIFIED);
501
502 // remove the child node, if no longer referenced
503 if (childNode->HardLinks() == 0) {
504 error = volume->RemoveNode(childNode);
505 if (error != B_OK)
506 return error;
507 }
508
509 // commit the transaction
510 return transaction.Commit(EntryRemovedNotification(directory, name,
511 childNode));
512 }
513
514
515 /*! Opens the node according to the given open mode (if the permissions allow
516 that) and creates a file cookie.
517 */
518 static status_t
open_file(Volume * volume,Node * node,int openMode,Transaction & transaction,bool commitTransaction,FileCookie * & _cookie)519 open_file(Volume* volume, Node* node, int openMode, Transaction& transaction,
520 bool commitTransaction, FileCookie*& _cookie)
521 {
522 // translate the open mode to required permissions
523 uint32 accessFlags = 0;
524 switch (openMode & O_RWMASK) {
525 case O_RDONLY:
526 accessFlags = R_OK;
527 break;
528 case O_WRONLY:
529 accessFlags = W_OK;
530 break;
531 case O_RDWR:
532 accessFlags = R_OK | W_OK;
533 break;
534 }
535
536 // We need to at least read-lock the node. If O_TRUNC is specified, we even
537 // need a write lock and a transaction.
538 NodeReadLocker nodeReadLocker;
539
540 if ((openMode & O_TRUNC) != 0) {
541 accessFlags |= W_OK;
542
543 status_t error = transaction.IsActive()
544 ? transaction.AddNode(node) : transaction.StartAndAddNode(node);
545 if (error != B_OK)
546 RETURN_ERROR(error);
547 } else if (!transaction.IsNodeLocked(node))
548 nodeReadLocker.SetTo(node, false);
549
550 // check permissions
551 if ((accessFlags & W_OK) != 0) {
552 if (volume->IsReadOnly())
553 return B_READ_ONLY_DEVICE;
554 if (S_ISDIR(node->Mode()))
555 return B_IS_A_DIRECTORY;
556 }
557
558 if ((openMode & O_DIRECTORY) != 0 && !S_ISDIR(node->Mode()))
559 return B_NOT_A_DIRECTORY;
560
561 status_t error = check_access(node, accessFlags);
562 if (error != B_OK)
563 return error;
564
565 // TODO: Support O_NOCACHE.
566
567 FileCookie* cookie = new(std::nothrow) FileCookie(openMode);
568 if (cookie == NULL)
569 return B_NO_MEMORY;
570 ObjectDeleter<FileCookie> cookieDeleter(cookie);
571
572 // truncate the file, if requested
573 if ((openMode & O_TRUNC) != 0 && node->Size() > 0) {
574 error = node->Resize(0, false, transaction);
575 if (error != B_OK)
576 return error;
577
578 node->Touched(NODE_MODIFIED);
579
580 if (commitTransaction) {
581 uint32 statFlags = B_STAT_MODIFICATION_TIME | B_STAT_CHANGE_TIME
582 | B_STAT_SIZE;
583 error = transaction.Commit(StatChangedNotification(node,
584 statFlags));
585 if (error != B_OK)
586 return error;
587 }
588 }
589
590 _cookie = cookieDeleter.Detach();
591 return B_OK;
592 }
593
594
595 static status_t
create_file(Volume * volume,Directory * directory,const char * name,int openMode,int permissions,Transaction & transaction,bool commitTransaction,FileCookie * & _cookie,Node * & _node,bool & _created)596 create_file(Volume* volume, Directory* directory, const char* name,
597 int openMode, int permissions, Transaction& transaction,
598 bool commitTransaction, FileCookie*& _cookie, Node*& _node, bool& _created)
599 {
600 Node* childNode = NULL;
601 NodePutter childNodePutter;
602
603 // Start the transaction and add the directory. We only need a read lock
604 // for the lookup, but later we'll need a write lock, if we have to create
605 // the file. So this is simpler.
606 status_t error = B_OK;
607 bool directoryLocked = false;
608 if (transaction.IsActive()) {
609 directoryLocked = transaction.IsNodeLocked(directory);
610 if (!directoryLocked)
611 error = transaction.AddNode(directory);
612 } else
613 error = transaction.StartAndAddNode(directory);
614 if (error != B_OK)
615 RETURN_ERROR(error);
616
617 // look up the entry
618 uint64 blockIndex;
619 error = directory->LookupEntry(name, blockIndex);
620 if (error == B_OK) {
621 // the entry already exists
622 if ((openMode & O_EXCL) != 0)
623 return B_FILE_EXISTS;
624
625 // get the entry's node
626 error = volume->GetNode(blockIndex, childNode);
627 if (error != B_OK)
628 RETURN_ERROR(error);
629 childNodePutter.SetTo(childNode);
630
631 // We can (must even) unlock the directory now. The file won't go
632 // anywhere, since a transaction is already running.
633 if (!directoryLocked)
634 transaction.RemoveNode(directory);
635
636 error = open_file(volume, childNode, openMode, transaction,
637 commitTransaction, _cookie);
638 if (error != B_OK)
639 RETURN_ERROR(error);
640
641 childNodePutter.Detach();
642 _node = childNode;
643 _created = false;
644 return B_OK;
645 }
646
647 if (error != B_ENTRY_NOT_FOUND)
648 RETURN_ERROR(error);
649
650 // The entry doesn't exist yet. We have to create a new file.
651
652 // check the directory write permission
653 error = check_access(directory, W_OK);
654 if (error != B_OK)
655 return error;
656
657 // don't create an entry in an unlinked directory
658 if (directory->HardLinks() == 0)
659 RETURN_ERROR(B_ENTRY_NOT_FOUND);
660
661 // create a file
662 File* newFile;
663 error = volume->CreateFile(permissions, transaction, newFile);
664 if (error != B_OK)
665 return error;
666
667 // insert the new file
668 error = directory->InsertEntry(name, newFile->BlockIndex(), transaction);
669 if (error != B_OK)
670 return error;
671
672 // open the file
673 FileCookie* cookie;
674 error = open_file(volume, newFile, openMode & ~O_TRUNC, transaction,
675 false, cookie);
676 if (error != B_OK)
677 RETURN_ERROR(error);
678 ObjectDeleter<FileCookie> cookieDeleter(cookie);
679
680 // update stat data
681 newFile->SetHardLinks(1);
682 newFile->SetParentDirectory(directory->BlockIndex());
683
684 directory->Touched(NODE_MODIFIED);
685
686 // announce the new vnode (needed for creating the file cache), but don't
687 // publish it yet
688 error = volume->NewNode(newFile);
689 if (error != B_OK)
690 RETURN_ERROR(error);
691
692 // There's a vnode now -- the File object will be deleted when that is
693 // removed.
694 transaction.UpdateNodeFlags(newFile, TRANSACTION_REMOVE_NODE_ON_ERROR);
695
696 // create the file cache
697 error = newFile->InitForVFS();
698 if (error != B_OK)
699 return error;
700
701 // node is fully initialized -- publish the vnode
702 error = volume->PublishNode(newFile, 0);
703 if (error != B_OK) {
704 // publish_vnode() deletes the vnode on error, but it doesn't call the
705 // remove_vnode() hook. So we need to make sure the object is deleted.
706 transaction.UpdateNodeFlags(newFile, TRANSACTION_DELETE_NODE);
707 RETURN_ERROR(error);
708 }
709
710 // commit the transaction
711 if (commitTransaction) {
712 error = transaction.Commit(EntryCreatedNotification(directory, name,
713 newFile));
714 if (error != B_OK) {
715 volume->PutNode(newFile);
716 RETURN_ERROR(error);
717 }
718 }
719
720 _cookie = cookieDeleter.Detach();
721 _node = newFile;
722 _created = true;
723
724 return B_OK;
725 }
726
727
728 /*! Gets the node's attribute directory.
729 If a transaction is given and the attribute directory doesn't exist, a new
730 one is created and associate with the node.
731 On success the caller gets a reference to the attribute directory and is
732 responsible for putting it. If a transaction was given, the attribute
733 directory must be put after committing/aborting the transaction.
734 */
735 static status_t
get_attribute_directory(Node * node,Transaction * transaction,Directory * & _attributeDirectory)736 get_attribute_directory(Node* node, Transaction* transaction,
737 Directory*& _attributeDirectory)
738 {
739 uint64 blockIndex = node->AttributeDirectory();
740 Directory* attributeDirectory;
741
742 if (blockIndex != 0) {
743 // get the attribute directory node
744 Node* attrDirNode;
745 status_t error = node->GetVolume()->GetNode(blockIndex, attrDirNode);
746 if (error != B_OK)
747 RETURN_ERROR(error);
748
749 attributeDirectory = dynamic_cast<Directory*>(attrDirNode);
750 if (attributeDirectory == NULL) {
751 node->GetVolume()->PutNode(node);
752 ERROR("checksumfs: attribute directory (%" B_PRIu64 ") of node %"
753 B_PRIu64 " is not a directory!\n", blockIndex,
754 node->BlockIndex());
755 RETURN_ERROR(B_BAD_DATA);
756 }
757 } else {
758 // no (i.e. empty) attribute directory yet
759 if (transaction == NULL)
760 return B_ENTRY_NOT_FOUND;
761
762 // create a new one
763 status_t error = node->GetVolume()->CreateDirectory(
764 S_IRWXU | S_IRWXG | S_IRWXO, *transaction, attributeDirectory);
765 if (error != B_OK)
766 RETURN_ERROR(error);
767
768 attributeDirectory->SetMode(attributeDirectory->Mode() | S_ATTR_DIR);
769 attributeDirectory->SetParentDirectory(node->BlockIndex());
770 attributeDirectory->SetHardLinks(1);
771 node->SetAttributeDirectory(attributeDirectory->BlockIndex());
772
773 // publish it
774 error = node->GetVolume()->PublishNode(attributeDirectory, 0);
775 if (error != B_OK)
776 RETURN_ERROR(error);
777
778 // We have published the attribute directory, so don't delete it when
779 // committing or aborting the transaction. Instead, on error remove it.
780 transaction->UpdateNodeFlags(attributeDirectory,
781 TRANSACTION_REMOVE_NODE_ON_ERROR);
782 }
783
784 _attributeDirectory = attributeDirectory;
785 return B_OK;
786 }
787
788
789 // #pragma mark - FS operations
790
791
792 static float
checksumfs_identify_partition(int fd,partition_data * partition,void ** _cookie)793 checksumfs_identify_partition(int fd, partition_data* partition,
794 void** _cookie)
795 {
796 if ((uint64)partition->size < kCheckSumFSMinSize)
797 return -1;
798
799 SuperBlock* superBlock = new(std::nothrow) SuperBlock;
800 if (superBlock == NULL)
801 return -1;
802 ObjectDeleter<SuperBlock> superBlockDeleter(superBlock);
803
804 if (pread(fd, superBlock, sizeof(*superBlock), kCheckSumFSSuperBlockOffset)
805 != sizeof(*superBlock)) {
806 return -1;
807 }
808
809 if (!superBlock->Check((uint64)partition->size / B_PAGE_SIZE))
810 return -1;
811
812 *_cookie = superBlockDeleter.Detach();
813 return 0.8f;
814 }
815
816
817 static status_t
checksumfs_scan_partition(int fd,partition_data * partition,void * cookie)818 checksumfs_scan_partition(int fd, partition_data* partition, void* cookie)
819 {
820 SuperBlock* superBlock = (SuperBlock*)cookie;
821
822 partition->status = B_PARTITION_VALID;
823 partition->flags |= B_PARTITION_FILE_SYSTEM;
824 partition->content_size = superBlock->TotalBlocks() * B_PAGE_SIZE;
825 partition->block_size = B_PAGE_SIZE;
826 partition->content_name = strdup(superBlock->Name());
827 if (partition->content_name == NULL)
828 return B_NO_MEMORY;
829
830 return B_OK;
831 }
832
833
834 static void
checksumfs_free_identify_partition_cookie(partition_data * partition,void * cookie)835 checksumfs_free_identify_partition_cookie(partition_data* partition,
836 void* cookie)
837 {
838 SuperBlock* superBlock = (SuperBlock*)cookie;
839 delete superBlock;
840 }
841
842
843 static status_t
checksumfs_mount(fs_volume * fsVolume,const char * device,uint32 flags,const char * args,ino_t * _rootVnodeID)844 checksumfs_mount(fs_volume* fsVolume, const char* device, uint32 flags,
845 const char* args, ino_t* _rootVnodeID)
846 {
847 Volume* volume = new(std::nothrow) Volume(flags);
848 if (volume == NULL)
849 RETURN_ERROR(B_NO_MEMORY);
850 ObjectDeleter<Volume> volumeDeleter(volume);
851
852 status_t error = volume->Init(device);
853 if (error != B_OK)
854 RETURN_ERROR(error);
855
856 error = volume->Mount(fsVolume);
857 if (error != B_OK)
858 RETURN_ERROR(error);
859
860 fsVolume->private_volume = volumeDeleter.Detach();
861 fsVolume->ops = &gCheckSumFSVolumeOps;
862 *_rootVnodeID = volume->RootDirectory()->BlockIndex();
863
864 return B_OK;
865 }
866
867
868 static status_t
checksumfs_set_content_name(int fd,partition_id partition,const char * name,disk_job_id job)869 checksumfs_set_content_name(int fd, partition_id partition, const char* name,
870 disk_job_id job)
871 {
872 // TODO: Implement!
873 return B_UNSUPPORTED;
874 }
875
876
877 static status_t
checksumfs_initialize(int fd,partition_id partition,const char * name,const char * parameters,off_t partitionSize,disk_job_id job)878 checksumfs_initialize(int fd, partition_id partition, const char* name,
879 const char* parameters, off_t partitionSize, disk_job_id job)
880 {
881 if (name == NULL || strlen(name) >= kCheckSumFSNameLength)
882 return B_BAD_VALUE;
883
884 // TODO: Forcing a non-empty name here. Superfluous when the userland disk
885 // system add-on has a parameter editor for it.
886 if (*name == '\0')
887 name = "Unnamed";
888
889 update_disk_device_job_progress(job, 0);
890
891 Volume volume(0);
892
893 status_t error = volume.Init(fd, partitionSize / B_PAGE_SIZE);
894 if (error != B_OK)
895 return error;
896
897 error = volume.Initialize(name);
898 if (error != B_OK)
899 return error;
900
901 // rescan partition
902 error = scan_partition(partition);
903 if (error != B_OK)
904 return error;
905
906 update_disk_device_job_progress(job, 1);
907
908 return B_OK;
909 }
910
911
912 // #pragma mark - volume operations
913
914
915 static status_t
checksumfs_unmount(fs_volume * fsVolume)916 checksumfs_unmount(fs_volume* fsVolume)
917 {
918 Volume* volume = (Volume*)fsVolume->private_volume;
919 volume->Unmount();
920 return B_OK;
921 }
922
923
924 static status_t
checksumfs_read_fs_info(fs_volume * fsVolume,struct fs_info * info)925 checksumfs_read_fs_info(fs_volume* fsVolume, struct fs_info* info)
926 {
927 Volume* volume = (Volume*)fsVolume->private_volume;
928 volume->GetInfo(*info);
929 return B_OK;
930 }
931
932
933 static status_t
checksumfs_write_fs_info(fs_volume * fsVolume,const struct fs_info * info,uint32 mask)934 checksumfs_write_fs_info(fs_volume* fsVolume, const struct fs_info* info,
935 uint32 mask)
936 {
937 Volume* volume = (Volume*)fsVolume->private_volume;
938
939 if ((mask & FS_WRITE_FSINFO_NAME) != 0) {
940 status_t error = volume->SetName(info->volume_name);
941 if (error != B_OK)
942 return error;
943 }
944
945 return B_OK;
946 }
947
948
949 static status_t
checksumfs_sync(fs_volume * fsVolume)950 checksumfs_sync(fs_volume* fsVolume)
951 {
952 Volume* volume = (Volume*)fsVolume->private_volume;
953
954 return block_cache_sync(volume->BlockCache());
955 }
956
957
958 static status_t
checksumfs_get_vnode(fs_volume * fsVolume,ino_t id,fs_vnode * vnode,int * _type,uint32 * _flags,bool reenter)959 checksumfs_get_vnode(fs_volume* fsVolume, ino_t id, fs_vnode* vnode,
960 int* _type, uint32* _flags, bool reenter)
961 {
962 Volume* volume = (Volume*)fsVolume->private_volume;
963
964 Node* node;
965 status_t error = volume->ReadNode(id, node);
966 if (error != B_OK)
967 return error;
968
969 error = node->InitForVFS();
970 if (error != B_OK) {
971 delete node;
972 return error;
973 }
974
975 vnode->private_node = node;
976 vnode->ops = &gCheckSumFSVnodeOps;
977 *_type = node->Mode();
978 *_flags = 0;
979
980 return B_OK;
981 }
982
983
984 // #pragma mark - vnode operations
985
986
987 static status_t
checksumfs_lookup(fs_volume * fsVolume,fs_vnode * fsDir,const char * name,ino_t * _id)988 checksumfs_lookup(fs_volume* fsVolume, fs_vnode* fsDir, const char* name,
989 ino_t* _id)
990 {
991 Volume* volume = (Volume*)fsVolume->private_volume;
992 Node* node = (Node*)fsDir->private_node;
993
994 Directory* directory = dynamic_cast<Directory*>(node);
995 if (directory == NULL)
996 return B_NOT_A_DIRECTORY;
997
998 status_t error = check_access(directory, X_OK);
999 if (error != B_OK)
1000 return error;
1001
1002 NodeReadLocker nodeLocker(node);
1003
1004 uint64 blockIndex;
1005
1006 if (strcmp(name, ".") == 0) {
1007 blockIndex = directory->BlockIndex();
1008 } else if (strcmp(name, "..") == 0) {
1009 blockIndex = directory->ParentDirectory();
1010 } else {
1011 status_t error = directory->LookupEntry(name, blockIndex);
1012 if (error != B_OK)
1013 return error;
1014 }
1015
1016 // get the node
1017 Node* childNode;
1018 error = volume->GetNode(blockIndex, childNode);
1019 if (error != B_OK)
1020 return error;
1021
1022 *_id = blockIndex;
1023 return B_OK;
1024 }
1025
1026
1027 static status_t
checksumfs_put_vnode(fs_volume * fsVolume,fs_vnode * vnode,bool reenter)1028 checksumfs_put_vnode(fs_volume* fsVolume, fs_vnode* vnode, bool reenter)
1029 {
1030 Node* node = (Node*)vnode->private_node;
1031 delete node;
1032 return B_OK;
1033 }
1034
1035
1036 static status_t
checksumfs_remove_vnode(fs_volume * fsVolume,fs_vnode * vnode,bool reenter)1037 checksumfs_remove_vnode(fs_volume* fsVolume, fs_vnode* vnode, bool reenter)
1038 {
1039 Volume* volume = (Volume*)fsVolume->private_volume;
1040 Node* node = (Node*)vnode->private_node;
1041 return volume->DeleteNode(node);
1042 }
1043
1044
1045
1046 // #pragma mark - asynchronous I/O
1047
1048
1049 static status_t
iterative_io_get_vecs_hook(void * cookie,io_request * request,off_t offset,size_t size,file_io_vec * vecs,size_t * _count)1050 iterative_io_get_vecs_hook(void* cookie, io_request* request, off_t offset,
1051 size_t size, file_io_vec* vecs, size_t* _count)
1052 {
1053 File* file = (File*)cookie;
1054
1055 RETURN_ERROR(file_map_translate(file->FileMap(), offset, size, vecs, _count,
1056 B_PAGE_SIZE));
1057 }
1058
1059
1060 static status_t
iterative_io_finished_hook(void * cookie,io_request * request,status_t status,bool partialTransfer,size_t bytesTransferred)1061 iterative_io_finished_hook(void* cookie, io_request* request, status_t status,
1062 bool partialTransfer, size_t bytesTransferred)
1063 {
1064 File* file = (File*)cookie;
1065 file->ReadUnlock();
1066 return B_OK;
1067 }
1068
1069
1070 static status_t
checksumfs_io(fs_volume * fsVolume,fs_vnode * vnode,void * cookie,io_request * request)1071 checksumfs_io(fs_volume* fsVolume, fs_vnode* vnode, void* cookie,
1072 io_request* request)
1073 {
1074 Volume* volume = (Volume*)fsVolume->private_volume;
1075 File* file = dynamic_cast<File*>((Node*)vnode->private_node);
1076 if (file == NULL) {
1077 notify_io_request(request, B_READ_ONLY_DEVICE);
1078 RETURN_ERROR(B_BAD_VALUE);
1079 }
1080
1081 if (io_request_is_write(request) && volume->IsReadOnly()) {
1082 notify_io_request(request, B_READ_ONLY_DEVICE);
1083 RETURN_ERROR(B_READ_ONLY_DEVICE);
1084 }
1085
1086 // Read-lock the file -- we'll unlock it in the finished hook.
1087 if (io_request_is_vip(request)) {
1088 // We cannot wait for the node lock indefinitely. So try read-locking
1089 // with a timeout (0.1 s).
1090 if (!file->ReadLockWithTimeout(B_RELATIVE_TIMEOUT, 100000)) {
1091 notify_io_request(request, B_BUSY);
1092 RETURN_ERROR(B_BUSY);
1093 }
1094 } else
1095 file->ReadLock();
1096
1097 RETURN_ERROR(do_iterative_fd_io(volume->FD(), request,
1098 iterative_io_get_vecs_hook, iterative_io_finished_hook, file));
1099 }
1100
1101
1102 // #pragma mark - cache file access
1103
1104
1105 static status_t
checksumfs_get_file_map(fs_volume * fsVolume,fs_vnode * vnode,off_t offset,size_t size,struct file_io_vec * vecs,size_t * _count)1106 checksumfs_get_file_map(fs_volume* fsVolume, fs_vnode* vnode, off_t offset,
1107 size_t size, struct file_io_vec* vecs, size_t* _count)
1108 {
1109 if (offset < 0)
1110 RETURN_ERROR(B_BAD_VALUE);
1111
1112 File* file = dynamic_cast<File*>((Node*)vnode->private_node);
1113 if (file == NULL)
1114 RETURN_ERROR(B_BAD_VALUE);
1115
1116 RETURN_ERROR(file->GetFileVecs(offset, size, vecs, *_count, *_count));
1117 }
1118
1119
1120 // #pragma mark - common operations
1121
1122
1123 static status_t
checksumfs_set_flags(fs_volume * fsVolume,fs_vnode * vnode,void * _cookie,int flags)1124 checksumfs_set_flags(fs_volume* fsVolume, fs_vnode* vnode, void* _cookie,
1125 int flags)
1126 {
1127 FileCookie* cookie = (FileCookie*)_cookie;
1128
1129 cookie->openMode = (cookie->openMode & ~O_APPEND) | (flags & O_APPEND);
1130
1131 // TODO: Also support O_NOCACHE!
1132
1133 return B_OK;
1134 }
1135
1136
1137 static status_t
checksumfs_fsync(fs_volume * fsVolume,fs_vnode * vnode)1138 checksumfs_fsync(fs_volume* fsVolume, fs_vnode* vnode)
1139 {
1140 Node* node = (Node*)vnode->private_node;
1141
1142 NodeReadLocker nodeLocker(node);
1143
1144 return node->Sync();
1145 }
1146
1147
1148 static status_t
checksumfs_read_symlink(fs_volume * fsVolume,fs_vnode * vnode,char * buffer,size_t * _bufferSize)1149 checksumfs_read_symlink(fs_volume* fsVolume, fs_vnode* vnode, char* buffer,
1150 size_t* _bufferSize)
1151 {
1152 SymLink* symLink = dynamic_cast<SymLink*>((Node*)vnode->private_node);
1153 if (symLink == NULL)
1154 RETURN_ERROR(B_BAD_VALUE);
1155
1156 status_t error = check_access(symLink, R_OK);
1157 if (error != B_OK)
1158 return error;
1159
1160 return symLink->ReadSymLink(buffer, *_bufferSize, *_bufferSize);
1161 }
1162
1163
1164 static status_t
checksumfs_create_symlink(fs_volume * fsVolume,fs_vnode * parent,const char * name,const char * path,int mode)1165 checksumfs_create_symlink(fs_volume* fsVolume, fs_vnode* parent,
1166 const char* name, const char* path, int mode)
1167 {
1168 Volume* volume = (Volume*)fsVolume->private_volume;
1169 Directory* directory
1170 = dynamic_cast<Directory*>((Node*)parent->private_node);
1171 if (directory == NULL)
1172 return B_NOT_A_DIRECTORY;
1173
1174 if (volume->IsReadOnly())
1175 return B_READ_ONLY_DEVICE;
1176
1177 status_t error = check_access(directory, W_OK);
1178 if (error != B_OK)
1179 return error;
1180
1181 // start a transaction and add the directory (write locks it, too)
1182 Transaction transaction(volume);
1183 error = transaction.StartAndAddNode(directory);
1184 if (error != B_OK)
1185 return error;
1186
1187 // don't create an entry in an unlinked directory
1188 if (directory->HardLinks() == 0)
1189 RETURN_ERROR(B_ENTRY_NOT_FOUND);
1190
1191 // create a symlink node
1192 SymLink* newSymLink;
1193 error = volume->CreateSymLink(mode, transaction, newSymLink);
1194 if (error != B_OK)
1195 return error;
1196
1197 // write it
1198 error = newSymLink->WriteSymLink(path, strlen(path), transaction);
1199 if (error != B_OK)
1200 return error;
1201
1202 // insert the new symlink
1203 error = directory->InsertEntry(name, newSymLink->BlockIndex(), transaction);
1204 if (error != B_OK)
1205 return error;
1206
1207 // update stat data
1208 newSymLink->SetHardLinks(1);
1209
1210 directory->Touched(NODE_MODIFIED);
1211
1212 // commit the transaction
1213 return transaction.Commit(EntryCreatedNotification(directory, name,
1214 newSymLink));
1215 }
1216
1217
1218 static status_t
checksumfs_link(fs_volume * fsVolume,fs_vnode * dir,const char * name,fs_vnode * vnode)1219 checksumfs_link(fs_volume* fsVolume, fs_vnode* dir, const char* name,
1220 fs_vnode* vnode)
1221 {
1222 Volume* volume = (Volume*)fsVolume->private_volume;
1223 Node* node = (Node*)vnode->private_node;
1224 Directory* directory = dynamic_cast<Directory*>((Node*)dir->private_node);
1225 if (directory == NULL)
1226 return B_NOT_A_DIRECTORY;
1227
1228 if (volume->IsReadOnly())
1229 return B_READ_ONLY_DEVICE;
1230
1231 // don't allow hardlinking directories
1232 if (S_ISDIR(node->Mode()))
1233 RETURN_ERROR(B_NOT_ALLOWED);
1234
1235 // start a transaction and lock the nodes
1236 Transaction transaction(volume);
1237 status_t error = transaction.Start();
1238 if (error != B_OK)
1239 RETURN_ERROR(error);
1240
1241 error = transaction.AddNodes(directory, node);
1242 if (error != B_OK)
1243 RETURN_ERROR(error);
1244
1245 // check permissions
1246 error = check_access(directory, W_OK);
1247 if (error != B_OK)
1248 RETURN_ERROR(error);
1249
1250 // don't create an entry in an unlinked directory
1251 if (directory->HardLinks() == 0)
1252 RETURN_ERROR(B_ENTRY_NOT_FOUND);
1253
1254 // insert the new entry
1255 error = directory->InsertEntry(name, node->BlockIndex(), transaction);
1256 if (error != B_OK)
1257 RETURN_ERROR(error);
1258
1259 // update stat data
1260 node->SetHardLinks(node->HardLinks() + 1);
1261 directory->Touched(NODE_MODIFIED);
1262
1263 // commit the transaction
1264 return transaction.Commit(EntryCreatedNotification(directory, name, node));
1265 }
1266
1267
1268 static status_t
checksumfs_unlink(fs_volume * fsVolume,fs_vnode * dir,const char * name)1269 checksumfs_unlink(fs_volume* fsVolume, fs_vnode* dir, const char* name)
1270 {
1271 return remove_entry(fsVolume, dir, name, false);
1272 }
1273
1274
1275 static status_t
checksumfs_rename(fs_volume * fsVolume,fs_vnode * fromDir,const char * fromName,fs_vnode * toDir,const char * toName)1276 checksumfs_rename(fs_volume* fsVolume, fs_vnode* fromDir, const char* fromName,
1277 fs_vnode* toDir, const char* toName)
1278 {
1279 Volume* volume = (Volume*)fsVolume->private_volume;
1280 Directory* fromDirectory
1281 = dynamic_cast<Directory*>((Node*)fromDir->private_node);
1282 Directory* toDirectory
1283 = dynamic_cast<Directory*>((Node*)toDir->private_node);
1284 if (fromDirectory == NULL || toDirectory == NULL)
1285 return B_NOT_A_DIRECTORY;
1286
1287 if (volume->IsReadOnly())
1288 return B_READ_ONLY_DEVICE;
1289
1290 // We need to write-lock all three nodes (both directories and the moved
1291 // node). To make that atomic, we have to lock the source directory, look up
1292 // the entry, unlock the directory, get the node, re-lock all, and look up
1293 // the entry again to check for changes.
1294 Transaction transaction(volume);
1295 Node* node;
1296 NodePutter nodePutter;
1297
1298 while (true) {
1299 // look up the entry
1300 NodeReadLocker directoryLocker(fromDirectory);
1301
1302 uint64 blockIndex;
1303 status_t error = fromDirectory->LookupEntry(fromName, blockIndex);
1304 if (error != B_OK)
1305 RETURN_ERROR(error);
1306
1307 directoryLocker.Unlock();
1308
1309 // get the entry's node
1310 error = volume->GetNode(blockIndex, node);
1311 if (error != B_OK)
1312 RETURN_ERROR(error);
1313 nodePutter.SetTo(node);
1314
1315 // start the transaction
1316 error = transaction.Start();
1317 if (error != B_OK)
1318 RETURN_ERROR(error);
1319
1320 // write-lock the nodes
1321 error = fromDirectory != toDirectory
1322 ? transaction.AddNodes(fromDirectory, toDirectory, node)
1323 : transaction.AddNodes(fromDirectory, node);
1324 if (error != B_OK)
1325 RETURN_ERROR(error);
1326
1327 // check the situation again
1328 error = fromDirectory->LookupEntry(fromName, blockIndex);
1329 if (error != B_OK)
1330 RETURN_ERROR(error);
1331 if (blockIndex != node->BlockIndex()) {
1332 transaction.Abort();
1333 continue;
1334 }
1335
1336 break;
1337 }
1338
1339 // check permissions
1340 status_t error = check_access(fromDirectory, W_OK);
1341 if (error != B_OK)
1342 RETURN_ERROR(error);
1343
1344 error = check_access(toDirectory, W_OK);
1345 if (error != B_OK)
1346 RETURN_ERROR(error);
1347
1348 // don't create an entry in an unlinked directory
1349 if (toDirectory->HardLinks() == 0)
1350 RETURN_ERROR(B_ENTRY_NOT_FOUND);
1351
1352 // Check whether this operation would move a directory into one of its
1353 // descendents. We iterate upwards, checking whether any ancestor of the
1354 // target directory is the moved directory (if it is a directory that is).
1355 if (fromDirectory != toDirectory && S_ISDIR(node->Mode())) {
1356 NodePutter ancestorPutter;
1357 Node* ancestor = toDirectory;
1358
1359 while (ancestor != volume->RootDirectory()
1360 || ancestor == fromDirectory) {
1361 if (ancestor == node)
1362 RETURN_ERROR(B_BAD_VALUE);
1363
1364 error = volume->GetNode(ancestor->ParentDirectory(), ancestor);
1365 if (error != B_OK)
1366 RETURN_ERROR(error);
1367 ancestorPutter.SetTo(ancestor);
1368 }
1369 }
1370
1371 // Everything looks good -- insert a new entry in the target directory and
1372 // remove the old entry from the source directory.
1373 error = toDirectory->InsertEntry(toName, node->BlockIndex(), transaction);
1374 if (error != B_OK)
1375 RETURN_ERROR(error);
1376
1377 error = fromDirectory->RemoveEntry(fromName, transaction);
1378 if (error != B_OK)
1379 RETURN_ERROR(error);
1380
1381 // update stat data
1382 node->SetParentDirectory(toDirectory->BlockIndex());
1383 fromDirectory->Touched(NODE_MODIFIED);
1384 toDirectory->Touched(NODE_MODIFIED);
1385
1386 // commit the transaction
1387 return transaction.Commit(EntryMovedNotification(fromDirectory, fromName,
1388 toDirectory, toName, node));
1389 }
1390
1391
1392 static status_t
checksumfs_access(fs_volume * fsVolume,fs_vnode * vnode,int mode)1393 checksumfs_access(fs_volume* fsVolume, fs_vnode* vnode, int mode)
1394 {
1395 Node* node = (Node*)vnode->private_node;
1396
1397 NodeReadLocker nodeLocker(node);
1398
1399 return check_access(node, mode);
1400 }
1401
1402
1403 static status_t
checksumfs_read_stat(fs_volume * fsVolume,fs_vnode * vnode,struct stat * st)1404 checksumfs_read_stat(fs_volume* fsVolume, fs_vnode* vnode, struct stat* st)
1405 {
1406 Node* node = (Node*)vnode->private_node;
1407
1408 NodeReadLocker nodeLocker(node);
1409
1410 st->st_mode = node->Mode();
1411 st->st_nlink = node->HardLinks();
1412 st->st_uid = node->UID();
1413 st->st_gid = node->GID();
1414 st->st_size = node->Size();
1415 st->st_blksize = B_PAGE_SIZE * 16; // random number
1416 set_timespec(st->st_mtim, node->ModificationTime());
1417 set_timespec(st->st_ctim, node->ChangeTime());
1418 set_timespec(st->st_crtim, node->CreationTime());
1419 set_timespec(st->st_atim, node->AccessedTime());
1420 st->st_type = 0; /* attribute/index type */
1421 st->st_blocks = 1 + (st->st_size + B_PAGE_SIZE - 1) / B_PAGE_SIZE;
1422 // TODO: That does neither count management structures for the content
1423 // (for files) nor attributes.
1424
1425 return B_OK;
1426 }
1427
1428
1429 static status_t
checksumfs_write_stat(fs_volume * fsVolume,fs_vnode * vnode,const struct stat * st,uint32 statMask)1430 checksumfs_write_stat(fs_volume* fsVolume, fs_vnode* vnode,
1431 const struct stat* st, uint32 statMask)
1432 {
1433 Volume* volume = (Volume*)fsVolume->private_volume;
1434 Node* node = (Node*)vnode->private_node;
1435
1436 if (volume->IsReadOnly())
1437 return B_READ_ONLY_DEVICE;
1438
1439 // start a transaction and add the node to it (write locks the node, too)
1440 Transaction transaction(volume);
1441 status_t error = transaction.StartAndAddNode(node);
1442 if (error != B_OK)
1443 return error;
1444
1445 uid_t uid = geteuid();
1446 bool isOwnerOrRoot = uid == 0 || uid == node->UID();
1447 bool hasWriteAccess = check_access(node, W_OK) == B_OK;
1448
1449 bool updateModified = false;
1450 bool updateChanged = false;
1451
1452 if ((statMask & B_STAT_SIZE) != 0 && (uint64)st->st_size != node->Size()) {
1453 if (!hasWriteAccess)
1454 RETURN_ERROR(B_NOT_ALLOWED);
1455
1456 error = node->Resize(st->st_size, true, transaction);
1457 if (error != B_OK)
1458 RETURN_ERROR(error);
1459
1460 updateModified = updateChanged = true;
1461 }
1462
1463 if ((statMask & B_STAT_UID) != 0 && st->st_uid != node->UID()) {
1464 // only root can do that
1465 if (uid != 0)
1466 RETURN_ERROR(B_NOT_ALLOWED);
1467
1468 node->SetUID(st->st_uid);
1469 updateChanged = true;
1470 }
1471
1472 if ((statMask & B_STAT_GID) != 0 && st->st_gid != node->GID()) {
1473 // only the user or root can do that
1474 if (!isOwnerOrRoot)
1475 RETURN_ERROR(B_NOT_ALLOWED);
1476
1477 node->SetGID(st->st_gid);
1478 updateChanged = true;
1479 }
1480
1481 if ((statMask & B_STAT_MODE) != 0) {
1482 // only the user or root can do that
1483 if (!isOwnerOrRoot)
1484 RETURN_ERROR(B_NOT_ALLOWED);
1485
1486 node->SetMode((node->Mode() & ~(mode_t)S_IUMSK)
1487 | (st->st_mode & S_IUMSK));
1488 updateChanged = true;
1489 }
1490
1491 if ((statMask & B_STAT_CREATION_TIME) != 0) {
1492 // the user or root can do that or any user with write access
1493 if (!isOwnerOrRoot && !hasWriteAccess)
1494 RETURN_ERROR(B_NOT_ALLOWED);
1495
1496 node->SetCreationTime(timespec_to_nsecs(st->st_crtim));
1497 updateChanged = true;
1498 }
1499
1500 if ((statMask & B_STAT_MODIFICATION_TIME) != 0) {
1501 // the user or root can do that or any user with write access
1502 if (!isOwnerOrRoot && !hasWriteAccess)
1503 RETURN_ERROR(B_NOT_ALLOWED);
1504
1505 node->SetModificationTime(timespec_to_nsecs(st->st_mtim));
1506 updateModified = false;
1507 updateChanged = true;
1508 }
1509
1510 if ((statMask & B_STAT_CHANGE_TIME) != 0) {
1511 // the user or root can do that or any user with write access
1512 if (!isOwnerOrRoot && !hasWriteAccess)
1513 RETURN_ERROR(B_NOT_ALLOWED);
1514
1515 node->SetModificationTime(timespec_to_nsecs(st->st_mtim));
1516 updateModified = false;
1517 updateChanged = false;
1518 }
1519
1520 // update access/change/modification time
1521 if (updateModified)
1522 node->Touched(NODE_MODIFIED);
1523 else if (updateChanged)
1524 node->Touched(NODE_STAT_CHANGED);
1525 else
1526 node->Touched(NODE_ACCESSED);
1527
1528 // commit the transaction
1529 return transaction.Commit(StatChangedNotification(node, statMask));
1530 }
1531
1532
1533 // #pragma mark - file operations
1534
1535
1536 static status_t
checksumfs_create(fs_volume * fsVolume,fs_vnode * parent,const char * name,int openMode,int permissions,void ** _cookie,ino_t * _newVnodeID)1537 checksumfs_create(fs_volume* fsVolume, fs_vnode* parent, const char* name,
1538 int openMode, int permissions, void** _cookie, ino_t* _newVnodeID)
1539 {
1540 Volume* volume = (Volume*)fsVolume->private_volume;
1541 Directory* directory
1542 = dynamic_cast<Directory*>((Node*)parent->private_node);
1543 if (directory == NULL)
1544 return B_NOT_A_DIRECTORY;
1545
1546 if (volume->IsReadOnly())
1547 return B_READ_ONLY_DEVICE;
1548
1549 Transaction transaction(volume);
1550 FileCookie* cookie;
1551 Node* node;
1552 bool created;
1553 status_t error = create_file(volume, directory, name, openMode, permissions,
1554 transaction, true, cookie, node, created);
1555 if (error != B_OK)
1556 RETURN_ERROR(error);
1557
1558 *_cookie = cookie;
1559 *_newVnodeID = node->BlockIndex();
1560
1561 return B_OK;
1562 }
1563
1564
1565 static status_t
checksumfs_open(fs_volume * fsVolume,fs_vnode * vnode,int openMode,void ** _cookie)1566 checksumfs_open(fs_volume* fsVolume, fs_vnode* vnode, int openMode,
1567 void** _cookie)
1568 {
1569 Volume* volume = (Volume*)fsVolume->private_volume;
1570 Node* node = (Node*)vnode->private_node;
1571
1572 // don't allow opening an attribute this way
1573 if ((node->Mode() & S_ATTR) != 0)
1574 RETURN_ERROR(B_BAD_VALUE);
1575
1576 Transaction transaction(volume);
1577 FileCookie* cookie;
1578 status_t error = open_file(volume, node, openMode, transaction, true,
1579 cookie);
1580 if (error != B_OK)
1581 RETURN_ERROR(error);
1582
1583 *_cookie = cookie;
1584 return B_OK;
1585 }
1586
1587
1588 static status_t
checksumfs_close(fs_volume * fsVolume,fs_vnode * vnode,void * cookie)1589 checksumfs_close(fs_volume* fsVolume, fs_vnode* vnode, void* cookie)
1590 {
1591 return B_OK;
1592 }
1593
1594
1595 static status_t
checksumfs_free_cookie(fs_volume * fsVolume,fs_vnode * vnode,void * _cookie)1596 checksumfs_free_cookie(fs_volume* fsVolume, fs_vnode* vnode, void* _cookie)
1597 {
1598 FileCookie* cookie = (FileCookie*)_cookie;
1599 Node* node = (Node*)vnode->private_node;
1600
1601 cookie->UpdateModifiedIfNecessary(node, true);
1602
1603 delete cookie;
1604 return B_OK;
1605 }
1606
1607
1608 static status_t
checksumfs_read(fs_volume * fsVolume,fs_vnode * vnode,void * _cookie,off_t pos,void * buffer,size_t * _length)1609 checksumfs_read(fs_volume* fsVolume, fs_vnode* vnode, void* _cookie, off_t pos,
1610 void* buffer, size_t* _length)
1611 {
1612 FileCookie* cookie = (FileCookie*)_cookie;
1613 Node* node = (Node*)vnode->private_node;
1614
1615 switch (cookie->openMode & O_RWMASK) {
1616 case O_RDONLY:
1617 case O_RDWR:
1618 break;
1619 case O_WRONLY:
1620 default:
1621 RETURN_ERROR(EBADF);
1622 }
1623
1624 return node->Read(pos, buffer, *_length, *_length);
1625 }
1626
1627
1628 static status_t
checksumfs_write(fs_volume * fsVolume,fs_vnode * vnode,void * _cookie,off_t pos,const void * buffer,size_t * _length)1629 checksumfs_write(fs_volume* fsVolume, fs_vnode* vnode, void* _cookie, off_t pos,
1630 const void* buffer, size_t* _length)
1631 {
1632 FileCookie* cookie = (FileCookie*)_cookie;
1633 Node* node = (Node*)vnode->private_node;
1634
1635 switch (cookie->openMode & O_RWMASK) {
1636 case O_WRONLY:
1637 case O_RDWR:
1638 break;
1639 case O_RDONLY:
1640 default:
1641 RETURN_ERROR(EBADF);
1642 }
1643
1644 if (pos < 0)
1645 RETURN_ERROR(B_BAD_VALUE);
1646
1647 if ((cookie->openMode & O_APPEND) != 0) {
1648 pos = -1;
1649 // special value handled by Write()
1650 }
1651
1652 bool sizeChanged;
1653 status_t error = node->Write(pos, buffer, *_length, *_length, sizeChanged);
1654 if (error != B_OK)
1655 RETURN_ERROR(error);
1656
1657 // update the modification time and send out a notification from time to
1658 // time
1659 cookie->FileModified(node, sizeChanged);
1660
1661 return B_OK;
1662 }
1663
1664
1665 // #pragma mark - directory operations
1666
1667
1668 status_t
checksumfs_create_dir(fs_volume * fsVolume,fs_vnode * parent,const char * name,int perms)1669 checksumfs_create_dir(fs_volume* fsVolume, fs_vnode* parent, const char* name,
1670 int perms)
1671 {
1672 Volume* volume = (Volume*)fsVolume->private_volume;
1673 Directory* directory
1674 = dynamic_cast<Directory*>((Node*)parent->private_node);
1675 if (directory == NULL)
1676 return B_NOT_A_DIRECTORY;
1677
1678 if (volume->IsReadOnly())
1679 return B_READ_ONLY_DEVICE;
1680
1681 status_t error = check_access(directory, W_OK);
1682 if (error != B_OK)
1683 return error;
1684
1685 // start a transaction and attach the directory (write locks it, too)
1686 Transaction transaction(volume);
1687 error = transaction.StartAndAddNode(directory);
1688 if (error != B_OK)
1689 return error;
1690
1691 // don't create an entry in an unlinked directory
1692 if (directory->HardLinks() == 0)
1693 RETURN_ERROR(B_ENTRY_NOT_FOUND);
1694
1695 // create a directory node
1696 Directory* newDirectory;
1697 error = volume->CreateDirectory(perms, transaction, newDirectory);
1698 if (error != B_OK)
1699 return error;
1700
1701 // insert the new directory
1702 error = directory->InsertEntry(name, newDirectory->BlockIndex(),
1703 transaction);
1704 if (error != B_OK)
1705 return error;
1706
1707 // update stat data
1708 newDirectory->SetHardLinks(1);
1709 newDirectory->SetParentDirectory(directory->BlockIndex());
1710
1711 directory->Touched(NODE_MODIFIED);
1712
1713 // commit the transaction
1714 return transaction.Commit(EntryCreatedNotification(directory, name,
1715 newDirectory));
1716 }
1717
1718
1719 status_t
checksumfs_remove_dir(fs_volume * volume,fs_vnode * parent,const char * name)1720 checksumfs_remove_dir(fs_volume* volume, fs_vnode* parent, const char* name)
1721 {
1722 return remove_entry(volume, parent, name, true);
1723 }
1724
1725
1726 static status_t
checksumfs_open_dir(fs_volume * fsVolume,fs_vnode * vnode,void ** _cookie)1727 checksumfs_open_dir(fs_volume* fsVolume, fs_vnode* vnode, void** _cookie)
1728 {
1729 Directory* directory = dynamic_cast<Directory*>((Node*)vnode->private_node);
1730 if (directory == NULL)
1731 return B_NOT_A_DIRECTORY;
1732
1733 NodeReadLocker nodeLocker(directory);
1734
1735 // don't allow opening an attribute directory this way
1736 if ((directory->Mode() & S_ATTR_DIR) != 0)
1737 RETURN_ERROR(B_BAD_VALUE);
1738
1739 status_t error = check_access(directory, R_OK);
1740 if (error != B_OK)
1741 return error;
1742
1743 DirCookie* cookie = new(std::nothrow) DirCookie(directory);
1744 if (cookie == NULL)
1745 return B_NO_MEMORY;
1746
1747 *_cookie = cookie;
1748 return B_OK;
1749 }
1750
1751
1752 static status_t
checksumfs_close_dir(fs_volume * fsVolume,fs_vnode * vnode,void * cookie)1753 checksumfs_close_dir(fs_volume* fsVolume, fs_vnode* vnode, void* cookie)
1754 {
1755 return B_OK;
1756 }
1757
1758
1759 static status_t
checksumfs_free_dir_cookie(fs_volume * fsVolume,fs_vnode * vnode,void * _cookie)1760 checksumfs_free_dir_cookie(fs_volume* fsVolume, fs_vnode* vnode, void* _cookie)
1761 {
1762 DirCookie* cookie = (DirCookie*)_cookie;
1763 delete cookie;
1764 return B_OK;
1765 }
1766
1767
1768 static status_t
checksumfs_read_dir(fs_volume * fsVolume,fs_vnode * vnode,void * _cookie,struct dirent * buffer,size_t bufferSize,uint32 * _num)1769 checksumfs_read_dir(fs_volume* fsVolume, fs_vnode* vnode, void* _cookie,
1770 struct dirent* buffer, size_t bufferSize, uint32* _num)
1771 {
1772 if (*_num == 0)
1773 return B_OK;
1774
1775 DirCookie* cookie = (DirCookie*)_cookie;
1776
1777 NodeReadLocker nodeLocker(cookie->GetDirectory());
1778
1779 return cookie->ReadNextEntry(buffer, bufferSize, *_num);
1780 }
1781
1782
1783 static status_t
checksumfs_rewind_dir(fs_volume * fsVolume,fs_vnode * vnode,void * _cookie)1784 checksumfs_rewind_dir(fs_volume* fsVolume, fs_vnode* vnode, void* _cookie)
1785 {
1786 DirCookie* cookie = (DirCookie*)_cookie;
1787
1788 NodeReadLocker nodeLocker(cookie->GetDirectory());
1789
1790 cookie->Rewind();
1791 return B_OK;
1792 }
1793
1794
1795 // #pragma mark - attribute directory operations
1796
1797
1798 static status_t
checksumfs_open_attr_dir(fs_volume * volume,fs_vnode * vnode,void ** _cookie)1799 checksumfs_open_attr_dir(fs_volume* volume, fs_vnode* vnode, void** _cookie)
1800 {
1801 Node* node = (Node*)vnode->private_node;
1802
1803 NodeReadLocker nodeLocker(node);
1804
1805 status_t error = check_access(node, R_OK);
1806 if (error != B_OK)
1807 return error;
1808
1809 AttrDirCookie* cookie = new(std::nothrow) AttrDirCookie(node);
1810 if (cookie == NULL)
1811 return B_NO_MEMORY;
1812
1813 *_cookie = cookie;
1814 return B_OK;
1815 }
1816
1817
1818 static status_t
checksumfs_close_attr_dir(fs_volume * volume,fs_vnode * vnode,void * cookie)1819 checksumfs_close_attr_dir(fs_volume* volume, fs_vnode* vnode, void* cookie)
1820 {
1821 return B_OK;
1822 }
1823
1824
1825 static status_t
checksumfs_free_attr_dir_cookie(fs_volume * volume,fs_vnode * vnode,void * _cookie)1826 checksumfs_free_attr_dir_cookie(fs_volume* volume, fs_vnode* vnode,
1827 void* _cookie)
1828 {
1829 AttrDirCookie* cookie = (AttrDirCookie*)_cookie;
1830 delete cookie;
1831 return B_OK;
1832 }
1833
1834
1835 static status_t
checksumfs_read_attr_dir(fs_volume * volume,fs_vnode * vnode,void * _cookie,struct dirent * buffer,size_t bufferSize,uint32 * _num)1836 checksumfs_read_attr_dir(fs_volume* volume, fs_vnode* vnode, void* _cookie,
1837 struct dirent* buffer, size_t bufferSize, uint32* _num)
1838 {
1839 if (*_num == 0)
1840 return B_OK;
1841
1842 Node* node = (Node*)vnode->private_node;
1843 AttrDirCookie* cookie = (AttrDirCookie*)_cookie;
1844
1845 NodeReadLocker nodeLocker(node);
1846
1847 return cookie->ReadNextEntry(buffer, bufferSize, *_num);
1848 }
1849
1850
1851 static status_t
checksumfs_rewind_attr_dir(fs_volume * volume,fs_vnode * vnode,void * _cookie)1852 checksumfs_rewind_attr_dir(fs_volume* volume, fs_vnode* vnode, void* _cookie)
1853 {
1854 Node* node = (Node*)vnode->private_node;
1855 AttrDirCookie* cookie = (AttrDirCookie*)_cookie;
1856
1857 NodeReadLocker nodeLocker(node);
1858
1859 cookie->Rewind();
1860 return B_OK;
1861 }
1862
1863
1864 // #pragma mark - attribute operations
1865
1866
1867 static status_t
checksumfs_create_attr(fs_volume * fsVolume,fs_vnode * vnode,const char * name,uint32 type,int openMode,void ** _cookie)1868 checksumfs_create_attr(fs_volume* fsVolume, fs_vnode* vnode, const char* name,
1869 uint32 type, int openMode, void** _cookie)
1870 {
1871 Volume* volume = (Volume*)fsVolume->private_volume;
1872 Node* node = (Node*)vnode->private_node;
1873 if (node == NULL)
1874 return B_NOT_A_DIRECTORY;
1875
1876 if (volume->IsReadOnly())
1877 return B_READ_ONLY_DEVICE;
1878
1879 // create the attribute cookie
1880 AttributeCookie* cookie = new(std::nothrow) AttributeCookie(name);
1881 if (cookie == NULL || cookie->name == NULL) {
1882 delete cookie;
1883 return B_NO_MEMORY;
1884 }
1885 ObjectDeleter<AttributeCookie> cookieDeleter(cookie);
1886
1887 // Start a transaction and lock the node.
1888 // Note: Other than for ordinary nodes the locking order when attributes
1889 // are involved is: node -> attribute directory -> attribute.
1890 Transaction transaction(volume);
1891 status_t error = transaction.StartAndAddNode(node);
1892 if (error != B_OK)
1893 RETURN_ERROR(error);
1894
1895 // check permissions
1896 error = check_access(node, W_OK);
1897 if (error != B_OK)
1898 return error;
1899
1900 // get the attribute directory (create, if necessary)
1901 Directory* attributeDirectory;
1902 error = get_attribute_directory(node, &transaction, attributeDirectory);
1903 if (error != B_OK)
1904 RETURN_ERROR(error);
1905 NodePutter attributeDirectoryPutter(attributeDirectory);
1906
1907 // open/create the attribute
1908 bool created;
1909 error = create_file(volume, attributeDirectory, name, openMode,
1910 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, transaction,
1911 false, cookie->fileCookie, cookie->attribute, created);
1912 if (error != B_OK)
1913 RETURN_ERROR(error);
1914
1915 if (created) {
1916 cookie->attribute->SetMode(cookie->attribute->Mode() | S_ATTR);
1917 cookie->attribute->SetAttributeType(type);
1918 }
1919
1920 // commit the transaction
1921 if (transaction.IsActive()) {
1922 if (created || (openMode & O_TRUNC) != 0) {
1923 node->Touched(NODE_STAT_CHANGED);
1924
1925 AttributeChangedNotification attributeNotification(node, name,
1926 created ? B_ATTR_CREATED : B_ATTR_CHANGED);
1927 StatChangedNotification statNotification(node, B_STAT_CHANGE_TIME);
1928 error = transaction.Commit(&attributeNotification,
1929 &statNotification);
1930 } else
1931 error = transaction.Commit();
1932 }
1933
1934 *_cookie = cookieDeleter.Detach();
1935 return B_OK;
1936 }
1937
1938
1939 static status_t
checksumfs_open_attr(fs_volume * fsVolume,fs_vnode * vnode,const char * name,int openMode,void ** _cookie)1940 checksumfs_open_attr(fs_volume* fsVolume, fs_vnode* vnode, const char* name,
1941 int openMode, void** _cookie)
1942 {
1943 Volume* volume = (Volume*)fsVolume->private_volume;
1944 Node* node = (Node*)vnode->private_node;
1945
1946 // create the attribute cookie
1947 AttributeCookie* cookie = new(std::nothrow) AttributeCookie(name);
1948 if (cookie == NULL || cookie->name == NULL) {
1949 delete cookie;
1950 return B_NO_MEMORY;
1951 }
1952 ObjectDeleter<AttributeCookie> cookieDeleter(cookie);
1953
1954 // Get the node's attribute directory (don't create it, if it doesn't exist
1955 // yet). We only need to read-lock the node for that, but when O_TRUNC is
1956 // given, we already start the transaction and write-lock the node, so we
1957 // don't get a locking order inversion later. The locking order when
1958 // attributes are involved is: node -> attribute directory -> attribute.
1959 Transaction transaction(volume);
1960 NodeReadLocker readLocker;
1961 if ((openMode & O_TRUNC) != 0) {
1962 status_t error = transaction.StartAndAddNode(node);
1963 if (error != B_OK)
1964 RETURN_ERROR(error);
1965 } else
1966 readLocker.SetTo(node, false);
1967
1968 Directory* attributeDirectory;
1969 status_t error = get_attribute_directory(node, NULL, attributeDirectory);
1970 if (error != B_OK)
1971 RETURN_ERROR(error);
1972 NodePutter attributeDirectoryPutter(attributeDirectory);
1973
1974 // look up the attribute
1975 readLocker.SetTo(attributeDirectory, false);
1976 uint64 blockIndex;
1977 error = attributeDirectory->LookupEntry(name, blockIndex);
1978 if (error != B_OK)
1979 RETURN_ERROR(error);
1980
1981 error = volume->GetNode(blockIndex, cookie->attribute);
1982 // the vnode reference directly goes to the cookie in case of success
1983 if (error != B_OK)
1984 RETURN_ERROR(error);
1985
1986 // open the attribute
1987 error = open_file(volume, cookie->attribute, openMode, transaction, false,
1988 cookie->fileCookie);
1989 if (error != B_OK)
1990 RETURN_ERROR(error);
1991
1992 // commit the transaction
1993 if (transaction.IsActive()) {
1994 if ((openMode & O_TRUNC) != 0) {
1995 node->Touched(NODE_STAT_CHANGED);
1996
1997 AttributeChangedNotification attributeNotification(node, name,
1998 B_ATTR_CHANGED);
1999 StatChangedNotification statNotification(node, B_STAT_CHANGE_TIME);
2000 error = transaction.Commit(&attributeNotification,
2001 &statNotification);
2002 } else
2003 error = transaction.Commit();
2004 }
2005
2006 *_cookie = cookieDeleter.Detach();
2007 return B_OK;
2008 }
2009
2010
2011 static status_t
checksumfs_close_attr(fs_volume * fsVolume,fs_vnode * vnode,void * cookie)2012 checksumfs_close_attr(fs_volume* fsVolume, fs_vnode* vnode, void* cookie)
2013 {
2014 return B_OK;
2015 }
2016
2017
2018 static status_t
checksumfs_free_attr_cookie(fs_volume * fsVolume,fs_vnode * vnode,void * _cookie)2019 checksumfs_free_attr_cookie(fs_volume* fsVolume, fs_vnode* vnode, void* _cookie)
2020 {
2021 AttributeCookie* cookie = (AttributeCookie*)_cookie;
2022 delete cookie;
2023 return B_OK;
2024 }
2025
2026
2027 static status_t
checksumfs_read_attr(fs_volume * fsVolume,fs_vnode * vnode,void * _cookie,off_t pos,void * buffer,size_t * _length)2028 checksumfs_read_attr(fs_volume* fsVolume, fs_vnode* vnode, void* _cookie,
2029 off_t pos, void* buffer, size_t* _length)
2030 {
2031 AttributeCookie* cookie = (AttributeCookie*)_cookie;
2032
2033 switch (cookie->fileCookie->openMode & O_RWMASK) {
2034 case O_RDONLY:
2035 case O_RDWR:
2036 break;
2037 case O_WRONLY:
2038 default:
2039 RETURN_ERROR(EBADF);
2040 }
2041
2042 return cookie->attribute->Read(pos, buffer, *_length, *_length);
2043 }
2044
2045
2046 static status_t
checksumfs_write_attr(fs_volume * fsVolume,fs_vnode * vnode,void * _cookie,off_t pos,const void * buffer,size_t * _length)2047 checksumfs_write_attr(fs_volume* fsVolume, fs_vnode* vnode, void* _cookie,
2048 off_t pos, const void* buffer, size_t* _length)
2049 {
2050 Volume* volume = (Volume*)fsVolume->private_volume;
2051 Node* node = (Node*)vnode->private_node;
2052 AttributeCookie* cookie = (AttributeCookie*)_cookie;
2053
2054 switch (cookie->fileCookie->openMode & O_RWMASK) {
2055 case O_WRONLY:
2056 case O_RDWR:
2057 break;
2058 case O_RDONLY:
2059 default:
2060 RETURN_ERROR(EBADF);
2061 }
2062
2063 if (pos < 0)
2064 RETURN_ERROR(B_BAD_VALUE);
2065
2066 if ((cookie->fileCookie->openMode & O_APPEND) != 0) {
2067 pos = -1;
2068 // special value handled by Write()
2069 }
2070
2071 bool sizeChanged;
2072 status_t error = cookie->attribute->Write(pos, buffer, *_length, *_length,
2073 sizeChanged);
2074 if (error != B_OK)
2075 RETURN_ERROR(error);
2076
2077 // update the node changed time and send out a notifications (don't fail,
2078 // if any of this fails)
2079 Transaction transaction(volume);
2080 if (transaction.StartAndAddNode(node) != B_OK)
2081 return B_OK;
2082 if (transaction.AddNode(cookie->attribute) != B_OK)
2083 return B_OK;
2084
2085 cookie->attribute->Touched(NODE_MODIFIED);
2086
2087 // commit the transaction
2088 if (cookie->attribute->ParentDirectory() != 0) {
2089 node->Touched(NODE_STAT_CHANGED);
2090
2091 AttributeChangedNotification attributeNotification(node, cookie->name,
2092 B_ATTR_CHANGED);
2093 StatChangedNotification statNotification(node, B_STAT_CHANGE_TIME);
2094 transaction.Commit(&attributeNotification, &statNotification);
2095 } else {
2096 // attribute has been removed -- no notifications needed
2097 transaction.Commit();
2098 }
2099
2100 return B_OK;
2101 }
2102
2103
2104 static status_t
checksumfs_read_attr_stat(fs_volume * fsVolume,fs_vnode * vnode,void * _cookie,struct stat * st)2105 checksumfs_read_attr_stat(fs_volume* fsVolume, fs_vnode* vnode, void* _cookie,
2106 struct stat* st)
2107 {
2108 AttributeCookie* cookie = (AttributeCookie*)_cookie;
2109
2110 // not many fields needed ATM
2111 st->st_size = cookie->attribute->Size();
2112 st->st_type = cookie->attribute->AttributeType();
2113
2114 return B_OK;
2115 }
2116
2117
2118 static status_t
checksumfs_remove_attr(fs_volume * fsVolume,fs_vnode * vnode,const char * name)2119 checksumfs_remove_attr(fs_volume* fsVolume, fs_vnode* vnode, const char* name)
2120 {
2121 Volume* volume = (Volume*)fsVolume->private_volume;
2122 Node* node = (Node*)vnode->private_node;
2123
2124 if (volume->IsReadOnly())
2125 return B_READ_ONLY_DEVICE;
2126
2127 // start a transaction
2128 Transaction transaction(volume);
2129 status_t error = transaction.StartAndAddNode(node);
2130 if (error != B_OK)
2131 RETURN_ERROR(error);
2132
2133 // check permissions
2134 error = check_access(node, W_OK);
2135 if (error != B_OK)
2136 RETURN_ERROR(error);
2137
2138 // get the attribute directory
2139 Directory* attributeDirectory;
2140 error = get_attribute_directory(node, NULL, attributeDirectory);
2141 if (error != B_OK)
2142 RETURN_ERROR(error);
2143 NodePutter attributeDirectoryPutter(attributeDirectory);
2144
2145 error = transaction.AddNode(attributeDirectory);
2146 if (error != B_OK)
2147 RETURN_ERROR(error);
2148
2149 // look up the entry
2150 uint64 blockIndex;
2151 error = attributeDirectory->LookupEntry(name, blockIndex);
2152 if (error != B_OK)
2153 RETURN_ERROR(error);
2154
2155 // get the attribute node
2156 Node* attribute;
2157 error = volume->GetNode(blockIndex, attribute);
2158 if (error != B_OK)
2159 RETURN_ERROR(error);
2160 NodePutter attributePutter(attribute);
2161
2162 error = transaction.AddNode(attribute);
2163 if (error != B_OK) {
2164 volume->PutNode(attribute);
2165 RETURN_ERROR(error);
2166 }
2167
2168 // remove the entry
2169 bool attrDirEmpty;
2170 error = attributeDirectory->RemoveEntry(name, transaction, &attrDirEmpty);
2171 if (error != B_OK)
2172 RETURN_ERROR(error);
2173
2174 // remove the attribute node
2175 error = volume->RemoveNode(attribute);
2176 if (error != B_OK)
2177 return error;
2178 transaction.UpdateNodeFlags(attribute, TRANSACTION_UNREMOVE_NODE_ON_ERROR);
2179
2180 // if the attribute directory is empty now, remove it too
2181 if (attrDirEmpty) {
2182 error = volume->RemoveNode(attributeDirectory);
2183 if (error != B_OK)
2184 return error;
2185 transaction.UpdateNodeFlags(attributeDirectory,
2186 TRANSACTION_UNREMOVE_NODE_ON_ERROR);
2187
2188 node->SetAttributeDirectory(0);
2189 }
2190
2191 // update stat data
2192 attribute->SetHardLinks(0);
2193
2194 node->Touched(NODE_STAT_CHANGED);
2195
2196 // commit the transaction
2197 AttributeChangedNotification attributeNotification(node, name,
2198 B_ATTR_REMOVED);
2199 StatChangedNotification statNotification(node, B_STAT_CHANGE_TIME);
2200 return transaction.Commit(&attributeNotification, &statNotification);
2201 }
2202
2203
2204 // #pragma mark - module
2205
2206
2207 static status_t
checksumfs_std_ops(int32 operation,...)2208 checksumfs_std_ops(int32 operation, ...)
2209 {
2210 switch (operation) {
2211 case B_MODULE_INIT:
2212 init_debugging();
2213 PRINT("checksumfs_std_ops(): B_MODULE_INIT\n");
2214 return B_OK;
2215
2216 case B_MODULE_UNINIT:
2217 PRINT("checksumfs_std_ops(): B_MODULE_UNINIT\n");
2218 exit_debugging();
2219 return B_OK;
2220
2221 default:
2222 return B_BAD_VALUE;
2223 }
2224 }
2225
2226
2227 static file_system_module_info sFSModule = {
2228 {
2229 kCheckSumFSModuleName,
2230 0,
2231 checksumfs_std_ops
2232 },
2233 kCheckSumFSShortName,
2234 CHECK_SUM_FS_PRETTY_NAME,
2235 // DDM flags
2236 B_DISK_SYSTEM_SUPPORTS_INITIALIZING
2237 | B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME
2238 | B_DISK_SYSTEM_SUPPORTS_WRITING,
2239
2240 /* scanning (the device is write locked) */
2241 checksumfs_identify_partition,
2242 checksumfs_scan_partition,
2243 checksumfs_free_identify_partition_cookie,
2244 NULL, // free_partition_content_cookie
2245
2246 /* general operations */
2247 checksumfs_mount,
2248
2249 /* capability querying (the device is read locked) */
2250 NULL, // get_supported_operations
2251
2252 NULL, // validate_resize
2253 NULL, // validate_move
2254 NULL, // validate_set_content_name
2255 NULL, // validate_set_content_parameters
2256 NULL, // validate_initialize
2257
2258 /* shadow partition modification (device is write locked) */
2259 NULL, // shadow_changed
2260
2261 /* writing (the device is NOT locked) */
2262 NULL, // defragment
2263 NULL, // repair
2264 NULL, // resize
2265 NULL, // move
2266 checksumfs_set_content_name,
2267 NULL, // set_content_parameters
2268 checksumfs_initialize
2269 };
2270
2271
2272 const module_info* modules[] = {
2273 (module_info*)&sFSModule,
2274 NULL
2275 };
2276
2277
2278 fs_volume_ops gCheckSumFSVolumeOps = {
2279 checksumfs_unmount,
2280
2281 checksumfs_read_fs_info,
2282 checksumfs_write_fs_info,
2283 checksumfs_sync,
2284
2285 checksumfs_get_vnode,
2286
2287 /* index directory & index operations */
2288 NULL, // open_index_dir
2289 NULL, // close_index_dir
2290 NULL, // free_index_dir_cookie
2291 NULL, // read_index_dir
2292 NULL, // rewind_index_dir
2293
2294 NULL, // create_index
2295 NULL, // remove_index
2296 NULL, // read_index_stat
2297
2298 /* query operations */
2299 NULL, // open_query
2300 NULL, // close_query
2301 NULL, // free_query_cookie
2302 NULL, // read_query
2303 NULL, // rewind_query
2304
2305 /* support for FS layers */
2306 NULL, // all_layers_mounted
2307 NULL, // create_sub_vnode
2308 NULL, // delete_sub_vnode
2309 };
2310
2311
2312 fs_vnode_ops gCheckSumFSVnodeOps = {
2313 /* vnode operations */
2314 checksumfs_lookup,
2315 NULL, // get_vnode_name
2316
2317 checksumfs_put_vnode,
2318 checksumfs_remove_vnode,
2319
2320 /* VM file access */
2321 NULL, // can_page
2322 NULL, // read_pages
2323 NULL, // write_pages
2324
2325 /* asynchronous I/O */
2326 checksumfs_io,
2327 NULL, // cancel_io
2328
2329 /* cache file access */
2330 checksumfs_get_file_map,
2331
2332 /* common operations */
2333 NULL, // ioctl
2334 checksumfs_set_flags,
2335 NULL, // select
2336 NULL, // deselect
2337 checksumfs_fsync,
2338
2339 checksumfs_read_symlink,
2340 checksumfs_create_symlink,
2341
2342 checksumfs_link,
2343 checksumfs_unlink,
2344 checksumfs_rename,
2345
2346 checksumfs_access,
2347 checksumfs_read_stat,
2348 checksumfs_write_stat,
2349 NULL, // preallocate
2350
2351 /* file operations */
2352 checksumfs_create,
2353 checksumfs_open,
2354 checksumfs_close,
2355 checksumfs_free_cookie,
2356 checksumfs_read,
2357 checksumfs_write,
2358
2359 /* directory operations */
2360 checksumfs_create_dir,
2361 checksumfs_remove_dir,
2362 checksumfs_open_dir,
2363 checksumfs_close_dir,
2364 checksumfs_free_dir_cookie,
2365 checksumfs_read_dir,
2366 checksumfs_rewind_dir,
2367
2368 /* attribute directory operations */
2369 checksumfs_open_attr_dir,
2370 checksumfs_close_attr_dir,
2371 checksumfs_free_attr_dir_cookie,
2372 checksumfs_read_attr_dir,
2373 checksumfs_rewind_attr_dir,
2374
2375 /* attribute operations */
2376 checksumfs_create_attr,
2377 checksumfs_open_attr,
2378 checksumfs_close_attr,
2379 checksumfs_free_attr_cookie,
2380 checksumfs_read_attr,
2381 checksumfs_write_attr,
2382
2383 checksumfs_read_attr_stat,
2384 NULL, // write_attr_stat
2385 NULL, // rename_attr
2386 checksumfs_remove_attr,
2387
2388 /* support for node and FS layers */
2389 NULL, // create_special_node
2390 NULL // get_super_vnode
2391 };
2392