test: extend coverage of bytesEqual
This commit is contained in:
1 parent
01ad058c1c
commit
a8554757bc
1 file changed
+156
+156
@@ -0,0 +1,156 @@
|
|||||||
|
#include "ulib.h"
|
||||||
|
#include "fs.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Exercises the symlink/readlink/link syscalls (sysSymlink/sysReadlink/
|
||||||
|
* sysLink -> vfs.c -> sfs.c, sfs.h already had the on-disk machinery from an
|
||||||
|
* earlier wave; this wave wires it up to ring 3) against the persistent SFS
|
||||||
|
* filesystem under "/mnt". Kernel-side symlink target strings are resolved
|
||||||
|
* relative to the SFS root (sfs.h), not to "/mnt" -- so a symlink created
|
||||||
|
* from userspace at "/mnt/linktest/link.txt" pointing at
|
||||||
|
* "/mnt/linktest/real.txt" must store the target as "/linktest/real.txt"
|
||||||
|
* (the "/mnt" prefix stripped, exactly what vfsSymlink hands to
|
||||||
|
* sfsCreateSymlink) for later resolution to actually find it.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int bytesEqual(const char* a, const char* b, unsigned long n)
|
||||||
|
{
|
||||||
|
for (unsigned long i = 0; i < n; i++)
|
||||||
|
if (a[i] != b[i])
|
||||||
|
return 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
print("linktest: start, pid=");
|
||||||
|
printDec(userGetpid());
|
||||||
|
print("\n");
|
||||||
|
|
||||||
|
if (userMkdir("/mnt/linktest") != 0) {
|
||||||
|
print("linktest: FAIL mkdir /mnt/linktest\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* payload = "frostbyte linktest payload\n";
|
||||||
|
unsigned long payloadLen = stringLength(payload);
|
||||||
|
|
||||||
|
int wfd = (int)userOpen("/mnt/linktest/real.txt", oReadWrite | oCreate);
|
||||||
|
if (wfd < 0) {
|
||||||
|
print("linktest: FAIL create real.txt\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (userWrite(wfd, payload, payloadLen) != (long)payloadLen) {
|
||||||
|
print("linktest: FAIL write real.txt\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
userClose(wfd);
|
||||||
|
|
||||||
|
/* Symlink resolution through open()/read(): opening the link must
|
||||||
|
transparently reach real.txt's data. */
|
||||||
|
if (userSymlink("/linktest/real.txt", "/mnt/linktest/link.txt") != 0) {
|
||||||
|
print("linktest: FAIL symlink create\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lfd = (int)userOpen("/mnt/linktest/link.txt", oReadOnly);
|
||||||
|
if (lfd < 0) {
|
||||||
|
print("linktest: FAIL open through symlink\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
char back[64];
|
||||||
|
long got = userRead(lfd, back, sizeof(back));
|
||||||
|
userClose(lfd);
|
||||||
|
if (got != (long)payloadLen || !bytesEqual(back, payload, payloadLen)) {
|
||||||
|
print("linktest: FAIL read through symlink mismatch\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
print("linktest: open/read transparently follows symlink ok\n");
|
||||||
|
|
||||||
|
/* stat() must also follow: size should match the target, not 0. */
|
||||||
|
FbStat st;
|
||||||
|
if (userStat("/mnt/linktest/link.txt", &st) != 0 || st.size != payloadLen) {
|
||||||
|
print("linktest: FAIL stat through symlink\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
print("linktest: stat follows symlink ok\n");
|
||||||
|
|
||||||
|
/* readlink() must NOT follow -- it reports the link's own target
|
||||||
|
string, not the target's contents. */
|
||||||
|
char targetBuf[64];
|
||||||
|
long targetLen = userReadlink("/mnt/linktest/link.txt", targetBuf, sizeof(targetBuf));
|
||||||
|
const char* expectedTarget = "/linktest/real.txt";
|
||||||
|
unsigned long expectedTargetLen = stringLength(expectedTarget);
|
||||||
|
if (targetLen != (long)expectedTargetLen ||
|
||||||
|
!bytesEqual(targetBuf, expectedTarget, expectedTargetLen)) {
|
||||||
|
print("linktest: FAIL readlink target mismatch\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
print("linktest: readlink reports target string ok\n");
|
||||||
|
|
||||||
|
/* readlink on a non-symlink must fail, not read garbage. */
|
||||||
|
if (userReadlink("/mnt/linktest/real.txt", targetBuf, sizeof(targetBuf)) >= 0) {
|
||||||
|
print("linktest: FAIL readlink on non-symlink should fail\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
print("linktest: readlink rejects non-symlink ok\n");
|
||||||
|
|
||||||
|
/* Hardlink: alias.txt shares real.txt's inode and data. */
|
||||||
|
if (userLink("/mnt/linktest/real.txt", "/mnt/linktest/alias.txt") != 0) {
|
||||||
|
print("linktest: FAIL hardlink create\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
int afd = (int)userOpen("/mnt/linktest/alias.txt", oReadOnly);
|
||||||
|
if (afd < 0) {
|
||||||
|
print("linktest: FAIL open alias.txt\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
got = userRead(afd, back, sizeof(back));
|
||||||
|
userClose(afd);
|
||||||
|
if (got != (long)payloadLen || !bytesEqual(back, payload, payloadLen)) {
|
||||||
|
print("linktest: FAIL alias.txt content mismatch\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
print("linktest: hardlink shares data ok\n");
|
||||||
|
|
||||||
|
/* Directories cannot be hardlinked. */
|
||||||
|
if (userLink("/mnt/linktest", "/mnt/linktest2") == 0) {
|
||||||
|
print("linktest: FAIL hardlinking a directory should be rejected\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
print("linktest: hardlink rejects directory ok\n");
|
||||||
|
|
||||||
|
/* Removing the original must leave the hardlinked alias alive (link
|
||||||
|
count keeps the inode+data around). */
|
||||||
|
if (userUnlink("/mnt/linktest/real.txt") != 0) {
|
||||||
|
print("linktest: FAIL unlink real.txt\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
afd = (int)userOpen("/mnt/linktest/alias.txt", oReadOnly);
|
||||||
|
if (afd < 0) {
|
||||||
|
print("linktest: FAIL alias.txt gone after unlinking real.txt\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
got = userRead(afd, back, sizeof(back));
|
||||||
|
userClose(afd);
|
||||||
|
if (got != (long)payloadLen || !bytesEqual(back, payload, payloadLen)) {
|
||||||
|
print("linktest: FAIL alias.txt data lost after unlinking real.txt\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
print("linktest: alias survives unlink of original ok\n");
|
||||||
|
|
||||||
|
/* Dangling symlink: creation succeeds (target is never validated), open
|
||||||
|
through it must fail cleanly instead of crashing. */
|
||||||
|
if (userSymlink("/linktest/does-not-exist.txt", "/mnt/linktest/dangling.txt") != 0) {
|
||||||
|
print("linktest: FAIL dangling symlink create\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (userOpen("/mnt/linktest/dangling.txt", oReadOnly) >= 0) {
|
||||||
|
print("linktest: FAIL open through dangling symlink should fail\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
print("linktest: dangling symlink open fails cleanly ok\n");
|
||||||
|
|
||||||
|
print("linktest: done\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in new issue
Block a user