ZetCode

Python os.fchown Function

Last modified April 11, 2025

This comprehensive guide explores Python's os.fchown function, which changes file ownership using file descriptors. We'll cover UID/GID management, permission requirements, and practical examples.

Basic Definitions

The os.fchown function changes the owner and group of a file referenced by file descriptor. It's similar to os.chown but works with open files rather than paths.

Key parameters: fd (file descriptor), uid (user ID), gid (group ID). Use -1 to leave either uid or gid unchanged. Requires appropriate privileges.

Changing File Ownership

This basic example demonstrates changing ownership of an open file. We first get the current ownership, then modify it using os.fchown.

basic_ownership.py
import os
import pwd
import grp

file_path = "testfile.txt"

# Create a test file
with open(file_path, "w") as f:
    f.write("Test content")

# Open file and get current stats
with open(file_path, "r+") as f:
    stat_info = os.fstat(f.fileno())
    print(f"Current owner: {stat_info.st_uid}, group: {stat_info.st_gid}")

    # Change to root:root (usually 0:0)
    os.fchown(f.fileno(), 0, 0)
    new_stat = os.fstat(f.fileno())
    print(f"New owner: {new_stat.st_uid}, group: {new_stat.st_gid}")

# Clean up
os.remove(file_path)

This example requires root privileges to change ownership to root. It shows how to use os.fchown with an open file descriptor.

Note that we use os.fstat to verify ownership changes. The file descriptor remains valid throughout the operation.

Partial Ownership Changes

os.fchown allows changing just the user or just the group by passing -1 for the parameter you want to leave unchanged. This example demonstrates both cases.

partial_changes.py
import os
import pwd
import grp

file_path = "partial.txt"

# Create test file
with open(file_path, "w") as f:
    f.write("Partial change test")

# Get current user and group
current_uid = os.getuid()
current_gid = os.getgid()

with open(file_path, "r+") as f:
    # Change only the group (leave owner unchanged)
    os.fchown(f.fileno(), -1, current_gid)
    
    # Change only the owner (leave group unchanged)
    os.fchown(f.fileno(), current_uid, -1)

    # Verify changes
    stat_info = os.fstat(f.fileno())
    print(f"Owner: {stat_info.st_uid}, Group: {stat_info.st_gid}")

os.remove(file_path)

This shows how to modify either the user or group while leaving the other unchanged. The -1 value indicates no change should be made.

This is useful when you only need to modify one aspect of the ownership.

Error Handling

os.fchown can raise various exceptions. This example demonstrates proper error handling for permission issues and invalid file descriptors.

error_handling.py
import os
import sys

file_path = "error_test.txt"

try:
    # Create test file
    with open(file_path, "w") as f:
        f.write("Error handling test")

    # Try changing to root:root without privileges
    with open(file_path, "r+") as f:
        try:
            os.fchown(f.fileno(), 0, 0)
        except PermissionError:
            print("Permission denied - need root privileges")
        except OSError as e:
            print(f"OS error occurred: {e}")

    # Try with invalid file descriptor
    try:
        os.fchown(9999, 1000, 1000)
    except OSError as e:
        print(f"Invalid file descriptor: {e}")

finally:
    if os.path.exists(file_path):
        os.remove(file_path)

This shows common error scenarios: insufficient privileges and invalid file descriptors. Always handle these cases when using os.fchown.

The finally block ensures cleanup regardless of whether errors occurred.

Working with User/Group Names

This example shows how to convert between user/group names and IDs, making os.fchown more user-friendly in scripts.

name_resolution.py
import os
import pwd
import grp

file_path = "named_owner.txt"

def change_owner_by_name(fd, username, groupname):
    """Change owner using names instead of IDs"""
    uid = pwd.getpwnam(username).pw_uid
    gid = grp.getgrnam(groupname).gr_gid
    os.fchown(fd, uid, gid)

# Create test file
with open(file_path, "w") as f:
    f.write("Named owner test")

# Change ownership using names
with open(file_path, "r+") as f:
    # Change to current user and group
    username = pwd.getpwuid(os.getuid()).pw_name
    groupname = grp.getgrgid(os.getgid()).gr_name
    
    change_owner_by_name(f.fileno(), username, groupname)
    stat_info = os.fstat(f.fileno())
    print(f"Set owner to {username} ({stat_info.st_uid})")
    print(f"Set group to {groupname} ({stat_info.st_gid})")

os.remove(file_path)

The change_owner_by_name function abstracts the ID lookup, making the code more readable. It uses pwd and grp modules for name resolution.

This approach is useful when user/group names are known but IDs are not.

Preserving Ownership

This example shows how to temporarily change ownership and then restore the original values, useful in privileged operations.

preserve_ownership.py
import os
import pwd

file_path = "preserve_test.txt"

# Create test file
with open(file_path, "w") as f:
    f.write("Ownership preservation test")

with open(file_path, "r+") as f:
    # Get original ownership
    stat_info = os.fstat(f.fileno())
    original_uid = stat_info.st_uid
    original_gid = stat_info.st_gid
    
    try:
        # Temporarily change to root (requires privileges)
        os.fchown(f.fileno(), 0, 0)
        print("Changed ownership to root")
        
        # Perform privileged operations here
        
    finally:
        # Always restore original ownership
        os.fchown(f.fileno(), original_uid, original_gid)
        print("Restored original ownership")

    # Verify restoration
    restored_stat = os.fstat(f.fileno())
    assert restored_stat.st_uid == original_uid
    assert restored_stat.st_gid == original_gid

os.remove(file_path)

The try/finally block ensures ownership is restored even if operations fail. This is critical for maintaining system security and consistency.

The assertions verify the ownership was properly restored to original values.

Changing Directory Ownership

os.fchown works with any file descriptor, including directories. This example shows how to change ownership of an open directory.

directory_ownership.py
import os

dir_path = "test_dir"

# Create test directory
os.makedirs(dir_path, exist_ok=True)

# Open directory and change ownership
dir_fd = os.open(dir_path, os.O_RDONLY)
try:
    # Get current stats
    stat_info = os.fstat(dir_fd)
    print(f"Current owner: {stat_info.st_uid}, group: {stat_info.st_gid}")
    
    # Change ownership (to current user/group)
    os.fchown(dir_fd, os.getuid(), os.getgid())
    
    # Verify change
    new_stat = os.fstat(dir_fd)
    print(f"New owner: {new_stat.st_uid}, group: {new_stat.st_gid}")
finally:
    os.close(dir_fd)

# Clean up
os.rmdir(dir_path)

This demonstrates using os.fchown with directory file descriptors. The process is similar to files but uses os.open with O_RDONLY for directories.

Always close file descriptors in finally blocks to prevent resource leaks.

Security Considerations

Best Practices

Source References

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Python tutorials.