Skip to content

Instantly share code, notes, and snippets.

@odeke-em
Last active December 11, 2021 20:17
Show Gist options
  • Save odeke-em/0cb3ca3b70a593a43b0a9499a39db340 to your computer and use it in GitHub Desktop.
Save odeke-em/0cb3ca3b70a593a43b0a9499a39db340 to your computer and use it in GitHub Desktop.

Purpose

Demonstrate an odd effect that holding onto a file handle can still allow you to edit the contents, of that file even after its permissions have been changed, despite even invoking .Sync() or *flush()

Tested on:

  • Linux 4.15.0-147-generic #151-Ubuntu SMP
  • Darwin 19.6.0 root:xnu-6153.141.2~1/RELEASE_X86_64 x86_64

Results

C

$ gcc -o demo demo.c && ./demo 
Unfortunately mismatched content
Got:  abcdefghijkl
Want: abcdef

Go

$ go run demo.go 
Unfortunately mismatched content
Got:  abcdefghijkl
Want: abcdef
exit status 1

Python

$ python3 demo.py 
Unfortunately mismatched content
Got:  abcdefghijkl
Want: abcdef
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/errno.h>
#include <sys/stat.h>
#include <sys/types.h>
int main() {
char *name = "the_file_c.txt";
FILE *wf = fopen(name, "w");
if (wf == NULL) {
printf("\033[31mFailed to open file: %s: %s\033[00m", name, strerror(errno));
exit(1);
}
int n = fprintf(wf, "abcdef");
if (n != 6) {
fclose(wf);
printf("\033[31mShort write: got %d want 6\033[00m", n);
exit(1);
}
// Flush the stream
fflush(wf);
// Change the permission to be ONLY "-r--r--r--"
if ((n = chmod(name, 0444)) != 0) {
fclose(wf);
printf("\033[31mFailed to chmod the file: %s\033[00m", strerror(n));
exit(1);
}
// Write more content to the file.
fprintf(wf, "ghijkl");
fflush(wf);
// Revert the permission change.
if ((n = chmod(name, 0755)) != 0) {
fclose(wf);
printf("\033[31mFailed to revert permissions, got: %s\033[00m", strerror(n));
exit(1);
}
fclose(wf);
struct stat *st;
st = (struct stat*)malloc(sizeof(*st));
if (stat(name, st) != 0) {
free(st);
printf("\033[31mFailed to stat the file: %s\033[00m", strerror(errno));
exit(1);
}
size_t file_size = st->st_size;
free(st);
char *got = (char *)malloc(file_size);
if (got == NULL) {
printf("\033[31mMalloc failed: %s\033[00m", strerror(errno));
exit(1);
}
// Reopen it for purely reading.
FILE *rf = fopen(name, "r");
if (fread(got, sizeof(*got), st->st_size, rf) < 0) {
free(got);
fclose(rf);
printf("\033[31mFailed to read file: %s\033[00m", strerror(errno));
exit(1);
}
fclose(rf);
const char *ideal_want = "abcdef";
if (strcmp(got, ideal_want) != 0) {
printf("\033[31mUnfortunately mismatched content\nGot: %s\nWant: %s\n\033[00m",
got, ideal_want);
free(got);
exit(1);
}
free(got);
return 0;
}
package main
import (
"fmt"
"io/ioutil"
"os"
)
func main() {
name := "the_file_go.txt"
f, err := os.OpenFile(name, os.O_CREATE|os.O_WRONLY, 0755)
if err != nil {
panic(err)
}
defer f.Close()
if _, err := f.Write([]byte("abcdef")); err != nil {
panic(err)
}
if err := f.Sync(); err != nil {
panic(err)
}
// Change the permission to be ONLY "-r--r--r--"
if err := os.Chmod(f.Name(), 0444); err != nil {
panic(err)
}
// Write more content to the file.
if _, err := f.Write([]byte("ghijkl")); err != nil {
panic(err)
}
if err := f.Sync(); err != nil {
panic(err)
}
// Revert the permission change.
if err := os.Chmod(f.Name(), 0755); err != nil {
panic(err)
}
idealWant := "abcdef"
got, err := ioutil.ReadFile(f.Name())
if err != nil {
panic(err)
}
if string(got) != idealWant {
fmt.Printf("\033[31mUnfortunately mismatched content\nGot: %s\nWant: %s\033[00m\n", got, idealWant)
os.Exit(1);
}
}
#!/usr/bin/env python3
import os
import sys
def main():
name = 'the_file_py.txt'
with open(name, mode='w') as wf:
wf.write('abcdef')
wf.flush()
# Change the permissions to be ONLY "-r--r--r--"
os.chmod(wf.name, 0o444)
wf.write('ghijkl')
wf.flush()
# Revert the permission change
os.chmod(name, 0o755)
# Next let's try to read all the content.
ideal_want = 'abcdef'
with open(name, mode='r') as rf:
got = rf.readlines()[0].strip('\n')
if got != ideal_want:
print('\033[31mUnfortunately mismatched content\nGot: %s\nWant: %s\033[00m\n' % (got, ideal_want))
sys.exit(1)
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment