shithub: mc

Download patch

ref: 943c865bcfacb00af4c681fc36fbcab2b7f9a55f
parent: 1f4389df622ed274371d0c134ffd3b1c5a94bd48
author: Ori Bernstein <ori@eigenstate.org>
date: Wed Sep 13 11:04:40 EDT 2017

Add loop checking to libfileutil.

--- a/lib/fileutil/bld.sub
+++ b/lib/fileutil/bld.sub
@@ -3,6 +3,9 @@
 	homedir.myr
 	tmpdir.myr
 
+	loopcheck+posixy.myr
+	loopcheck+plan9.myr
+
 	lib ../sys:sys
 	lib ../std:std
 ;;
--- /dev/null
+++ b/lib/fileutil/loopcheck+plan9.myr
@@ -1,0 +1,21 @@
+use std
+
+/* plan 9 can't have directory loops, by construction, so this is nops */
+pkg fileutil =
+	type loopcheck = void
+
+	const mkloopcheck	: (cwd : byte[:] -> loopcheck)
+	const freeloopcheck	: (l : loopcheck -> void)
+	const looped	: (l : loopcheck, p : byte[:] -> bool)
+;;
+
+const mkloopcheck = {cwd
+	-> (void : loopcheck)
+}
+
+const freeloopcheck = {l, l
+}
+
+const looped = {p
+	-> false
+}
--- /dev/null
+++ b/lib/fileutil/loopcheck+posixy.myr
@@ -1,0 +1,65 @@
+use std
+use sys
+
+/* plan 9 can't have directory loops, by construction, so this is nops */
+pkg fileutil =
+	type loopcheck = std.htab((int64, int64), void)#
+
+	const mkloopcheck	: (cwd : byte[:] -> loopcheck)
+	const freeloopcheck	: (l : loopcheck -> void)
+	const looped	: (l : loopcheck, p : byte[:] -> bool)
+;;
+
+const mkloopcheck = {cwd
+	var ht : std.htab((int64, int64), void)#
+	var l
+
+	ht = std.mkht(fidhash, fideq)
+	l = (ht : loopcheck)
+	looped(l, cwd)
+	-> l
+}
+
+const freeloopcheck = {l
+	std.htfree((l : std.htab((int64, int64), void)#))
+}
+
+const looped = {l, p
+	var ht, has
+
+	ht = (l : std.htab((int64, int64), void)#)
+	match fid(p)
+	| `std.Err e:
+		-> false
+	| `std.Ok id:
+		has = std.hthas(ht, id)
+		std.htput(ht, id, void)
+		-> has
+	;;
+}
+
+const fid = {p
+	var sb
+
+	if sys.stat(p, &sb) != 0
+		-> `std.Err void
+	else
+		-> `std.Ok ((sb.dev : int64), (sb.ino : int64))
+	;;
+}
+
+const fidhash = {fid
+	var dev, ino
+
+	(dev, ino) = fid
+	-> std.inthash(dev) ^ std.inthash(ino)
+}
+
+const fideq = {a, b
+	var adev, aino
+	var bdev, bino
+
+	(adev, aino) = a
+	(bdev, bino) = b
+	-> adev == bdev && aino == bino
+}
--- a/lib/fileutil/walk.myr
+++ b/lib/fileutil/walk.myr
@@ -1,9 +1,12 @@
 use std
 
+use "loopcheck"
+
 pkg fileutil =
 	type walkiter = struct
 		dirstk	: std.dir#[:]
 		curdir	: byte[:][:]
+		loopck	: loopcheck
 	;;
 
 	impl iterable walkiter -> byte[:]
@@ -13,9 +16,16 @@
 const bywalk = {p
 	match std.diropen(p)
 	| `std.Ok d:
-		-> [.dirstk = std.sldup([d][:]), .curdir = std.sldup([std.sldup(p)][:])]
+		-> [
+			.dirstk = std.sldup([d][:]),
+			.curdir = std.sldup([std.sldup(p)][:]),
+			.loopck = mkloopcheck(p),
+		]
 	| `std.Err e:
-		-> [.dirstk = [][:], .curdir = [][:]]
+		-> [
+			.dirstk = [][:],
+			.curdir = [][:]
+		]
 	;;
 }
 
@@ -25,6 +35,7 @@
 
 :nextfile
 		if itp.dirstk.len < 1
+			freeloopcheck(itp.loopck)
 			-> false
 		;;
 		cur = itp.dirstk[itp.dirstk.len - 1]
@@ -33,6 +44,10 @@
 		| `std.Some "..":	goto nextfile
 		| `std.Some ent:
 			p = std.pathcat(itp.curdir[itp.curdir.len - 1], ent)
+			if looped(itp.loopck, p)
+				std.slfree(p)
+				goto nextfile
+			;;
 			if std.fisdir(p)
 				match std.diropen(p)
 				| `std.Ok d: