annotate core/lib/Drupal/Core/StreamWrapper/LocalStream.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents af1871eacc83
children
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 namespace Drupal\Core\StreamWrapper;
Chris@0 4
Chris@0 5 /**
Chris@0 6 * Defines a Drupal stream wrapper base class for local files.
Chris@0 7 *
Chris@0 8 * This class provides a complete stream wrapper implementation. URIs such as
Chris@0 9 * "public://example.txt" are expanded to a normal filesystem path such as
Chris@0 10 * "sites/default/files/example.txt" and then PHP filesystem functions are
Chris@0 11 * invoked.
Chris@0 12 *
Chris@0 13 * Drupal\Core\StreamWrapper\LocalStream implementations need to implement at least the
Chris@0 14 * getDirectoryPath() and getExternalUrl() methods.
Chris@0 15 */
Chris@0 16 abstract class LocalStream implements StreamWrapperInterface {
Chris@0 17 /**
Chris@0 18 * Stream context resource.
Chris@0 19 *
Chris@0 20 * @var resource
Chris@0 21 */
Chris@0 22 public $context;
Chris@0 23
Chris@0 24 /**
Chris@0 25 * A generic resource handle.
Chris@0 26 *
Chris@0 27 * @var resource
Chris@0 28 */
Chris@0 29 public $handle = NULL;
Chris@0 30
Chris@0 31 /**
Chris@0 32 * Instance URI (stream).
Chris@0 33 *
Chris@0 34 * A stream is referenced as "scheme://target".
Chris@0 35 *
Chris@0 36 * @var string
Chris@0 37 */
Chris@0 38 protected $uri;
Chris@0 39
Chris@0 40 /**
Chris@0 41 * {@inheritdoc}
Chris@0 42 */
Chris@0 43 public static function getType() {
Chris@0 44 return StreamWrapperInterface::NORMAL;
Chris@0 45 }
Chris@0 46
Chris@0 47 /**
Chris@0 48 * Gets the path that the wrapper is responsible for.
Chris@0 49 *
Chris@0 50 * @todo Review this method name in D8 per https://www.drupal.org/node/701358.
Chris@0 51 *
Chris@0 52 * @return string
Chris@0 53 * String specifying the path.
Chris@0 54 */
Chris@0 55 abstract public function getDirectoryPath();
Chris@0 56
Chris@0 57 /**
Chris@0 58 * {@inheritdoc}
Chris@0 59 */
Chris@0 60 public function setUri($uri) {
Chris@0 61 $this->uri = $uri;
Chris@0 62 }
Chris@0 63
Chris@0 64 /**
Chris@0 65 * {@inheritdoc}
Chris@0 66 */
Chris@0 67 public function getUri() {
Chris@0 68 return $this->uri;
Chris@0 69 }
Chris@0 70
Chris@0 71 /**
Chris@0 72 * Returns the local writable target of the resource within the stream.
Chris@0 73 *
Chris@0 74 * This function should be used in place of calls to realpath() or similar
Chris@0 75 * functions when attempting to determine the location of a file. While
Chris@0 76 * functions like realpath() may return the location of a read-only file, this
Chris@0 77 * method may return a URI or path suitable for writing that is completely
Chris@0 78 * separate from the URI used for reading.
Chris@0 79 *
Chris@0 80 * @param string $uri
Chris@0 81 * Optional URI.
Chris@0 82 *
Chris@0 83 * @return string|bool
Chris@0 84 * Returns a string representing a location suitable for writing of a file,
Chris@0 85 * or FALSE if unable to write to the file such as with read-only streams.
Chris@0 86 */
Chris@0 87 protected function getTarget($uri = NULL) {
Chris@0 88 if (!isset($uri)) {
Chris@0 89 $uri = $this->uri;
Chris@0 90 }
Chris@0 91
Chris@0 92 list(, $target) = explode('://', $uri, 2);
Chris@0 93
Chris@0 94 // Remove erroneous leading or trailing, forward-slashes and backslashes.
Chris@0 95 return trim($target, '\/');
Chris@0 96 }
Chris@0 97
Chris@0 98 /**
Chris@0 99 * {@inheritdoc}
Chris@0 100 */
Chris@0 101 public function realpath() {
Chris@0 102 return $this->getLocalPath();
Chris@0 103 }
Chris@0 104
Chris@0 105 /**
Chris@0 106 * Returns the canonical absolute path of the URI, if possible.
Chris@0 107 *
Chris@0 108 * @param string $uri
Chris@0 109 * (optional) The stream wrapper URI to be converted to a canonical
Chris@0 110 * absolute path. This may point to a directory or another type of file.
Chris@0 111 *
Chris@0 112 * @return string|bool
Chris@0 113 * If $uri is not set, returns the canonical absolute path of the URI
Chris@0 114 * previously set by the
Chris@0 115 * Drupal\Core\StreamWrapper\StreamWrapperInterface::setUri() function.
Chris@0 116 * If $uri is set and valid for this class, returns its canonical absolute
Chris@0 117 * path, as determined by the realpath() function. If $uri is set but not
Chris@0 118 * valid, returns FALSE.
Chris@0 119 */
Chris@0 120 protected function getLocalPath($uri = NULL) {
Chris@0 121 if (!isset($uri)) {
Chris@0 122 $uri = $this->uri;
Chris@0 123 }
Chris@0 124 $path = $this->getDirectoryPath() . '/' . $this->getTarget($uri);
Chris@0 125
Chris@0 126 // In PHPUnit tests, the base path for local streams may be a virtual
Chris@0 127 // filesystem stream wrapper URI, in which case this local stream acts like
Chris@0 128 // a proxy. realpath() is not supported by vfsStream, because a virtual
Chris@0 129 // file system does not have a real filepath.
Chris@0 130 if (strpos($path, 'vfs://') === 0) {
Chris@0 131 return $path;
Chris@0 132 }
Chris@0 133
Chris@0 134 $realpath = realpath($path);
Chris@0 135 if (!$realpath) {
Chris@0 136 // This file does not yet exist.
Chris@18 137 $realpath = realpath(dirname($path)) . '/' . \Drupal::service('file_system')->basename($path);
Chris@0 138 }
Chris@0 139 $directory = realpath($this->getDirectoryPath());
Chris@0 140 if (!$realpath || !$directory || strpos($realpath, $directory) !== 0) {
Chris@0 141 return FALSE;
Chris@0 142 }
Chris@0 143 return $realpath;
Chris@0 144 }
Chris@0 145
Chris@0 146 /**
Chris@0 147 * Support for fopen(), file_get_contents(), file_put_contents() etc.
Chris@0 148 *
Chris@0 149 * @param string $uri
Chris@0 150 * A string containing the URI to the file to open.
Chris@0 151 * @param int $mode
Chris@0 152 * The file mode ("r", "wb" etc.).
Chris@0 153 * @param int $options
Chris@0 154 * A bit mask of STREAM_USE_PATH and STREAM_REPORT_ERRORS.
Chris@0 155 * @param string $opened_path
Chris@0 156 * A string containing the path actually opened.
Chris@0 157 *
Chris@0 158 * @return bool
Chris@0 159 * Returns TRUE if file was opened successfully.
Chris@0 160 *
Chris@0 161 * @see http://php.net/manual/streamwrapper.stream-open.php
Chris@0 162 */
Chris@0 163 public function stream_open($uri, $mode, $options, &$opened_path) {
Chris@0 164 $this->uri = $uri;
Chris@0 165 $path = $this->getLocalPath();
Chris@0 166 $this->handle = ($options & STREAM_REPORT_ERRORS) ? fopen($path, $mode) : @fopen($path, $mode);
Chris@0 167
Chris@0 168 if ((bool) $this->handle && $options & STREAM_USE_PATH) {
Chris@0 169 $opened_path = $path;
Chris@0 170 }
Chris@0 171
Chris@0 172 return (bool) $this->handle;
Chris@0 173 }
Chris@0 174
Chris@0 175 /**
Chris@0 176 * Support for flock().
Chris@0 177 *
Chris@0 178 * @param int $operation
Chris@0 179 * One of the following:
Chris@0 180 * - LOCK_SH to acquire a shared lock (reader).
Chris@0 181 * - LOCK_EX to acquire an exclusive lock (writer).
Chris@0 182 * - LOCK_UN to release a lock (shared or exclusive).
Chris@0 183 * - LOCK_NB if you don't want flock() to block while locking (not
Chris@0 184 * supported on Windows).
Chris@0 185 *
Chris@0 186 * @return bool
Chris@0 187 * Always returns TRUE at the present time.
Chris@0 188 *
Chris@0 189 * @see http://php.net/manual/streamwrapper.stream-lock.php
Chris@0 190 */
Chris@0 191 public function stream_lock($operation) {
Chris@0 192 if (in_array($operation, [LOCK_SH, LOCK_EX, LOCK_UN, LOCK_NB])) {
Chris@0 193 return flock($this->handle, $operation);
Chris@0 194 }
Chris@0 195
Chris@0 196 return TRUE;
Chris@0 197 }
Chris@0 198
Chris@0 199 /**
Chris@0 200 * Support for fread(), file_get_contents() etc.
Chris@0 201 *
Chris@0 202 * @param int $count
Chris@0 203 * Maximum number of bytes to be read.
Chris@0 204 *
Chris@0 205 * @return string|bool
Chris@0 206 * The string that was read, or FALSE in case of an error.
Chris@0 207 *
Chris@0 208 * @see http://php.net/manual/streamwrapper.stream-read.php
Chris@0 209 */
Chris@0 210 public function stream_read($count) {
Chris@0 211 return fread($this->handle, $count);
Chris@0 212 }
Chris@0 213
Chris@0 214 /**
Chris@0 215 * Support for fwrite(), file_put_contents() etc.
Chris@0 216 *
Chris@0 217 * @param string $data
Chris@0 218 * The string to be written.
Chris@0 219 *
Chris@0 220 * @return int
Chris@0 221 * The number of bytes written.
Chris@0 222 *
Chris@0 223 * @see http://php.net/manual/streamwrapper.stream-write.php
Chris@0 224 */
Chris@0 225 public function stream_write($data) {
Chris@0 226 return fwrite($this->handle, $data);
Chris@0 227 }
Chris@0 228
Chris@0 229 /**
Chris@0 230 * Support for feof().
Chris@0 231 *
Chris@0 232 * @return bool
Chris@0 233 * TRUE if end-of-file has been reached.
Chris@0 234 *
Chris@0 235 * @see http://php.net/manual/streamwrapper.stream-eof.php
Chris@0 236 */
Chris@0 237 public function stream_eof() {
Chris@0 238 return feof($this->handle);
Chris@0 239 }
Chris@0 240
Chris@0 241 /**
Chris@0 242 * {@inheritdoc}
Chris@0 243 */
Chris@0 244 public function stream_seek($offset, $whence = SEEK_SET) {
Chris@0 245 // fseek returns 0 on success and -1 on a failure.
Chris@0 246 // stream_seek 1 on success and 0 on a failure.
Chris@0 247 return !fseek($this->handle, $offset, $whence);
Chris@0 248 }
Chris@0 249
Chris@0 250 /**
Chris@0 251 * Support for fflush().
Chris@0 252 *
Chris@0 253 * @return bool
Chris@0 254 * TRUE if data was successfully stored (or there was no data to store).
Chris@0 255 *
Chris@0 256 * @see http://php.net/manual/streamwrapper.stream-flush.php
Chris@0 257 */
Chris@0 258 public function stream_flush() {
Chris@0 259 return fflush($this->handle);
Chris@0 260 }
Chris@0 261
Chris@0 262 /**
Chris@0 263 * Support for ftell().
Chris@0 264 *
Chris@0 265 * @return bool
Chris@0 266 * The current offset in bytes from the beginning of file.
Chris@0 267 *
Chris@0 268 * @see http://php.net/manual/streamwrapper.stream-tell.php
Chris@0 269 */
Chris@0 270 public function stream_tell() {
Chris@0 271 return ftell($this->handle);
Chris@0 272 }
Chris@0 273
Chris@0 274 /**
Chris@0 275 * Support for fstat().
Chris@0 276 *
Chris@0 277 * @return bool
Chris@0 278 * An array with file status, or FALSE in case of an error - see fstat()
Chris@0 279 * for a description of this array.
Chris@0 280 *
Chris@0 281 * @see http://php.net/manual/streamwrapper.stream-stat.php
Chris@0 282 */
Chris@0 283 public function stream_stat() {
Chris@0 284 return fstat($this->handle);
Chris@0 285 }
Chris@0 286
Chris@0 287 /**
Chris@0 288 * Support for fclose().
Chris@0 289 *
Chris@0 290 * @return bool
Chris@0 291 * TRUE if stream was successfully closed.
Chris@0 292 *
Chris@0 293 * @see http://php.net/manual/streamwrapper.stream-close.php
Chris@0 294 */
Chris@0 295 public function stream_close() {
Chris@0 296 return fclose($this->handle);
Chris@0 297 }
Chris@0 298
Chris@0 299 /**
Chris@0 300 * {@inheritdoc}
Chris@0 301 */
Chris@0 302 public function stream_cast($cast_as) {
Chris@0 303 return $this->handle ? $this->handle : FALSE;
Chris@0 304 }
Chris@0 305
Chris@0 306 /**
Chris@0 307 * {@inheritdoc}
Chris@0 308 */
Chris@0 309 public function stream_metadata($uri, $option, $value) {
Chris@0 310 $target = $this->getLocalPath($uri);
Chris@0 311 $return = FALSE;
Chris@0 312 switch ($option) {
Chris@0 313 case STREAM_META_TOUCH:
Chris@0 314 if (!empty($value)) {
Chris@0 315 $return = touch($target, $value[0], $value[1]);
Chris@0 316 }
Chris@0 317 else {
Chris@0 318 $return = touch($target);
Chris@0 319 }
Chris@0 320 break;
Chris@0 321
Chris@0 322 case STREAM_META_OWNER_NAME:
Chris@0 323 case STREAM_META_OWNER:
Chris@0 324 $return = chown($target, $value);
Chris@0 325 break;
Chris@0 326
Chris@0 327 case STREAM_META_GROUP_NAME:
Chris@0 328 case STREAM_META_GROUP:
Chris@0 329 $return = chgrp($target, $value);
Chris@0 330 break;
Chris@0 331
Chris@0 332 case STREAM_META_ACCESS:
Chris@0 333 $return = chmod($target, $value);
Chris@0 334 break;
Chris@0 335 }
Chris@0 336 if ($return) {
Chris@0 337 // For convenience clear the file status cache of the underlying file,
Chris@0 338 // since metadata operations are often followed by file status checks.
Chris@0 339 clearstatcache(TRUE, $target);
Chris@0 340 }
Chris@0 341 return $return;
Chris@0 342 }
Chris@0 343
Chris@0 344 /**
Chris@0 345 * {@inheritdoc}
Chris@0 346 *
Chris@0 347 * Since Windows systems do not allow it and it is not needed for most use
Chris@0 348 * cases anyway, this method is not supported on local files and will trigger
Chris@0 349 * an error and return false. If needed, custom subclasses can provide
Chris@0 350 * OS-specific implementations for advanced use cases.
Chris@0 351 */
Chris@0 352 public function stream_set_option($option, $arg1, $arg2) {
Chris@0 353 trigger_error('stream_set_option() not supported for local file based stream wrappers', E_USER_WARNING);
Chris@0 354 return FALSE;
Chris@0 355 }
Chris@0 356
Chris@0 357 /**
Chris@0 358 * {@inheritdoc}
Chris@0 359 */
Chris@0 360 public function stream_truncate($new_size) {
Chris@0 361 return ftruncate($this->handle, $new_size);
Chris@0 362 }
Chris@0 363
Chris@0 364 /**
Chris@0 365 * Support for unlink().
Chris@0 366 *
Chris@0 367 * @param string $uri
Chris@0 368 * A string containing the URI to the resource to delete.
Chris@0 369 *
Chris@0 370 * @return bool
Chris@0 371 * TRUE if resource was successfully deleted.
Chris@0 372 *
Chris@0 373 * @see http://php.net/manual/streamwrapper.unlink.php
Chris@0 374 */
Chris@0 375 public function unlink($uri) {
Chris@0 376 $this->uri = $uri;
Chris@0 377 return drupal_unlink($this->getLocalPath());
Chris@0 378 }
Chris@0 379
Chris@0 380 /**
Chris@0 381 * Support for rename().
Chris@0 382 *
Chris@0 383 * @param string $from_uri
Chris@0 384 * The URI to the file to rename.
Chris@0 385 * @param string $to_uri
Chris@0 386 * The new URI for file.
Chris@0 387 *
Chris@0 388 * @return bool
Chris@0 389 * TRUE if file was successfully renamed.
Chris@0 390 *
Chris@0 391 * @see http://php.net/manual/streamwrapper.rename.php
Chris@0 392 */
Chris@0 393 public function rename($from_uri, $to_uri) {
Chris@0 394 return rename($this->getLocalPath($from_uri), $this->getLocalPath($to_uri));
Chris@0 395 }
Chris@0 396
Chris@0 397 /**
Chris@0 398 * Gets the name of the directory from a given path.
Chris@0 399 *
Chris@18 400 * This method is usually accessed through
Chris@18 401 * \Drupal\Core\File\FileSystemInterface::dirname(), which wraps around the
Chris@18 402 * PHP dirname() function because it does not support stream wrappers.
Chris@0 403 *
Chris@0 404 * @param string $uri
Chris@0 405 * A URI or path.
Chris@0 406 *
Chris@0 407 * @return string
Chris@0 408 * A string containing the directory name.
Chris@0 409 *
Chris@18 410 * @see \Drupal\Core\File\FileSystemInterface::dirname()
Chris@0 411 */
Chris@0 412 public function dirname($uri = NULL) {
Chris@0 413 list($scheme) = explode('://', $uri, 2);
Chris@0 414 $target = $this->getTarget($uri);
Chris@0 415 $dirname = dirname($target);
Chris@0 416
Chris@0 417 if ($dirname == '.') {
Chris@0 418 $dirname = '';
Chris@0 419 }
Chris@0 420
Chris@0 421 return $scheme . '://' . $dirname;
Chris@0 422 }
Chris@0 423
Chris@0 424 /**
Chris@0 425 * Support for mkdir().
Chris@0 426 *
Chris@0 427 * @param string $uri
Chris@0 428 * A string containing the URI to the directory to create.
Chris@0 429 * @param int $mode
Chris@0 430 * Permission flags - see mkdir().
Chris@0 431 * @param int $options
Chris@0 432 * A bit mask of STREAM_REPORT_ERRORS and STREAM_MKDIR_RECURSIVE.
Chris@0 433 *
Chris@0 434 * @return bool
Chris@0 435 * TRUE if directory was successfully created.
Chris@0 436 *
Chris@0 437 * @see http://php.net/manual/streamwrapper.mkdir.php
Chris@0 438 */
Chris@0 439 public function mkdir($uri, $mode, $options) {
Chris@0 440 $this->uri = $uri;
Chris@0 441 $recursive = (bool) ($options & STREAM_MKDIR_RECURSIVE);
Chris@0 442 if ($recursive) {
Chris@0 443 // $this->getLocalPath() fails if $uri has multiple levels of directories
Chris@0 444 // that do not yet exist.
Chris@0 445 $localpath = $this->getDirectoryPath() . '/' . $this->getTarget($uri);
Chris@0 446 }
Chris@0 447 else {
Chris@0 448 $localpath = $this->getLocalPath($uri);
Chris@0 449 }
Chris@18 450 /** @var \Drupal\Core\File\FileSystemInterface $file_system */
Chris@18 451 $file_system = \Drupal::service('file_system');
Chris@0 452 if ($options & STREAM_REPORT_ERRORS) {
Chris@18 453 return $file_system->mkdir($localpath, $mode, $recursive);
Chris@0 454 }
Chris@0 455 else {
Chris@18 456 return @$file_system->mkdir($localpath, $mode, $recursive);
Chris@0 457 }
Chris@0 458 }
Chris@0 459
Chris@0 460 /**
Chris@0 461 * Support for rmdir().
Chris@0 462 *
Chris@0 463 * @param string $uri
Chris@0 464 * A string containing the URI to the directory to delete.
Chris@0 465 * @param int $options
Chris@0 466 * A bit mask of STREAM_REPORT_ERRORS.
Chris@0 467 *
Chris@0 468 * @return bool
Chris@0 469 * TRUE if directory was successfully removed.
Chris@0 470 *
Chris@0 471 * @see http://php.net/manual/streamwrapper.rmdir.php
Chris@0 472 */
Chris@0 473 public function rmdir($uri, $options) {
Chris@0 474 $this->uri = $uri;
Chris@18 475 /** @var \Drupal\Core\File\FileSystemInterface $file_system */
Chris@18 476 $file_system = \Drupal::service('file_system');
Chris@0 477 if ($options & STREAM_REPORT_ERRORS) {
Chris@18 478 return $file_system->rmdir($this->getLocalPath());
Chris@0 479 }
Chris@0 480 else {
Chris@18 481 return @$file_system->rmdir($this->getLocalPath());
Chris@0 482 }
Chris@0 483 }
Chris@0 484
Chris@0 485 /**
Chris@0 486 * Support for stat().
Chris@0 487 *
Chris@0 488 * @param string $uri
Chris@0 489 * A string containing the URI to get information about.
Chris@0 490 * @param int $flags
Chris@0 491 * A bit mask of STREAM_URL_STAT_LINK and STREAM_URL_STAT_QUIET.
Chris@0 492 *
Chris@0 493 * @return array
Chris@0 494 * An array with file status, or FALSE in case of an error - see fstat()
Chris@0 495 * for a description of this array.
Chris@0 496 *
Chris@0 497 * @see http://php.net/manual/streamwrapper.url-stat.php
Chris@0 498 */
Chris@0 499 public function url_stat($uri, $flags) {
Chris@0 500 $this->uri = $uri;
Chris@0 501 $path = $this->getLocalPath();
Chris@0 502 // Suppress warnings if requested or if the file or directory does not
Chris@0 503 // exist. This is consistent with PHP's plain filesystem stream wrapper.
Chris@0 504 if ($flags & STREAM_URL_STAT_QUIET || !file_exists($path)) {
Chris@0 505 return @stat($path);
Chris@0 506 }
Chris@0 507 else {
Chris@0 508 return stat($path);
Chris@0 509 }
Chris@0 510 }
Chris@0 511
Chris@0 512 /**
Chris@0 513 * Support for opendir().
Chris@0 514 *
Chris@0 515 * @param string $uri
Chris@0 516 * A string containing the URI to the directory to open.
Chris@0 517 * @param int $options
Chris@0 518 * Unknown (parameter is not documented in PHP Manual).
Chris@0 519 *
Chris@0 520 * @return bool
Chris@0 521 * TRUE on success.
Chris@0 522 *
Chris@0 523 * @see http://php.net/manual/streamwrapper.dir-opendir.php
Chris@0 524 */
Chris@0 525 public function dir_opendir($uri, $options) {
Chris@0 526 $this->uri = $uri;
Chris@0 527 $this->handle = opendir($this->getLocalPath());
Chris@0 528
Chris@0 529 return (bool) $this->handle;
Chris@0 530 }
Chris@0 531
Chris@0 532 /**
Chris@0 533 * Support for readdir().
Chris@0 534 *
Chris@0 535 * @return string
Chris@0 536 * The next filename, or FALSE if there are no more files in the directory.
Chris@0 537 *
Chris@0 538 * @see http://php.net/manual/streamwrapper.dir-readdir.php
Chris@0 539 */
Chris@0 540 public function dir_readdir() {
Chris@0 541 return readdir($this->handle);
Chris@0 542 }
Chris@0 543
Chris@0 544 /**
Chris@0 545 * Support for rewinddir().
Chris@0 546 *
Chris@0 547 * @return bool
Chris@0 548 * TRUE on success.
Chris@0 549 *
Chris@0 550 * @see http://php.net/manual/streamwrapper.dir-rewinddir.php
Chris@0 551 */
Chris@0 552 public function dir_rewinddir() {
Chris@0 553 rewinddir($this->handle);
Chris@0 554 // We do not really have a way to signal a failure as rewinddir() does not
Chris@0 555 // have a return value and there is no way to read a directory handler
Chris@0 556 // without advancing to the next file.
Chris@0 557 return TRUE;
Chris@0 558 }
Chris@0 559
Chris@0 560 /**
Chris@0 561 * Support for closedir().
Chris@0 562 *
Chris@0 563 * @return bool
Chris@0 564 * TRUE on success.
Chris@0 565 *
Chris@0 566 * @see http://php.net/manual/streamwrapper.dir-closedir.php
Chris@0 567 */
Chris@0 568 public function dir_closedir() {
Chris@0 569 closedir($this->handle);
Chris@0 570 // We do not really have a way to signal a failure as closedir() does not
Chris@0 571 // have a return value.
Chris@0 572 return TRUE;
Chris@0 573 }
Chris@0 574
Chris@0 575 }