2 * Copyright 2003-2013, Axel Dörfler, axeld@pinc-software.de.
3 * Copyright 2014, Ingo Weinhold, ingo_weinhold@gmx.de.
4 * Copyright 2017, Jessica Hamilton, jessica.l.hamilton@gmail.com.
5 * Distributed under the terms of the MIT License.
17 #include <StorageDefs.h>
19 #include <AutoDeleter.h>
21 #include <boot/platform.h>
22 #include <boot/partitions.h>
23 #include <boot/stdio.h>
24 #include <boot/stage2.h>
25 #include <syscall_utils.h>
27 #include "package_support.h"
28 #include "RootFileSystem.h"
29 #include "file_systems/packagefs/packagefs.h"
36 # define TRACE(x) dprintf x
46 char nameBuffer
[B_FILE_NAME_LENGTH
- 1];
52 Descriptor(Node
*node
, void *cookie
);
55 ssize_t
ReadAt(off_t pos
, void *buffer
, size_t bufferSize
);
56 ssize_t
Read(void *buffer
, size_t bufferSize
);
57 ssize_t
WriteAt(off_t pos
, const void *buffer
, size_t bufferSize
);
58 ssize_t
Write(const void *buffer
, size_t bufferSize
);
60 void Stat(struct stat
&stat
);
61 status_t
Seek(off_t position
, int mode
);
63 off_t
Offset() const { return fOffset
; }
64 int32
RefCount() const { return fRefCount
; }
69 Node
*GetNode() const { return fNode
; }
78 #define MAX_VFS_DESCRIPTORS 64
80 NodeList gBootDevices
;
82 RootFileSystem
*gRoot
;
83 static Descriptor
*sDescriptors
[MAX_VFS_DESCRIPTORS
];
84 static Node
*sBootDevice
;
100 Node::Open(void **_cookie
, int mode
)
102 TRACE(("%p::Open()\n", this));
108 Node::Close(void *cookie
)
110 TRACE(("%p::Close()\n", this));
116 Node::ReadLink(char* buffer
, size_t bufferSize
)
123 Node::GetName(char *nameBuffer
, size_t bufferSize
) const
130 Node::GetFileMap(struct file_map_run
*runs
, int32
*count
)
158 Node::Stat(struct stat
& stat
)
160 stat
.st_mode
= Type();
161 stat
.st_size
= Size();
162 stat
.st_ino
= Inode();
170 TRACE(("%p::Acquire(), fRefCount = %ld\n", this, fRefCount
));
178 TRACE(("%p::Release(), fRefCount = %ld\n", this, fRefCount
));
179 if (--fRefCount
== 0) {
180 TRACE(("delete node: %p\n", this));
192 ConsoleNode::ConsoleNode()
199 ConsoleNode::Read(void *buffer
, size_t bufferSize
)
201 return ReadAt(NULL
, -1, buffer
, bufferSize
);
206 ConsoleNode::Write(const void *buffer
, size_t bufferSize
)
208 return WriteAt(NULL
, -1, buffer
, bufferSize
);
215 Directory::Directory()
222 Directory::ReadAt(void *cookie
, off_t pos
, void *buffer
, size_t bufferSize
)
229 Directory::WriteAt(void *cookie
, off_t pos
, const void *buffer
, size_t bufferSize
)
236 Directory::Type() const
243 Directory::Lookup(const char* name
, bool traverseLinks
)
245 Node
* node
= LookupDontTraverse(name
);
249 if (!traverseLinks
|| !S_ISLNK(node
->Type()))
252 // the node is a symbolic link, so we have to resolve the path
253 char linkPath
[B_PATH_NAME_LENGTH
];
254 status_t error
= node
->ReadLink(linkPath
, sizeof(linkPath
));
257 // we don't need this one anymore
262 // let open_from() do the real work
263 int fd
= open_from(this, linkPath
, O_RDONLY
);
267 node
= get_node_from(fd
);
277 Directory::CreateFile(const char *name
, mode_t permissions
, Node
**_node
)
286 MemoryDisk::MemoryDisk(const uint8
* data
, size_t size
, const char* name
)
291 strlcpy(fName
, name
, sizeof(fName
));
296 MemoryDisk::ReadAt(void* cookie
, off_t pos
, void* buffer
, size_t bufferSize
)
300 if ((size_t)pos
>= fSize
)
303 if (pos
+ bufferSize
> fSize
)
304 bufferSize
= fSize
- pos
;
306 memcpy(buffer
, fData
+ pos
, bufferSize
);
312 MemoryDisk::WriteAt(void* cookie
, off_t pos
, const void* buffer
,
315 return B_NOT_ALLOWED
;
320 MemoryDisk::Size() const
327 MemoryDisk::GetName(char *nameBuffer
, size_t bufferSize
) const
332 strlcpy(nameBuffer
, fName
, bufferSize
);
340 Descriptor::Descriptor(Node
*node
, void *cookie
)
350 Descriptor::~Descriptor()
356 Descriptor::Read(void *buffer
, size_t bufferSize
)
358 ssize_t bytesRead
= fNode
->ReadAt(fCookie
, fOffset
, buffer
, bufferSize
);
359 if (bytesRead
> B_OK
)
360 fOffset
+= bytesRead
;
367 Descriptor::ReadAt(off_t pos
, void *buffer
, size_t bufferSize
)
369 return fNode
->ReadAt(fCookie
, pos
, buffer
, bufferSize
);
374 Descriptor::Write(const void *buffer
, size_t bufferSize
)
376 ssize_t bytesWritten
= fNode
->WriteAt(fCookie
, fOffset
, buffer
, bufferSize
);
377 if (bytesWritten
> B_OK
)
378 fOffset
+= bytesWritten
;
385 Descriptor::WriteAt(off_t pos
, const void *buffer
, size_t bufferSize
)
387 return fNode
->WriteAt(fCookie
, pos
, buffer
, bufferSize
);
392 Descriptor::Stat(struct stat
&stat
)
399 Descriptor::Seek(off_t position
, int mode
)
405 newPosition
= position
;
408 newPosition
= fOffset
+ position
;
414 newPosition
= st
.st_size
+ position
;
424 fOffset
= newPosition
;
430 Descriptor::Acquire()
438 Descriptor::Release()
440 if (--fRefCount
== 0) {
441 status_t status
= fNode
->Close(fCookie
);
453 BootVolume::BootVolume()
455 fRootDirectory(NULL
),
456 fSystemDirectory(NULL
),
457 fPackageVolumeInfo(NULL
),
458 fPackageVolumeState(NULL
)
463 BootVolume::~BootVolume()
470 BootVolume::SetTo(Directory
* rootDirectory
,
471 PackageVolumeInfo
* packageVolumeInfo
,
472 PackageVolumeState
* packageVolumeState
)
476 status_t error
= _SetTo(rootDirectory
, packageVolumeInfo
,
488 if (fRootDirectory
!= NULL
) {
489 fRootDirectory
->Release();
490 fRootDirectory
= NULL
;
493 if (fSystemDirectory
!= NULL
) {
494 fSystemDirectory
->Release();
495 fSystemDirectory
= NULL
;
498 if (fPackageVolumeInfo
!= NULL
) {
499 fPackageVolumeInfo
->ReleaseReference();
500 fPackageVolumeInfo
= NULL
;
501 fPackageVolumeState
= NULL
;
507 BootVolume::_SetTo(Directory
* rootDirectory
,
508 PackageVolumeInfo
* packageVolumeInfo
,
509 PackageVolumeState
* packageVolumeState
)
513 if (rootDirectory
== NULL
)
516 fRootDirectory
= rootDirectory
;
517 fRootDirectory
->Acquire();
519 // find the system directory
520 Node
* systemNode
= fRootDirectory
->Lookup("system", true);
521 if (systemNode
== NULL
|| !S_ISDIR(systemNode
->Type())) {
522 if (systemNode
!= NULL
)
523 systemNode
->Release();
525 return B_ENTRY_NOT_FOUND
;
528 fSystemDirectory
= static_cast<Directory
*>(systemNode
);
530 if (packageVolumeInfo
== NULL
) {
531 // get a package volume info
532 BReference
<PackageVolumeInfo
> packageVolumeInfoReference(
533 new(std::nothrow
) PackageVolumeInfo
);
534 status_t error
= packageVolumeInfoReference
->SetTo(fSystemDirectory
,
537 // apparently not packaged
541 fPackageVolumeInfo
= packageVolumeInfoReference
.Detach();
543 fPackageVolumeInfo
= packageVolumeInfo
;
544 fPackageVolumeInfo
->AcquireReference();
547 fPackageVolumeState
= packageVolumeState
!= NULL
548 ? packageVolumeState
: fPackageVolumeInfo
->States().Head();
550 // try opening the system package
551 int packageFD
= _OpenSystemPackage();
556 Directory
* packageRootDirectory
;
557 status_t error
= packagefs_mount_file(packageFD
, fSystemDirectory
,
558 packageRootDirectory
);
565 fSystemDirectory
->Release();
566 fSystemDirectory
= packageRootDirectory
;
573 BootVolume::_OpenSystemPackage()
575 // open the packages directory
576 Node
* packagesNode
= fSystemDirectory
->Lookup("packages", false);
577 if (packagesNode
== NULL
)
579 MethodDeleter
<Node
, status_t
> packagesNodeReleaser(packagesNode
,
582 if (!S_ISDIR(packagesNode
->Type()))
584 Directory
* packageDirectory
= (Directory
*)packagesNode
;
586 // open the system package
587 return open_from(packageDirectory
, fPackageVolumeState
->SystemPackage(),
596 vfs_init(stage2_args
*args
)
598 gRoot
= new(nothrow
) RootFileSystem();
607 register_boot_file_system(BootVolume
& bootVolume
)
609 Directory
* rootDirectory
= bootVolume
.RootDirectory();
610 gRoot
->AddLink("boot", rootDirectory
);
612 Partition
*partition
;
613 status_t status
= gRoot
->GetPartitionFor(rootDirectory
, &partition
);
614 if (status
!= B_OK
) {
615 dprintf("register_boot_file_system(): could not locate boot volume in "
620 gBootVolume
.SetInt64(BOOT_VOLUME_PARTITION_OFFSET
,
623 if (bootVolume
.IsPackaged()) {
624 gBootVolume
.SetBool(BOOT_VOLUME_PACKAGED
, true);
625 PackageVolumeState
* state
= bootVolume
.GetPackageVolumeState();
626 if (state
->Name() != NULL
)
627 gBootVolume
.AddString(BOOT_VOLUME_PACKAGES_STATE
, state
->Name());
630 Node
*device
= get_node_from(partition
->FD());
631 if (device
== NULL
) {
632 dprintf("register_boot_file_system(): could not get boot device!\n");
636 return platform_register_boot_device(device
);
640 /*! Gets the boot device, scans all of its partitions, gets the
641 boot partition, and mounts its file system.
643 \param args The stage 2 arguments.
644 \param _bootVolume On success set to the boot volume.
645 \return \c B_OK on success, another error code otherwise.
648 get_boot_file_system(stage2_args
* args
, BootVolume
& _bootVolume
)
650 status_t error
= platform_add_boot_device(args
, &gBootDevices
);
654 NodeIterator iterator
= gBootDevices
.GetIterator();
655 while (iterator
.HasNext()) {
656 Node
*device
= iterator
.Next();
658 error
= add_partitions_for(device
, false, true);
662 NodeList bootPartitions
;
663 error
= platform_get_boot_partitions(args
, device
, &gPartitions
, &bootPartitions
);
667 NodeIterator partitionIterator
= bootPartitions
.GetIterator();
668 while (partitionIterator
.HasNext()) {
669 Partition
*partition
= (Partition
*)partitionIterator
.Next();
671 Directory
*fileSystem
;
672 error
= partition
->Mount(&fileSystem
, true);
674 // this partition doesn't contain any known file system; we
675 // don't need it anymore
676 gPartitions
.Remove(partition
);
681 // init the BootVolume
682 error
= _bootVolume
.SetTo(fileSystem
);
686 sBootDevice
= device
;
695 /** Mounts all file systems recognized on the given device by
696 * calling the add_partitions_for() function on them.
700 mount_file_systems(stage2_args
*args
)
702 // mount other partitions on boot device (if any)
703 NodeIterator iterator
= gPartitions
.GetIterator();
705 Partition
*partition
= NULL
;
706 while ((partition
= (Partition
*)iterator
.Next()) != NULL
) {
707 // don't scan known partitions again
708 if (partition
->IsFileSystem())
711 // remove the partition if it doesn't contain a (known) file system
712 if (partition
->Scan(true) != B_OK
&& !partition
->IsFileSystem()) {
713 gPartitions
.Remove(partition
);
718 // add all block devices the platform has for us
720 status_t status
= platform_add_block_devices(args
, &gBootDevices
);
724 iterator
= gBootDevices
.GetIterator();
725 Node
*device
= NULL
, *last
= NULL
;
726 while ((device
= iterator
.Next()) != NULL
) {
727 // don't scan former boot device again
728 if (device
== sBootDevice
)
731 if (add_partitions_for(device
, true) == B_OK
) {
732 // ToDo: we can't delete the object here, because it must
733 // be removed from the list before we know that it was
736 /* // if the Release() deletes the object, we need to skip it
737 if (device->Release() > 0) {
738 list_remove_item(&gBootDevices, device);
747 if (gPartitions
.IsEmpty())
748 return B_ENTRY_NOT_FOUND
;
752 if (gRoot
->Open(&cookie
, O_RDONLY
) == B_OK
) {
753 Directory
*directory
;
754 while (gRoot
->GetNextNode(cookie
, (Node
**)&directory
) == B_OK
) {
756 if (directory
->GetName(name
, sizeof(name
)) == B_OK
)
757 printf(":: %s (%p)\n", name
, directory
);
760 if (directory
->Open(&subCookie
, O_RDONLY
) == B_OK
) {
761 while (directory
->GetNextEntry(subCookie
, name
, sizeof(name
)) == B_OK
) {
762 printf("\t%s\n", name
);
764 directory
->Close(subCookie
);
767 gRoot
->Close(cookie
);
775 /*! Resolves \a directory + \a path to a node.
776 Note that \a path will be modified by the function.
779 get_node_for_path(Directory
*directory
, char *path
, Node
**_node
)
781 directory
->Acquire();
782 // balance Acquire()/Release() calls
788 // walk to find the next path component ("path" will point to a single
789 // path component), and filter out multiple slashes
790 for (nextPath
= path
+ 1; nextPath
[0] != '\0' && nextPath
[0] != '/'; nextPath
++);
792 if (*nextPath
== '/') {
796 while (*nextPath
== '/');
799 nextNode
= directory
->Lookup(path
, true);
800 directory
->Release();
802 if (nextNode
== NULL
)
803 return B_ENTRY_NOT_FOUND
;
806 if (S_ISDIR(nextNode
->Type()))
807 directory
= (Directory
*)nextNode
;
809 return B_NOT_ALLOWED
;
812 if (path
[0] == '\0') {
818 return B_ENTRY_NOT_FOUND
;
822 /*! Version of get_node_for_path() not modifying \a path.
825 get_node_for_path(Directory
* directory
, const char* path
, Node
** _node
)
827 char* mutablePath
= strdup(path
);
828 if (mutablePath
== NULL
)
830 MemoryDeleter
mutablePathDeleter(mutablePath
);
832 return get_node_for_path(directory
, mutablePath
, _node
);
839 get_descriptor(int fd
)
841 if (fd
< 0 || fd
>= MAX_VFS_DESCRIPTORS
)
844 return sDescriptors
[fd
];
849 free_descriptor(int fd
)
851 if (fd
>= MAX_VFS_DESCRIPTORS
)
854 delete sDescriptors
[fd
];
855 sDescriptors
[fd
] = NULL
;
859 /** Reserves an entry of the descriptor table and
860 * assigns the given node to it.
864 open_node(Node
*node
, int mode
)
869 // get free descriptor
872 for (; fd
< MAX_VFS_DESCRIPTORS
; fd
++) {
873 if (sDescriptors
[fd
] == NULL
)
876 if (fd
== MAX_VFS_DESCRIPTORS
)
879 TRACE(("got descriptor %d for node %p\n", fd
, node
));
881 // we got a free descriptor entry, now try to open the node
884 status_t status
= node
->Open(&cookie
, mode
);
888 TRACE(("could open node at %p\n", node
));
890 Descriptor
*descriptor
= new(nothrow
) Descriptor(node
, cookie
);
891 if (descriptor
== NULL
)
894 sDescriptors
[fd
] = descriptor
;
903 Descriptor
*descriptor
= get_descriptor(fd
);
904 if (descriptor
== NULL
)
905 RETURN_AND_SET_ERRNO(B_FILE_ERROR
);
907 descriptor
->Acquire();
908 RETURN_AND_SET_ERRNO(fd
);
913 lseek(int fd
, off_t offset
, int whence
)
915 Descriptor
* descriptor
= get_descriptor(fd
);
916 if (descriptor
== NULL
)
917 RETURN_AND_SET_ERRNO(B_FILE_ERROR
);
919 status_t error
= descriptor
->Seek(offset
, whence
);
921 RETURN_AND_SET_ERRNO(B_FILE_ERROR
);
923 return descriptor
->Offset();
928 ftruncate(int fd
, off_t newSize
)
930 dprintf("ftruncate() not implemented!\n");
931 RETURN_AND_SET_ERRNO(B_FILE_ERROR
);
936 read_pos(int fd
, off_t offset
, void *buffer
, size_t bufferSize
)
938 Descriptor
*descriptor
= get_descriptor(fd
);
939 if (descriptor
== NULL
)
940 RETURN_AND_SET_ERRNO(B_FILE_ERROR
);
942 RETURN_AND_SET_ERRNO(descriptor
->ReadAt(offset
, buffer
, bufferSize
));
947 pread(int fd
, void* buffer
, size_t bufferSize
, off_t offset
)
949 return read_pos(fd
, offset
, buffer
, bufferSize
);
954 read(int fd
, void *buffer
, size_t bufferSize
)
956 Descriptor
*descriptor
= get_descriptor(fd
);
957 if (descriptor
== NULL
)
958 RETURN_AND_SET_ERRNO(B_FILE_ERROR
);
960 RETURN_AND_SET_ERRNO(descriptor
->Read(buffer
, bufferSize
));
965 write_pos(int fd
, off_t offset
, const void *buffer
, size_t bufferSize
)
967 Descriptor
*descriptor
= get_descriptor(fd
);
968 if (descriptor
== NULL
)
969 RETURN_AND_SET_ERRNO(B_FILE_ERROR
);
971 RETURN_AND_SET_ERRNO(descriptor
->WriteAt(offset
, buffer
, bufferSize
));
976 pwrite(int fd
, const void* buffer
, size_t bufferSize
, off_t offset
)
978 return write_pos(fd
, offset
, buffer
, bufferSize
);
983 write(int fd
, const void *buffer
, size_t bufferSize
)
985 Descriptor
*descriptor
= get_descriptor(fd
);
986 if (descriptor
== NULL
)
987 RETURN_AND_SET_ERRNO(B_FILE_ERROR
);
989 RETURN_AND_SET_ERRNO(descriptor
->Write(buffer
, bufferSize
));
994 writev(int fd
, const struct iovec
* vecs
, size_t count
)
996 size_t totalWritten
= 0;
998 for (size_t i
= 0; i
< count
; i
++) {
999 ssize_t written
= write(fd
, vecs
[i
].iov_base
, vecs
[i
].iov_len
);
1001 return totalWritten
== 0 ? written
: totalWritten
;
1003 totalWritten
+= written
;
1005 if ((size_t)written
!= vecs
[i
].iov_len
)
1009 return totalWritten
;
1014 open(const char *name
, int mode
, ...)
1016 mode_t permissions
= 0;
1017 if ((mode
& O_CREAT
) != 0) {
1019 va_start(args
, mode
);
1020 permissions
= va_arg(args
, int) /*& ~__gUmask*/;
1021 // adapt the permissions as required by POSIX
1025 // we always start at the top (there is no notion of a current directory (yet?))
1026 RETURN_AND_SET_ERRNO(open_from(gRoot
, name
, mode
, permissions
));
1031 open_from(Directory
*directory
, const char *name
, int mode
, mode_t permissions
)
1033 if (name
[0] == '/') {
1034 // ignore the directory and start from root if we are asked to do that
1039 char path
[B_PATH_NAME_LENGTH
];
1040 if (strlcpy(path
, name
, sizeof(path
)) >= sizeof(path
))
1041 return B_NAME_TOO_LONG
;
1044 status_t error
= get_node_for_path(directory
, path
, &node
);
1045 if (error
!= B_OK
) {
1046 if (error
!= B_ENTRY_NOT_FOUND
)
1049 if ((mode
& O_CREAT
) == 0)
1050 return B_ENTRY_NOT_FOUND
;
1052 // try to resolve the parent directory
1053 strlcpy(path
, name
, sizeof(path
));
1054 if (char* lastSlash
= strrchr(path
, '/')) {
1055 if (lastSlash
[1] == '\0')
1056 return B_ENTRY_NOT_FOUND
;
1059 name
= lastSlash
+ 1;
1061 // resolve the directory
1062 if (get_node_for_path(directory
, path
, &node
) != B_OK
)
1063 return B_ENTRY_NOT_FOUND
;
1065 if (node
->Type() != S_IFDIR
) {
1067 return B_NOT_A_DIRECTORY
;
1070 directory
= static_cast<Directory
*>(node
);
1072 directory
->Acquire();
1075 error
= directory
->CreateFile(name
, permissions
, &node
);
1076 directory
->Release();
1080 } else if ((mode
& O_EXCL
) != 0) {
1082 return B_FILE_EXISTS
;
1085 int fd
= open_node(node
, mode
);
1092 /** Since we don't have directory functions yet, this
1093 * function is needed to get the contents of a directory.
1094 * It should be removed once readdir() & co. are in place.
1098 get_node_from(int fd
)
1100 Descriptor
*descriptor
= get_descriptor(fd
);
1101 if (descriptor
== NULL
)
1104 return descriptor
->GetNode();
1109 get_stat(Directory
* directory
, const char* path
, struct stat
& st
)
1112 status_t error
= get_node_for_path(directory
, path
, &node
);
1123 directory_from(DIR* dir
)
1125 return dir
!= NULL
? dir
->directory
: NULL
;
1132 Descriptor
*descriptor
= get_descriptor(fd
);
1133 if (descriptor
== NULL
)
1134 RETURN_AND_SET_ERRNO(B_FILE_ERROR
);
1136 status_t status
= descriptor
->Release();
1137 if (!descriptor
->RefCount())
1138 free_descriptor(fd
);
1140 RETURN_AND_SET_ERRNO(status
);
1144 // ToDo: remove this kludge when possible
1146 #if defined(fstat) && !defined(main)
1147 _fstat(int fd
, struct stat
*stat
, size_t /*statSize*/)
1149 fstat(int fd
, struct stat
*stat
)
1153 RETURN_AND_SET_ERRNO(B_BAD_VALUE
);
1155 Descriptor
*descriptor
= get_descriptor(fd
);
1156 if (descriptor
== NULL
)
1157 RETURN_AND_SET_ERRNO(B_FILE_ERROR
);
1159 descriptor
->Stat(*stat
);
1165 open_directory(Directory
* baseDirectory
, const char* path
)
1167 DIR* dir
= new(std::nothrow
) DIR;
1169 errno
= B_NO_MEMORY
;
1172 ObjectDeleter
<DIR> dirDeleter(dir
);
1175 status_t error
= get_node_for_path(baseDirectory
, path
, &node
);
1176 if (error
!= B_OK
) {
1180 MethodDeleter
<Node
, status_t
> nodeReleaser(node
, &Node::Release
);
1182 if (!S_ISDIR(node
->Type())) {
1187 dir
->directory
= static_cast<Directory
*>(node
);
1189 error
= dir
->directory
->Open(&dir
->cookie
, O_RDONLY
);
1190 if (error
!= B_OK
) {
1195 nodeReleaser
.Detach();
1196 return dirDeleter
.Detach();
1201 opendir(const char* dirName
)
1203 return open_directory(gRoot
, dirName
);
1211 dir
->directory
->Close(dir
->cookie
);
1212 dir
->directory
->Release();
1224 errno
= B_BAD_VALUE
;
1229 status_t error
= dir
->directory
->GetNextEntry(dir
->cookie
,
1230 dir
->entry
.d_name
, B_FILE_NAME_LENGTH
);
1231 if (error
!= B_OK
) {
1236 dir
->entry
.d_pdev
= 0;
1238 dir
->entry
.d_pino
= dir
->directory
->Inode();
1239 dir
->entry
.d_dev
= dir
->entry
.d_pdev
;
1242 if (strcmp(dir
->entry
.d_name
, ".") == 0
1243 || strcmp(dir
->entry
.d_name
, "..") == 0) {
1244 // Note: That's obviously not correct for "..", but we can't
1245 // retrieve that information.
1246 dir
->entry
.d_ino
= dir
->entry
.d_pino
;
1248 Node
* node
= dir
->directory
->Lookup(dir
->entry
.d_name
, false);
1252 dir
->entry
.d_ino
= node
->Inode();
1265 errno
= B_BAD_VALUE
;
1269 status_t error
= dir
->directory
->Rewind(dir
->cookie
);