permissions.rkt
#lang racket/base

(require ffi/unsafe)

(define *lib* (ffi-lib #f))

(define setuid
   (get-ffi-obj
    "setreuid"
    *lib*
    (_fun (uid) :: (uid : _int) (_int = uid) -> _int)))

(define ffi-chown
   (get-ffi-obj
    "chown"
    *lib*
    (_fun
     (path uid gid)
     ::
     (path : _string)
     (uid : _int)
     (gid : _int)
     ->
     _int)))

(define ffi-chmod
   (get-ffi-obj
    "chmod"
    *lib*
    (_fun (path mode) :: (path : _string) (mode : _int) -> _int)))

(define (drop-permissions (uid #f))
   (let ((uid (or uid (string->number (getenv "UID")))))
     (let ((result (setuid uid)))
       (when (< result 0) (error "setuid failed")))))

(define (chown path uid (gid #f))
   (let ((result (ffi-chown path uid (if gid gid -1))))
     (when (< result 0) (error "chown failed"))))

(define (chmod path mode)
   (let ((result (ffi-chmod path mode)))
     (when (< result 0) (error "chmod failed"))))

(provide drop-permissions chown chmod)