umask() 函数用于设置或获取当前进程的文件权限掩码(umask)。权限掩码用于控制在创建新文件或目录时应用的默认权限。
umask 是一个八进制数,表示要从默认权限中"减去"的权限。它通过屏蔽(mask)某些权限位来工作,确保新创建的文件和目录具有安全的默认权限。
umask ([ int $mask ] ) : int
参数说明:
$mask:新的权限掩码(可选参数)| 参数 | 描述 |
|---|---|
mask |
新的权限掩码。可选参数。
|
umask 通过从默认权限中屏蔽某些位来工作:
| 项目 | 默认权限 | 实际权限计算 |
|---|---|---|
| 普通文件 | 0666 (rw-rw-rw-) | 0666 & ~umask |
| 目录 | 0777 (rwxrwxrwx) | 0777 & ~umask |
八进制权限表示:
0 = --- (无权限)
1 = --x (执行)
2 = -w- (写)
3 = -wx (写+执行)
4 = r-- (读)
5 = r-x (读+执行)
6 = rw- (读+写)
7 = rwx (读+写+执行)
三位数分别表示:
第一位: 所有者权限
第二位: 组权限
第三位: 其他用户权限
例如:
755 = rwxr-xr-x
644 = rw-r--r--
755 = 所有者:读+写+执行, 组:读+执行, 其他:读+执行
<?php
// 示例: umask 022 时的权限计算
// 默认文件权限: 0666 (rw-rw-rw-)
// 默认目录权限: 0777 (rwxrwxrwx)
// umask 022 (八进制) = 二进制 000010010
// ~022 = 二进制 111101101 (八进制 755)
// 文件实际权限: 0666 & ~022 = 0644 (rw-r--r--)
// 目录实际权限: 0777 & ~022 = 0755 (rwxr-xr-x)
echo "umask 022 时:<br>";
echo "新文件权限: 0644 (rw-r--r--)<br>";
echo "新目录权限: 0755 (rwxr-xr-x)<br>";
// 另一个示例: umask 077
// ~077 = 0700
// 文件实际权限: 0666 & ~077 = 0600 (rw-------)
// 目录实际权限: 0777 & ~077 = 0700 (rwx------)
?>
查看当前进程的 umask 值:
<?php
// 获取当前 umask
$current_umask = umask();
// 以八进制显示
echo "当前 umask: " . sprintf("%04o", $current_umask) . "<br>";
echo "十进制值: " . $current_umask . "<br>";
// 解释umask的含义
function explain_umask($umask) {
$file_perm = 0666 & ~$umask;
$dir_perm = 0777 & ~$umask;
echo "umask " . sprintf("%03o", $umask) . " 意味着:<br>";
echo "新文件权限: " . sprintf("%03o", $file_perm) . " (" . decoct($file_perm) . ")<br>";
echo "新目录权限: " . sprintf("%03o", $dir_perm) . " (" . decoct($dir_perm) . ")<br>";
}
explain_umask($current_umask);
?>
修改 umask 并观察效果:
<?php
// 保存旧的 umask
$old_umask = umask();
echo "旧的 umask: " . sprintf("%04o", $old_umask) . "<br>";
// 设置新的 umask 为 027 (更严格的权限)
$previous_umask = umask(027);
echo "设置时的 previous umask: " . sprintf("%04o", $previous_umask) . "<br>";
echo "新的 umask: " . sprintf("%04o", umask()) . "<br>";
// 创建文件和目录来验证
$test_file = "test_umask_file.txt";
$test_dir = "test_umask_dir";
// 创建文件
touch($test_file);
// 创建目录
mkdir($test_dir);
// 获取实际权限
$file_perms = fileperms($test_file);
$dir_perms = fileperms($test_dir);
// 显示权限(去掉前导的0)
echo "<br>创建的文件权限: " . substr(sprintf('%o', $file_perms), -4) . "<br>";
echo "创建的目录权限: " . substr(sprintf('%o', $dir_perms), -4) . "<br>";
// 清理
unlink($test_file);
rmdir($test_dir);
// 恢复原来的 umask
umask($old_umask);
echo "<br>恢复后的 umask: " . sprintf("%04o", umask());
?>
比较不同 umask 值对权限的影响:
<?php
function test_umask_values() {
$umask_values = [0, 022, 027, 077, 002];
$results = [];
foreach ($umask_values as $umask) {
// 设置 umask
$old_umask = umask($umask);
// 计算预期的权限
$expected_file = 0666 & ~$umask;
$expected_dir = 0777 & ~$umask;
// 创建测试文件
$test_file = "test_file_" . $umask . ".txt";
$test_dir = "test_dir_" . $umask;
touch($test_file);
mkdir($test_dir);
// 获取实际权限
$actual_file = fileperms($test_file) & 0777;
$actual_dir = fileperms($test_dir) & 0777;
// 清理
unlink($test_file);
rmdir($test_dir);
// 恢复原来的 umask
umask($old_umask);
$results[] = [
'umask' => sprintf("%03o", $umask),
'expected_file' => sprintf("%03o", $expected_file),
'actual_file' => sprintf("%03o", $actual_file),
'expected_dir' => sprintf("%03o", $expected_dir),
'actual_dir' => sprintf("%03o", $actual_dir),
'file_match' => ($expected_file === $actual_file),
'dir_match' => ($expected_dir === $actual_dir)
];
}
return $results;
}
echo "不同 umask 值的效果比较:<br><br>";
$results = test_umask_values();
echo "<table border='1' cellpadding='5'>";
echo "<tr><th>umask</th><th>预期文件权限</th><th>实际文件权限</th><th>预期目录权限</th><th>实际目录权限</th><th>文件匹配</th><th>目录匹配</th></tr>";
foreach ($results as $row) {
echo "<tr>";
echo "<td>" . $row['umask'] . "</td>";
echo "<td>" . $row['expected_file'] . "</td>";
echo "<td>" . $row['actual_file'] . "</td>";
echo "<td>" . $row['expected_dir'] . "</td>";
echo "<td>" . $row['actual_dir'] . "</td>";
echo "<td>" . ($row['file_match'] ? '✓' : '✗') . "</td>";
echo "<td>" . ($row['dir_match'] ? '✓' : '✗') . "</td>";
echo "</tr>";
}
echo "</table>";
?>
使用 umask 确保安全地创建文件:
<?php
class SecureFileCreator {
private $original_umask;
public function __construct() {
// 保存当前的 umask
$this->original_umask = umask();
}
public function createSecureFile($filename, $content = '', $directory = '.') {
// 设置安全的 umask (只允许所有者读写)
umask(077);
$full_path = $directory . '/' . $filename;
try {
// 创建文件
if (file_put_contents($full_path, $content) === false) {
throw new Exception("无法创建文件: $filename");
}
// 验证权限
$perms = fileperms($full_path) & 0777;
$expected_perms = 0600; // rw-------
if ($perms !== $expected_perms) {
// 如果权限不正确,尝试修复
chmod($full_path, $expected_perms);
$perms = fileperms($full_path) & 0777;
if ($perms !== $expected_perms) {
throw new Exception("无法设置正确的文件权限");
}
}
echo "安全创建文件: $full_path<br>";
echo "文件权限: " . substr(sprintf('%o', $perms), -4) . "<br>";
return $full_path;
} catch (Exception $e) {
// 清理
if (file_exists($full_path)) {
unlink($full_path);
}
throw $e;
} finally {
// 恢复原来的 umask
umask($this->original_umask);
}
}
public function __destruct() {
// 确保恢复 umask
umask($this->original_umask);
}
}
// 使用示例
try {
$creator = new SecureFileCreator();
$file = $creator->createSecureFile('secret_data.txt', '这是敏感数据');
// 检查文件权限
$perms = fileperms($file) & 0777;
echo "最终文件权限: " . substr(sprintf('%o', $perms), -4) . "<br>";
// 清理
unlink($file);
} catch (Exception $e) {
echo "错误: " . $e->getMessage();
}
?>
使用临时修改 umask 的模式:
<?php
// 使用 withUmask 模式临时修改 umask
function withUmask($new_umask, $callback) {
$old_umask = umask();
try {
// 设置新的 umask
umask($new_umask);
// 执行回调函数
return $callback();
} finally {
// 恢复原来的 umask
umask($old_umask);
}
}
// 使用示例
$result = withUmask(027, function() {
// 在这个函数中,umask 是 027
$files = ['file1.txt', 'file2.txt', 'file3.txt'];
$created = [];
foreach ($files as $file) {
touch($file);
$perms = fileperms($file) & 0777;
echo "创建 $file, 权限: " . substr(sprintf('%o', $perms), -4) . "<br>";
$created[] = $file;
}
// 返回创建的文件列表
return $created;
});
echo "创建的文件数量: " . count($result) . "<br>";
// 验证 umask 已恢复
$current_umask = umask();
echo "恢复后的 umask: " . sprintf("%04o", $current_umask) . "<br>";
// 清理文件
foreach ($result as $file) {
if (file_exists($file)) {
unlink($file);
}
}
?>
| umask 值 | 文件权限 | 目录权限 | 描述和使用场景 |
|---|---|---|---|
| 022 | 644 (rw-r--r--) | 755 (rwxr-xr-x) | 最常用的设置。所有者可读写,其他用户只读。适用于大多数Web应用。 |
| 027 | 640 (rw-r-----) | 750 (rwxr-x---) | 更安全。所有者可读写,组用户只读,其他用户无权限。适用于共享主机。 |
| 077 | 600 (rw-------) | 700 (rwx------) | 最严格。只有所有者有权限。适用于敏感数据、配置文件。 |
| 002 | 664 (rw-rw-r--) | 775 (rwxrwxr-x) | 宽松的设置。所有者和组用户可读写。适用于开发环境或协作项目。 |
| 000 | 666 (rw-rw-rw-) | 777 (rwxrwxrwx) | 最宽松。所有用户都有完全权限。一般不推荐使用。 |
| 033 | 644 (rw-r--r--) | 744 (rwxr--r--) | 特殊设置。文件与022相同,但目录的组和其他用户没有执行权限。 |
<?php
// 1. 总是保存和恢复原来的 umask
function safeUmaskOperation() {
$old_umask = umask(); // 保存原来的 umask
try {
umask(027); // 设置新的 umask
// 执行文件操作
touch('secure_file.txt');
} finally {
umask($old_umask); // 恢复原来的 umask
}
}
// 2. 在类的构造和析构函数中管理 umask
class SecureFileHandler {
private $original_umask;
public function __construct() {
$this->original_umask = umask();
umask(077); // 设置严格的 umask
}
public function __destruct() {
umask($this->original_umask); // 恢复原来的 umask
}
}
// 3. 验证 umask 设置
function verifyUmask($expected_umask) {
$actual_umask = umask();
umask($actual_umask); // 重新设置以获取当前值
if ($actual_umask !== $expected_umask) {
throw new RuntimeException("umask 设置不正确");
}
}
// 4. 使用适合场景的 umask 值
function getRecommendedUmask() {
if (PHP_SAPI === 'cli') {
return 022; // 命令行环境
} else {
return 027; // Web 环境
}
}
?>
<?php
class SecureUploadHandler {
private $uploadDir;
public function __construct($uploadDir) {
$this->uploadDir = $uploadDir;
// 确保上传目录存在且安全
if (!is_dir($uploadDir)) {
// 使用安全的 umask 创建目录
$old_umask = umask(077);
mkdir($uploadDir, 0755, true);
umask($old_umask);
}
}
public function handleUpload($fileData) {
// 生成安全的文件名
$filename = $this->generateSafeFilename($fileData['name']);
$filepath = $this->uploadDir . '/' . $filename;
// 使用安全的 umask 创建文件
$old_umask = umask(077);
try {
// 移动上传的文件
if (move_uploaded_file($fileData['tmp_name'], $filepath)) {
// 设置适当的权限(根据文件类型)
$this->setFilePermissions($filepath, $fileData['type']);
return [
'success' => true,
'filename' => $filename,
'path' => $filepath,
'permissions' => substr(sprintf('%o', fileperms($filepath)), -4)
];
} else {
return ['success' => false, 'error' => '文件移动失败'];
}
} finally {
// 恢复原来的 umask
umask($old_umask);
}
}
private function generateSafeFilename($originalName) {
// 生成安全的文件名(防止路径遍历等攻击)
$ext = pathinfo($originalName, PATHINFO_EXTENSION);
return uniqid('upload_', true) . '.' . $ext;
}
private function setFilePermissions($filepath, $mimeType) {
// 根据文件类型设置不同的权限
if (strpos($mimeType, 'image/') === 0) {
// 图片文件:所有者读写,其他用户只读
chmod($filepath, 0644);
} else {
// 其他文件:更严格的权限
chmod($filepath, 0600);
}
}
}
// 使用示例
$uploadHandler = new SecureUploadHandler('/var/www/uploads');
// 处理上传...
?>
<?php
class TempFileGenerator {
private $tempDir;
private $defaultUmask;
public function __construct($tempDir = null) {
$this->tempDir = $tempDir ?: sys_get_temp_dir();
$this->defaultUmask = umask();
}
public function createTempFile($content = '', $secure = true) {
// 生成唯一文件名
$filename = uniqid('temp_', true) . '.tmp';
$filepath = $this->tempDir . '/' . $filename;
// 根据安全需求设置 umask
$old_umask = umask($secure ? 077 : 022);
try {
// 创建文件
if (file_put_contents($filepath, $content) === false) {
throw new RuntimeException("无法创建临时文件");
}
return [
'path' => $filepath,
'name' => $filename,
'secure' => $secure,
'permissions' => substr(sprintf('%o', fileperms($filepath)), -4)
];
} finally {
// 恢复原来的 umask
umask($old_umask);
}
}
public function cleanupOldFiles($maxAge = 3600) {
// 清理过期的临时文件
$files = glob($this->tempDir . '/temp_*.tmp');
$cleaned = 0;
foreach ($files as $file) {
if (filemtime($file) < time() - $maxAge) {
unlink($file);
$cleaned++;
}
}
return $cleaned;
}
}
// 使用示例
$generator = new TempFileGenerator();
// 创建安全的临时文件(只有所有者可读写)
$secureFile = $generator->createTempFile('敏感数据', true);
echo "安全文件: " . $secureFile['path'] . " (权限: " . $secureFile['permissions'] . ")<br>";
// 创建普通的临时文件
$normalFile = $generator->createTempFile('普通数据', false);
echo "普通文件: " . $normalFile['path'] . " (权限: " . $normalFile['permissions'] . ")<br>";
// 清理
unlink($secureFile['path']);
unlink($normalFile['path']);
?>