about summary refs log tree commit diff stats
path: root/src/file.lua
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2022-03-07 19:14:02 -0800
committerKartik K. Agaram <vc@akkartik.com>2022-03-07 19:14:02 -0800
commita0674f7b85430201ab40b73d775781d9eeeed065 (patch)
treeab520d62f8bf89ae1f0b79e49986bde7f92bb6b5 /src/file.lua
parent88827db20d7a2d1acde328f0b77351475d516d4a (diff)
downloadteliva-a0674f7b85430201ab40b73d775781d9eeeed065.tar.gz
hokey primitive to create temporary file
The trouble with os.tmpname() is that it always creates in /tmp.
If /tmp is in a different volume from our real filename, os.rename()
will fail.

This new primitive doesn't support primitive paths yet.

I'm also again nervous about the security implications of my whole
approach. What if we create an inner function called start_writing?
Would we be able to do anything inside it? I'm starting to suspect this
whole approach of going by caller name is broken. An app could also
create inner functions called 'main'..
Diffstat (limited to 'src/file.lua')
-rw-r--r--src/file.lua24
1 files changed, 23 insertions, 1 deletions
diff --git a/src/file.lua b/src/file.lua
index cd71f50..17e543c 100644
--- a/src/file.lua
+++ b/src/file.lua
@@ -23,8 +23,11 @@ end
 -- indicate you're done writing by calling :close()
 -- file will not be externally visible until :close()
 function start_writing(fs, filename)
+  if filename == nil then
+    error('start_writing requires two arguments: a file-system (nil for real disk) and a filename')
+  end
   local result = task.Channel:new()
-  local initial_filename = os.tmpname()
+  local initial_filename = temporary_filename_in_same_volume(filename)
   local outfile = io.open(initial_filename, 'w')
   if outfile == nil then return nil end
   result.close = function()
@@ -36,6 +39,25 @@ function start_writing(fs, filename)
   return result
 end
 
+function temporary_filename_in_same_volume(filename)
+  -- opening in same directory will hopefully keep it on the same volume,
+  -- so that a future rename works
+  local i = 1
+  while true do
+    temporary_filename = 'teliva_temp_'..filename..'_'..i
+    if io.open(temporary_filename) == nil then
+      -- file doesn't exist yet; create a placeholder and return it
+      local handle = io.open(temporary_filename, 'w')
+      if handle == nil then
+        error("this is unexpected; I can't create temporary files..")
+      end
+      handle:close()
+      return temporary_filename
+    end
+    i = i+1
+  end
+end
+
 function writing_task(outfile, chanin)
   while true do
     local line = chanin:recv()