mkstemp
Stephen Weeks
MLton@sourcelight.com
Thu, 21 Jun 2001 16:20:57 -0700
> Strange about the 0600 vs. 0666. The manual says earlier versions of glibc
> use 0666 but newer ones use 0600. I would argue that you pretty much never
> want to use anything besides 0666 and 0777, with any extra security being
> handled by the umask. Still, it all depends on how paranoid you want to be.
> If you look at the info entry for mkstemp, it says:
> The file is opened using mode `0600'. If the file is meant
> to be used by other users this mode must be changed
> explicitly.
> Any way, it isn't a big deal since either way if you care about it you just
> call open directly. As to the mode, I would just leave mkstemp as in the C
> case. After all, it is what people expect, and it is just a trade off
> between flexibility and convenience.
OK. I will leave the mode hardwired as 600.
> By the way, looking at the info pages I see that there is a mkdtemp()
> function, but no man page. That uses mode 0700 (again, minus umask). I
> don't think I would bother with mkdtemp(), just because it isn't useful that
> often. For that you can just use use Posix.FileSys.mkdir.
OK.
> As to the source to mkstemp, you don't want to do the Random.seed on every
> call to tempName, do you? If so, then it really MUST be /dev/urandom, not
> /dev/random.
OK. I switched to a pseudo-random generator.
> Also 100 tries isn't enough. Also, you have to detect the
> reason for the failure. Looping is the right thing to do (I would probably
> go forever) if it fails by EEXIST
OK. I will continuing looping iff it fails with EEXIST.
> but for other errors you want to fail
> right away, no retries. (The usual case is failure because of permissions.)
OK.
> To me, the only question is if you should have mkstemp return both an in and
> out stream.
Or neither.
> I missed something. What is the argument for having MLton.TextIO.mkstemp
> return only the string? This would be very confusing since the file has been
> made.
The only argument in favor of an outstream over an instream is that surely you
are going to write the file you just created. However, there are cases in MLton
(and surely elsewhere) where that writing is done by another process, and hence
the outstream is useless too. So I can see an argument for dropping the
outstream and just returning the name (after all, it is call mkstemp, not
mkandopenstmp). Anyways, I am happy either way.
> Also I don't understand the suffix.
It is so the temp file can have a certain suffix, which is useful, e.g. if you
are about to call gcc on it.
> I think that the way to go is
> either
> string -> string * TextIO.outstream
> or
> string -> string * TextIO.instream * TextIO.outstream
> where the string argument is just like in the C mkstemp, but without the
> trailing "XXXXXX".
I still vote for
{prefix: string, suffix: string} -> string * TextIO.outstream
or possibly two versions (mkstemp and mkstemps).
Here is the latest.
local
val chars =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
val n = Word.fromInt (String.size chars)
val r: word ref = ref 0w0
in
fun tempName {prefix, suffix} =
let
val _ = r := Random.seed ()
val unique =
String.tabulate
(6, fn _ =>
let
val w = !r
val c = String.sub (chars,
Word.toInt (Word.mod (w, n)))
val _ = r := Word.div (w, n)
in c
end)
in
concat [prefix, unique, suffix]
end
end
fun mkstemp arg: string * outstream =
let
fun loop () =
let
val name = tempName arg
open Posix.FileSys
in
(name,
newOut (createf (name, O_WRONLY, O.flags [O.excl],
let open S
in flags [irusr, iwusr]
end)))
end handle e as PosixError.SysErr (_, SOME s) =>
if s = Posix.Error.exist
then loop ()
else raise e
in
loop ()
end