I don't think you can really do this from just standard tools, but if you have access to a python interpreter, you can do something like:
python3 -c 'import os, sys, shutil; shutil.copyfileobj(sys.stdin, open("filename.txt", "w", opener=lambda name, flag: os.open(name, flag, mode=0o631)))' <<< "file_content"
Expanded, the python script basically does:
import os, sys, shutil
file_opener = lambda name, flag: os.open(name, flag, mode=0o631)
dest = open("filename.txt", "w", opener=file_opener)
shutil.copyfileobj(sys.stdin, dest)
There's probably something similar in other scripting language interpreter's toolkit.
Output of strace
:
$ strace python3 -c 'import os, sys, shutil; shutil.copyfileobj(sys.stdin, open("filename.txt", "w", opener=lambda name, flag: os.open(name, flag, mode=0o631)))' <<< "file_content"
...
openat(AT_FDCWD, "filename.txt", O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0631) = 3
ioctl(3, FIOCLEX) = 0
newfstatat(3, "", {st_mode=S_IFREG|0631, st_size=0, ...}, AT_EMPTY_PATH) = 0
ioctl(3, TCGETS, 0x7ffe4fce65e0) = -1 ENOTTY (Inappropriate ioctl for device)
lseek(3, 0, SEEK_CUR) = 0
ioctl(3, TCGETS, 0x7ffe4fce6430) = -1 ENOTTY (Inappropriate ioctl for device)
lseek(3, 0, SEEK_CUR) = 0
brk(0x55c88d356000) = 0x55c88d356000
read(0, "file_content\n", 65536) = 13
brk(0x55c88d346000) = 0x55c88d346000
read(0, "", 65523) = 0
read(0, "", 65536) = 0
write(3, "file_content\n", 13) = 13
...
Alternatively, if the only reason you care about atomicity is just to prevent the file content being read from a hostile process running as other users, and not necessarily the use of atomicity of open(..., mode)
itself, you can just set the umask to 777 in a subprocess so that the file is first created to deny permission to everyone:
$ (umask 777; <<< "file_content" > filename.txt)
$ ls -lah filename.txt
---------- 1 lieryan lieryan 13 Sep 8 15:58 filename.txt
then set the file permission with the right value right afterwards:
$ chmod 631 filename.txt
Or, putting it all together:
$ (umask 777; <<< "file_content" > filename.txt; chmod 631 filename.txt)
This is still susceptible to race condition attacks from other processes running as the same user, but if you have to worry about that, you're probably worrying about the wrong thing.
install
, so-m 755
is not needed. – Kusalananda Nov 11 '19 at 07:20install -b -m 755 <(echo contents) newfile
worked for me. – dimo414 Apr 25 '20 at 05:51install
does not atomically create files with the given permissions. It callsfchmod()
separately. An unauthorised user may open the file between the creation and the permissions change, thus it possible to read contents later that they are not allowed to read (e.g. for a private key file). Thusinstall
should not be used if file permission security is important. (This was edited into the question, but I think it's good to state it under this answer since it's currently marked accepted despite not fulfilling the requested "atomic" requirement.) – nh2 Feb 09 '22 at 11:35