Skip to content

Add system-wide file descriptor limits support (/proc/sys/fs)#71

Merged
Nugine merged 6 commits intomainfrom
copilot/fix-file-max-settings
Dec 14, 2025
Merged

Add system-wide file descriptor limits support (/proc/sys/fs)#71
Nugine merged 6 commits intomainfrom
copilot/fix-file-max-settings

Conversation

Copy link
Contributor

Copilot AI commented Dec 7, 2025

This PR adds comprehensive support for reading and writing system-wide file descriptor limits from /proc/sys/fs/ on Linux and Android systems, addressing the distinction between system-wide limits (file-max) and per-process limits (NOFILE).

Changes

New module sys_limits provides:

  • SysLimits::read() - reads file-max, file-nr, and nr_open from /proc/sys/fs/
  • SysLimits::set_file_max(u64) - sets system-wide file descriptor maximum (requires root)
  • SysLimits::set_nr_open(u64) - sets per-process NOFILE ceiling (requires root)

New types:

  • SysLimits - container for system-wide limits
  • FileNr - current file descriptor usage stats (allocated, free, maximum)

Example usage:

use rlimit::SysLimits;

let limits = SysLimits::read()?;
println!("System file-max: {:?}", limits.file_max);        // /proc/sys/fs/file-max
println!("Current usage: {:?}", limits.file_nr);           // /proc/sys/fs/file-nr  
println!("Per-process ceiling: {:?}", limits.nr_open);     // /proc/sys/fs/nr_open

See examples/sys_limits.rs for a comparison between system-wide and per-process limits.

Documentation

  • Added comprehensive inline documentation for all new types and methods
  • Documented that the free field in FileNr has been deprecated since Linux 2.6 and always returns 0 in modern kernels
  • Clarified that file parsing uses whitespace-separated values for flexibility
  • 3 unit tests + 1 integration test with thread-safe temporary file handling

Testing

  • ✅ All unit tests pass (5 tests)
  • ✅ All integration tests pass (6 tests)
  • ✅ All doc tests pass (8 tests)
  • ✅ Clippy passes with no warnings
  • ✅ Formatting check passes
  • ✅ CodeQL security scan passes with no alerts

Security

No security vulnerabilities were introduced. The implementation:

  • Uses safe Rust only (#![deny(unsafe_code)])
  • Properly handles file I/O errors
  • Validates input data format
  • Documents privilege requirements for write operations

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

Original prompt

This section details on the original issue you should resolve

<issue_title>/proc/sys/fs/file-max</issue_title>
<issue_description>+ https://serverfault.com/questions/122679/how-do-ulimit-n-and-proc-sys-fs-file-max-differ</issue_description>

Comments on the Issue (you are @copilot in this section)


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Copilot AI and others added 2 commits December 7, 2025 07:28
Co-authored-by: Nugine <30099658+Nugine@users.noreply.github.com>
… write function

Co-authored-by: Nugine <30099658+Nugine@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix file-max settings in sysctl configuration Add system-wide file descriptor limits support (/proc/sys/fs) Dec 7, 2025
Copilot AI requested a review from Nugine December 7, 2025 07:33
@Nugine
Copy link
Owner

Nugine commented Dec 7, 2025

@copilot Fix the CI failures

Co-authored-by: Nugine <30099658+Nugine@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Dec 7, 2025

@copilot Fix the CI failures

Fixed in 076d28c. The CI failure was due to trailing whitespace in the example file that didn't pass cargo fmt --check.

@Nugine Nugine marked this pull request as ready for review December 14, 2025 18:44
@Nugine Nugine requested a review from Copilot December 14, 2025 18:44
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds comprehensive support for reading and writing system-wide file descriptor limits from /proc/sys/fs/ on Linux and Android systems. The implementation creates a new module parallel to the existing proc_limits module, providing access to system-wide resource limits rather than per-process limits.

Key changes:

  • New SysLimits struct and API for reading system-wide file descriptor limits (file-max, file-nr, nr_open)
  • Write operations for file-max and nr_open (requires root privileges)
  • Comprehensive example demonstrating the differences between system-wide and per-process limits

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 7 comments.

File Description
src/sys_limits.rs New module implementing system-wide limits with read/write operations, helper functions, and unit tests
src/lib.rs Module integration to export SysLimits types under Linux/Android platform guards
examples/sys_limits.rs Example demonstrating system-wide vs per-process limits with practical use cases
tests/it/linux.rs Integration test validating SysLimits functionality and data consistency

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


/// Reads file descriptor statistics from `/proc/sys/fs/file-nr`.
///
/// The file contains three tab-separated values: allocated, free, and maximum.
Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment states the values are "tab-separated", but the implementation on line 145 uses split_whitespace() which handles any whitespace (tabs, spaces, etc.). While this works correctly since /proc/sys/fs/file-nr typically uses tabs, the documentation should either be updated to say "whitespace-separated" for accuracy or the implementation should explicitly use split('\t') if strictly tab-separated parsing is required.

Suggested change
/// The file contains three tab-separated values: allocated, free, and maximum.
/// The file contains three whitespace-separated values: allocated, free, and maximum.

Copilot uses AI. Check for mistakes.
use std::io::Write;

let temp_dir = std::env::temp_dir();
let test_file = temp_dir.join("test_file_nr.txt");
Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test file name "test_file_nr.txt" in temp_dir could potentially collide with parallel test execution. Consider using a more unique name, such as including the thread ID or using a randomly generated suffix, to prevent race conditions in test environments where tests may run concurrently.

Suggested change
let test_file = temp_dir.join("test_file_nr.txt");
let test_file = temp_dir.join(format!(
"test_file_nr_{}.txt",
std::thread::current().id().as_u64()
));

Copilot uses AI. Check for mistakes.
Comment on lines +233 to +248

let temp_dir = std::env::temp_dir();
let test_file = temp_dir.join("test_u64.txt");

// Write test data
let mut file = fs::File::create(&test_file).unwrap();
writeln!(file, "1048576").unwrap();
file.flush().unwrap();
drop(file);

// Read and verify
let result = read_u64_from_file(&test_file).unwrap();
assert_eq!(result, 1_048_576);

// Clean up
fs::remove_file(&test_file).ok();
Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to the test_file_nr.txt case, this test file name could collide in parallel test execution. Consider using a unique name to prevent race conditions in concurrent test environments.

Suggested change
let temp_dir = std::env::temp_dir();
let test_file = temp_dir.join("test_u64.txt");
// Write test data
let mut file = fs::File::create(&test_file).unwrap();
writeln!(file, "1048576").unwrap();
file.flush().unwrap();
drop(file);
// Read and verify
let result = read_u64_from_file(&test_file).unwrap();
assert_eq!(result, 1_048_576);
// Clean up
fs::remove_file(&test_file).ok();
use tempfile::NamedTempFile;
// Create a uniquely named temporary file
let mut temp_file = NamedTempFile::new().unwrap();
// Write test data
writeln!(temp_file, "1048576").unwrap();
temp_file.flush().unwrap();
// Read and verify
let result = read_u64_from_file(temp_file.path()).unwrap();
assert_eq!(result, 1_048_576);
// No need to manually clean up; NamedTempFile will remove the file when dropped

Copilot uses AI. Check for mistakes.
Comment on lines +106 to +107
let file_max = limits.file_max.unwrap();
println!("file_max: {}", file_max);
Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code unwraps the same optional value twice. After checking that file_max is Some on line 105, the code unwraps it on line 106 and stores it in the file_max variable. However, line 107 then prints this value. The redundant unwrap on line 106 shadows the outer scope and should be removed or the variable should be reused directly.

Copilot uses AI. Check for mistakes.
Comment on lines +125 to +126
let nr_open = limits.nr_open.unwrap();
println!("nr_open: {}", nr_open);
Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to the file_max pattern above, this code unwraps nr_open after verifying it exists. However, unlike file_max which is used in an assertion on line 119, this unwrapped value is only used for printing. Consider directly printing without the intermediate variable or ensuring consistency with the file_max pattern.

Copilot uses AI. Check for mistakes.
Comment on lines +40 to +46
/// File descriptor usage statistics from `/proc/sys/fs/file-nr`.
#[cfg_attr(docsrs, doc(cfg(any(target_os = "linux", target_os = "android"))))]
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct FileNr {
/// Number of allocated file descriptors.
pub allocated: u64,
/// Number of free file descriptors.
Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "free" field in /proc/sys/fs/file-nr has been deprecated since Linux 2.6 and is always 0 in modern kernels. The documentation should mention that this field is deprecated and always returns 0 to avoid confusion for users who might expect it to contain meaningful data.

Suggested change
/// File descriptor usage statistics from `/proc/sys/fs/file-nr`.
#[cfg_attr(docsrs, doc(cfg(any(target_os = "linux", target_os = "android"))))]
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct FileNr {
/// Number of allocated file descriptors.
pub allocated: u64,
/// Number of free file descriptors.
/// File descriptor usage statistics from `/proc/sys/fs/file-nr`.
///
/// **Note:** The "free" field in `/proc/sys/fs/file-nr` has been deprecated since Linux 2.6 and is always 0 in modern kernels.
#[cfg_attr(docsrs, doc(cfg(any(target_os = "linux", target_os = "android"))))]
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct FileNr {
/// Number of allocated file descriptors.
pub allocated: u64,
/// Number of free file descriptors.
///
/// **Deprecated:** This field in `/proc/sys/fs/file-nr` has been deprecated since Linux 2.6 and is always 0 in modern kernels.
/// It does not contain meaningful data.

Copilot uses AI. Check for mistakes.
///
/// This corresponds to `/proc/sys/fs/file-nr` and contains three values:
/// - `allocated`: Number of allocated file descriptors
/// - `free`: Number of free file descriptors
Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation states that "free" represents the number of free file descriptors, but this field has been deprecated since Linux 2.6 and is always 0 in modern kernels. Consider clarifying this in the documentation to avoid misleading users.

Copilot uses AI. Check for mistakes.
@Nugine
Copy link
Owner

Nugine commented Dec 14, 2025

@copilot apply changes based on the comments in this thread

…precated fields

Co-authored-by: Nugine <30099658+Nugine@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Dec 14, 2025

@copilot apply changes based on the comments in this thread

Applied all changes from the review in 12c30eb:

  • Updated documentation to clarify "whitespace-separated" instead of "tab-separated"
  • Fixed test file naming to use unique names (thread ID) to prevent race conditions in parallel execution
  • Added comprehensive deprecation notes for the "free" field in FileNr (deprecated since Linux 2.6, always 0)
  • Improved code consistency in test assertions

@Nugine Nugine merged commit 2af2ec5 into main Dec 14, 2025
70 checks passed
@Nugine Nugine deleted the copilot/fix-file-max-settings branch December 14, 2025 19:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

/proc/sys/fs/file-max

3 participants