| 1 |
<?php
|
| 2 |
// $Id: provision.path.inc,v 1.27 2009/08/26 01:18:38 anarcat Exp $
|
| 3 |
/**
|
| 4 |
* @defgroup pathhandling Managing paths, permissions and file ownership
|
| 5 |
*
|
| 6 |
* This group provides an interface to common path handling operations, through
|
| 7 |
* the provision_path helper function, which will take care of verification and
|
| 8 |
* any error logging required.
|
| 9 |
*/
|
| 10 |
|
| 11 |
/**
|
| 12 |
* Perform tasks on a path.
|
| 13 |
*
|
| 14 |
* Perform tasks on a path, and logs error messages / codes on success or failure.
|
| 15 |
* This function will call another function which defines the functionality,
|
| 16 |
* and exists to provide a consistent interface for file operations with error logging
|
| 17 |
* integration.
|
| 18 |
*
|
| 19 |
* Many of the provision_path_$op functions are really simple, but are wrapped
|
| 20 |
* in functions to provide a consistent interface for provision_path to operate
|
| 21 |
* with.
|
| 22 |
*
|
| 23 |
* @param type
|
| 24 |
* The type of operation to perform. One of the following:
|
| 25 |
* writable - The $path can be written to.
|
| 26 |
* exists - The $path exists.
|
| 27 |
* is_dir - The $path is a directory.
|
| 28 |
* readable - The $path is readable.
|
| 29 |
* owner - The $path belongs to the user in $confirm.
|
| 30 |
* group - The $path belongs to the group in $confirm.
|
| 31 |
* mkdir - Create the $path directory.
|
| 32 |
* unlink - Delete the file $path.
|
| 33 |
* symlink - Create a symlink from $path to $confirm.
|
| 34 |
* rmdir - Delete the directory $path.
|
| 35 |
* chmod - Change the file permissions of $path to the octal value in $confirm.
|
| 36 |
* chown - Change the owner of $path to the user in $confirm.
|
| 37 |
* chgrp - Change the group of $path to the group in $confirm.
|
| 38 |
* switch_paths - Move $path to $confirm, and vice versa.
|
| 39 |
*
|
| 40 |
* @param path
|
| 41 |
* The path you want to perform the file operation on.
|
| 42 |
*
|
| 43 |
* @param confirm
|
| 44 |
* Confirm that the final value of the file operation matches this value.
|
| 45 |
* This value defaults to TRUE, which is sufficient for most file operations.
|
| 46 |
*
|
| 47 |
* Certain tasks such as chmod, chown and chgp will attempt to change the
|
| 48 |
* properties of $path to match the value in $confirm, and then test that
|
| 49 |
* the change was completed succesfully afterwards.
|
| 50 |
*
|
| 51 |
* These exceptions are :
|
| 52 |
* symlink - $confirm is the path to the symlink being created
|
| 53 |
* chmod - $confirm is an octal value denoting the desired file permissions.
|
| 54 |
* chown - $confirm is the name or user id you wish to change the file ownership to.
|
| 55 |
* chgrp - $confirm is the name of group id you wish to change the file group ownership to.
|
| 56 |
* switch_paths - $confirm is the path that you want to replace the $path with.
|
| 57 |
*
|
| 58 |
* @param succeed_message
|
| 59 |
* Log this as a notice into the logging system, if the operation completed succesfully.
|
| 60 |
*
|
| 61 |
* @param fail_message
|
| 62 |
* Log this as a error to the logging system, if the $error_codes parameter has been set,
|
| 63 |
* otherwise, log this as a warning. If the operation specifies an additional reason for
|
| 64 |
* the operation failing, it will be appended to this message.
|
| 65 |
*
|
| 66 |
* @param error_codes
|
| 67 |
* Generate these system level errors using the provision error bitmasks.
|
| 68 |
*
|
| 69 |
* @return
|
| 70 |
* Returns TRUE if the test against $confirm passed, otherwise returns FALSE.
|
| 71 |
*/
|
| 72 |
function provision_path($op, $path, $confirm = TRUE, $succeed_message = NULL, $fail_message = NULL, $error_codes = NULL) {
|
| 73 |
# The code style is a bit weird here, but it's a bit easier to read this way.
|
| 74 |
$func = "provision_path_" . $op;
|
| 75 |
if (function_exists($func)) {
|
| 76 |
// The reason variable is passed into the operation function, to allow the function
|
| 77 |
// to specify an additional reason as to why the operation failed.
|
| 78 |
$reason = '';
|
| 79 |
|
| 80 |
$value = $func($path, $confirm, $reason);
|
| 81 |
|
| 82 |
clearstatcache(); // this needs to be called, otherwise we get the old info
|
| 83 |
$tokens = array("@path" => $path, "@op" => $op, "@confirm" => $confirm);
|
| 84 |
if ($reason) {
|
| 85 |
$fail_message = $fail_message . " (" . $reason . ")";
|
| 86 |
}
|
| 87 |
$status = ($value == $confirm);
|
| 88 |
if ($status) {
|
| 89 |
if (!is_null($succeed_message)) {
|
| 90 |
drush_log(dt($succeed_message, $tokens), 'message');
|
| 91 |
}
|
| 92 |
}
|
| 93 |
else {
|
| 94 |
if ($error_codes) {
|
| 95 |
// Trigger a sysem halting error
|
| 96 |
if (!is_null($fail_message)) {
|
| 97 |
drush_set_error($error_codes, dt($fail_message, $tokens));
|
| 98 |
}
|
| 99 |
else {
|
| 100 |
drush_set_error($error_codes);
|
| 101 |
}
|
| 102 |
}
|
| 103 |
else {
|
| 104 |
// Trigger a warning
|
| 105 |
if (!is_null($fail_message)) {
|
| 106 |
drush_log(dt($fail_message, $tokens), 'warning');
|
| 107 |
}
|
| 108 |
}
|
| 109 |
}
|
| 110 |
return $status;
|
| 111 |
}
|
| 112 |
}
|
| 113 |
|
| 114 |
|
| 115 |
function provision_path_writable($path) {
|
| 116 |
return is_writable($path);
|
| 117 |
}
|
| 118 |
|
| 119 |
function provision_path_exists($path) {
|
| 120 |
return file_exists($path);
|
| 121 |
}
|
| 122 |
|
| 123 |
function provision_path_is_dir($path) {
|
| 124 |
return is_dir($path);
|
| 125 |
}
|
| 126 |
|
| 127 |
function provision_path_readable($path) {
|
| 128 |
return is_readable($path);
|
| 129 |
}
|
| 130 |
|
| 131 |
function provision_path_owner($path) {
|
| 132 |
$info = posix_getpwuid(fileowner($path));
|
| 133 |
return $info['name'];
|
| 134 |
}
|
| 135 |
|
| 136 |
function provision_path_group($path) {
|
| 137 |
return filegroup($path);
|
| 138 |
}
|
| 139 |
|
| 140 |
function provision_path_mkdir($path) {
|
| 141 |
if (version_compare(PHP_VERSION, '5.0.0', '<')) {
|
| 142 |
return _provision_mkdir_recursive($path, 0770);
|
| 143 |
} else {
|
| 144 |
return mkdir($path, 0770, TRUE);
|
| 145 |
}
|
| 146 |
}
|
| 147 |
|
| 148 |
function provision_path_rmdir($path) {
|
| 149 |
return (file_exists($path) && is_dir($path)) ? rmdir($path) : false;
|
| 150 |
}
|
| 151 |
|
| 152 |
function provision_path_unlink($path) {
|
| 153 |
return (file_exists($path)) ? unlink($path) : false;
|
| 154 |
}
|
| 155 |
|
| 156 |
|
| 157 |
/*
|
| 158 |
* This is where the more complex file operations start
|
| 159 |
*/
|
| 160 |
|
| 161 |
function provision_path_chmod($path, &$perms, &$reason, $recursive = FALSE) {
|
| 162 |
$func = ($recursive) ? '_provision_chmod_recursive' : 'chmod';
|
| 163 |
|
| 164 |
if (!@$func($path, $perms)) {
|
| 165 |
$reason = dt('chmod to @perm failed on @path', array('@perm' => sprintf('%o', $perms), '@path' => $path));
|
| 166 |
return false;
|
| 167 |
}
|
| 168 |
clearstatcache(); // this needs to be called, otherwise we get the old info
|
| 169 |
$value = substr(sprintf('%o', fileperms($path)), -4);
|
| 170 |
$perms = sprintf('%04o', $perms);
|
| 171 |
return $value;
|
| 172 |
}
|
| 173 |
|
| 174 |
function provision_path_chown($path, &$owner, &$reason, $recursive = FALSE) {
|
| 175 |
$func = ($recursive) ? '_provision_chown_recursive' : 'chown';
|
| 176 |
if ($owner = provision_posix_username($owner)) {
|
| 177 |
if (!$func($path, $owner)) {
|
| 178 |
$reason = dt("chown to @owner failed on @path", array('@owner' => $owner, '@path' => $path)) ;
|
| 179 |
}
|
| 180 |
}
|
| 181 |
else {
|
| 182 |
$reason = dt("the user does not exist");
|
| 183 |
}
|
| 184 |
|
| 185 |
clearstatcache(); // this needs to be called, otherwise we get the old info
|
| 186 |
return provision_posix_username(fileowner($path));
|
| 187 |
}
|
| 188 |
|
| 189 |
function provision_path_chgrp($path, &$gid, &$reason, $recursive = FALSE) {
|
| 190 |
$func = ($recursive) ? '_provision_chgrp_recursive' : 'chgrp';
|
| 191 |
if ($group = provision_posix_groupname($gid)) {
|
| 192 |
if (provision_user_in_group(drush_get_option('script_user'), $gid)) {
|
| 193 |
if ($func($path, $group)) {
|
| 194 |
return $group;
|
| 195 |
}
|
| 196 |
else {
|
| 197 |
$reason = dt("chgrp to @group failed on @path", array('@group' => $group, '@path' => $path));
|
| 198 |
}
|
| 199 |
}
|
| 200 |
else {
|
| 201 |
$reason = dt("@user is not in @group group", array("@user" => drush_get_option('script_user'), "@group" => $group));
|
| 202 |
}
|
| 203 |
}
|
| 204 |
elseif (!@$func($path, $gid)) { # try to change the group anyways
|
| 205 |
$reason = dt("the group does not exist");
|
| 206 |
}
|
| 207 |
|
| 208 |
clearstatcache(); // this needs to be called, otherwise we get the old info
|
| 209 |
return provision_posix_groupname(filegroup($path));
|
| 210 |
}
|
| 211 |
|
| 212 |
|
| 213 |
function provision_path_chmod_recursive($path, &$perms, &$reason) {
|
| 214 |
return provision_path_chmod($path, $perms, $reason, TRUE);
|
| 215 |
}
|
| 216 |
|
| 217 |
function provision_path_chown_recursive($path, &$owner, &$reason) {
|
| 218 |
return provision_path_chown($path, $owner, $reason, TRUE);
|
| 219 |
}
|
| 220 |
|
| 221 |
function provision_path_chgrp_recursive($path, &$gid, &$reason) {
|
| 222 |
return provision_path_chgrp($path, $gid, $reason, TRUE);
|
| 223 |
}
|
| 224 |
|
| 225 |
|
| 226 |
function provision_path_switch_paths($path1, &$path2, &$reason) {
|
| 227 |
//TODO : Add error reasons.
|
| 228 |
$temp = $path1 .'.tmp';
|
| 229 |
if (!file_exists($path1)) {
|
| 230 |
return rename($path2, $path1);
|
| 231 |
}
|
| 232 |
elseif (!file_exists($path2)) {
|
| 233 |
return rename($path1, $path2);
|
| 234 |
}
|
| 235 |
elseif (rename($path1, $temp)) {
|
| 236 |
if (rename($path2, $path1)) {
|
| 237 |
if (rename($temp, $path2)) {
|
| 238 |
return $path2; // path1 is now path2
|
| 239 |
}
|
| 240 |
else {
|
| 241 |
// same .. just in reverse
|
| 242 |
return rename($path1, $path2) && rename($temp, $path1);
|
| 243 |
}
|
| 244 |
}
|
| 245 |
else {
|
| 246 |
// same .. just in reverse
|
| 247 |
return rename($temp, $path1);
|
| 248 |
}
|
| 249 |
|
| 250 |
}
|
| 251 |
return FALSE;
|
| 252 |
}
|
| 253 |
|
| 254 |
function provision_path_extract($path, &$target, &$reason) {
|
| 255 |
if (file_exists($path) && is_readable($path)) {
|
| 256 |
if (is_writeable(dirname($target)) && !file_exists($target) && !is_dir($target)) {
|
| 257 |
mkdir($target);
|
| 258 |
drush_log(sprintf("Running: tar -zpxf %s -C %s", $path, $target));
|
| 259 |
$result = provision_shell_exec("tar -zpxf %s -C %s", $path, $target);
|
| 260 |
|
| 261 |
if ($result && is_writeable(dirname($target)) && is_readable(dirname($target)) && is_dir($target)) {
|
| 262 |
$target = TRUE;
|
| 263 |
return TRUE;
|
| 264 |
}
|
| 265 |
else {
|
| 266 |
$reason = dt("The file could not be extracted");
|
| 267 |
}
|
| 268 |
}
|
| 269 |
else {
|
| 270 |
$reason = dt("The target directory could not be written to");
|
| 271 |
return false;
|
| 272 |
}
|
| 273 |
}
|
| 274 |
else {
|
| 275 |
$reason = dt("Backup file could not be opened");
|
| 276 |
return false;
|
| 277 |
}
|
| 278 |
|
| 279 |
}
|
| 280 |
|
| 281 |
function provision_path_symlink($path, &$target, &$reason) {
|
| 282 |
if (file_exists($target) && !is_link($target)) {
|
| 283 |
$reason = dt("A file already exists at @path");
|
| 284 |
return FALSE;
|
| 285 |
}
|
| 286 |
if (is_link($target) && (readlink($target) != $path)) {
|
| 287 |
$reason = dt("A symlink already exists at target, but it is pointing to @link", array("@link" => readlink($target)));
|
| 288 |
return FALSE;
|
| 289 |
}
|
| 290 |
if (is_link($target) && (readlink($target) == $path)) {
|
| 291 |
$target = TRUE;
|
| 292 |
return TRUE;
|
| 293 |
}
|
| 294 |
if (symlink($path, $target)) {
|
| 295 |
$target = TRUE;
|
| 296 |
return TRUE;
|
| 297 |
}
|
| 298 |
else {
|
| 299 |
$reason = dt('The symlink could not be created, an error has occured');
|
| 300 |
return FALSE;
|
| 301 |
}
|
| 302 |
|
| 303 |
|
| 304 |
}
|
| 305 |
|
| 306 |
/**
|
| 307 |
*@} end filegroup
|
| 308 |
*/
|
| 309 |
|
| 310 |
/**
|
| 311 |
* Small helper function for creation of configuration directories.
|
| 312 |
*/
|
| 313 |
function _provision_create_dir($path, $name, $perms) {
|
| 314 |
$exists = provision_path("exists",$path, TRUE ,
|
| 315 |
$name . ' ' . dt("path exists."),
|
| 316 |
$name . ' ' . dt("path does not exist.")
|
| 317 |
);
|
| 318 |
|
| 319 |
if (!$exists) {
|
| 320 |
$exists = provision_path("mkdir", $path, TRUE,
|
| 321 |
$name . ' ' . dt("path has been created."),
|
| 322 |
$name . ' ' . dt("path could not be created."),
|
| 323 |
'DRUSH_PERM_ERROR');
|
| 324 |
}
|
| 325 |
|
| 326 |
if ($exists) {
|
| 327 |
provision_path("chown", $path, drush_get_option('script_user'),
|
| 328 |
$name . ' ' . dt("ownership of path has been changed to @confirm."),
|
| 329 |
$name . ' ' . dt("ownership of path could not be changed to @confirm."),
|
| 330 |
'DRUSH_PERM_ERROR');
|
| 331 |
|
| 332 |
provision_path("chmod", $path, $perms,
|
| 333 |
$name . ' ' . dt("permissions of path have been changed to @confirm."),
|
| 334 |
$name . ' ' . dt("permissions of path could not be changed to @confirm."),
|
| 335 |
'DRUSH_PERM_ERROR' );
|
| 336 |
|
| 337 |
$writable = provision_path("writable", $path, TRUE,
|
| 338 |
$name . ' ' . dt("path is writable."),
|
| 339 |
$name . ' ' . dt("path is not writable."),
|
| 340 |
'DRUSH_PERM_ERROR');
|
| 341 |
|
| 342 |
}
|
| 343 |
|
| 344 |
return ($exists && $writable);
|
| 345 |
}
|
| 346 |
|
| 347 |
/**
|
| 348 |
* Makes directory recursively, returns TRUE if exists or made (for PHP4 compatibility)
|
| 349 |
* Code courtesy of: http://ca3.php.net/manual/en/function.mkdir.php#81656
|
| 350 |
*
|
| 351 |
* @param string $pathname The directory path.
|
| 352 |
* @return boolean returns TRUE if exists or made or FALSE on failure.
|
| 353 |
*/
|
| 354 |
function _provision_mkdir_recursive($path, $mode) {
|
| 355 |
is_dir(dirname($path)) || _provision_mkdir_recursive(dirname($path), $mode);
|
| 356 |
return is_dir($path) || mkdir($path, $mode);
|
| 357 |
}
|
| 358 |
|
| 359 |
/**
|
| 360 |
* Walk the given tree recursively (depth first), calling a function on each file
|
| 361 |
*
|
| 362 |
* $func is not checked for existence and called directly with $path and $arg
|
| 363 |
* for every file encountered.
|
| 364 |
*
|
| 365 |
* @param string $func a valid callback, usually chmod, chown or chgrp
|
| 366 |
* @param string $path a path in the filesystem
|
| 367 |
* @param string $arg the second argument to $func
|
| 368 |
* @return boolean returns TRUE if every $func call returns true
|
| 369 |
*/
|
| 370 |
function _provision_call_recursive($func, $path, $arg) {
|
| 371 |
$status = 1;
|
| 372 |
if ($dh = @opendir($path)) {
|
| 373 |
while (($file = readdir($dh)) !== false) {
|
| 374 |
if ($file != '.' && $file != '..') {
|
| 375 |
$status = _provision_call_recursive($func, $path . "/" . $file, $arg) && $status;
|
| 376 |
}
|
| 377 |
}
|
| 378 |
closedir($dh);
|
| 379 |
}
|
| 380 |
$status = $func($path, $arg) && $status;
|
| 381 |
return $status;
|
| 382 |
}
|
| 383 |
|
| 384 |
/**
|
| 385 |
* Chmod a directory recursively
|
| 386 |
*
|
| 387 |
*/
|
| 388 |
function _provision_chmod_recursive($path, $filemode) {
|
| 389 |
return _provision_call_recursive("chmod", $path, $filemode);
|
| 390 |
}
|
| 391 |
|
| 392 |
/**
|
| 393 |
* Chown a directory recursively
|
| 394 |
*/
|
| 395 |
function _provision_chown_recursive($path, $owner) {
|
| 396 |
return _provision_call_recursive("chown", $path, $owner);
|
| 397 |
}
|
| 398 |
|
| 399 |
/**
|
| 400 |
* Chgrp a directory recursively
|
| 401 |
*/
|
| 402 |
function _provision_chgrp_recursive($path, $owner) {
|
| 403 |
return _provision_call_recursive("chgrp", $path, $owner);
|
| 404 |
}
|