HPCloud-PHP  1.2.0
PHP bindings for HPCloud and OpenStack services.
 All Classes Namespaces Files Functions Variables Pages
StreamWrapperFS.php
Go to the documentation of this file.
1 <?php
2 /* ============================================================================
3 (c) Copyright 2012 Hewlett-Packard Development Company, L.P.
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights to
7 use, copy, modify, merge,publish, distribute, sublicense, and/or sell copies of
8 the Software, and to permit persons to whom the Software is furnished to do so,
9 subject to the following conditions:
10 
11 The above copyright notice and this permission notice shall be included in all
12 copies or substantial portions of the Software.
13 
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 SOFTWARE.
21 ============================================================================ */
22 /**
23  * @file
24  * Contains the stream wrapper for `swiftfs://` URLs.
25  *
26  * <b>Note, this stream wrapper is in early testing.</b>
27  *
28  * The stream wrapper implemented in HPCloud\Storage\ObjectStorage\StreamWrapper
29  * only supports the elements of a stream that are implemented by object
30  * storage. This is how the PHP documentation states a stream wrapper should be
31  * created. Because some features do not exist, attempting to treat a stream
32  * wrapper as if it were a file system will not entirely work. For example,
33  * while there are not directories objects have pathy names (with / separators).
34  * Directory calls to object storage with the default stream wrappers will not
35  * operate how they would for a file system.
36  *
37  * StreamWrapperFS is an attempt to make a filesystem like stream wrapper.
38  * Hence the protocol is swiftfs standing for swift file system.
39  *
40  * To understand how this stream wrapper works start by first reading the
41  * documentation on the HPCloud::Storage::ObjectStorage::StreamWrapper.
42  *
43  * <b>DIRECTORIES</b>
44  *
45  * Because OpenStack Swift does not support directories the swift:// stream
46  * wrapper does not support them. This stream wrapper attempts to fake them by
47  * faking directory stats, mkdir, and rmdir. By default (see the options below
48  * for how to change these) directories have permissions of 777, timestamps
49  * close to that of the request, and the user and group called by php. We mock
50  * these on the fly though information is stored in the PHP stat cache.
51  *
52  * In addition to the parameters supported by StreamWrapper, the following
53  * parameters may be set either in the stream context or through
54  * HPCloud::Bootstrap::setConfiguration():
55  * - swiftfs_fake_stat_mode: Directories don't exist in swift. When stat() is
56  * is called on a directory we mock the stat information so functions like
57  * is_dir will work. The default file permissions is 0777. Though this
58  * parameter you can pass is a different set of file permissions to use
59  * for these mock stats.
60  * - swiftfs_fake_isdir_true: Directory functions like mkdir and is_dir (stat)
61  * check to see if there are objects with the the passed in directory as a
62  * prefix to see if it already exists. If you want is_dir to always return
63  * true even if it is not an existing prefix set this to TRUE. Defaults to
64  * FALSE.
65  */
66 
68 
69 use \HPCloud\Bootstrap;
70 use \HPCloud\Storage\ObjectStorage;
71 
72 /**
73  * Provides stream wrapping for Swift like a file system.
74  *
75  * This provides a full stream wrapper to expose `swiftfs://` URLs to the
76  * PHP stream system.
77  *
78  *
79  * @see http://us3.php.net/manual/en/class.streamwrapper.php
80  */
82 
83  const DEFAULT_SCHEME = 'swiftfs';
84  protected $schemeName = self::DEFAULT_SCHEME;
85 
86  /**
87  * Fake a make a dir.
88  *
89  * ObjectStorage has pathy objects not directories. If no objects with a path
90  * prefix exist we can pass creating a directory. If objects with a path
91  * prefix exist adding the directory will fail.
92  */
93  public function mkdir($uri, $mode, $options) {
94 
95  return ($this->cxt('swiftfs_fake_isdir_true', FALSE) || !($this->testDirectoryExists($uri)));
96 
97  }
98 
99  /**
100  * Fake Remove a directory.
101  *
102  * ObjectStorage has pathy objects not directories. If no objects with a path
103  * prefix exist we can pass removing it. If objects with a path prefix exist
104  * removing the directory will fail.
105  */
106  public function rmdir($path, $options) {
107 
108  return !($this->testDirectoryExists($path));
109 
110  }
111 
112  /**
113  * @see stream_stat().
114  */
115  public function url_stat($path, $flags) {
116  $stat = parent::url_stat($path, $flags);
117 
118  // If the file stat setup returned anything return it.
119  if ($stat) {
120  return $stat;
121  }
122  // When FALSE is returned there is no file to stat. So, we attempt to handle
123  // it like a directory.
124  else {
125  if ($this->cxt('swiftfs_fake_isdir_true', FALSE) || $this->testDirectoryExists($path)) {
126  // The directory prefix exists. Fake the directory file permissions.
127  return $this->fakeStat(TRUE);
128  }
129  else {
130  // The directory does not exist as a prefix.
131  return FALSE;
132  }
133  }
134  }
135 
136  ///////////////////////////////////////////////////////////////////
137  // INTERNAL METHODS
138  // All methods beneath this line are not part of the Stream API.
139  ///////////////////////////////////////////////////////////////////
140 
141  /**
142  * Test if a path prefix (directory like) esits.
143  *
144  * ObjectStorage has pathy objects not directories. If objects exist with a
145  * path prefix we can consider that the directory exists. For example, if
146  * we have an object at foo/bar/baz.txt and test the existance of the
147  * directory foo/bar/ we sould see it.
148  *
149  * @param string $path
150  * The directory path to test.
151  * @retval boolean
152  * @return boolean
153  * TRUE if the directory prefix exists and FALSE otherwise.
154  */
155  protected function testDirectoryExists($path) {
156  $url = $this->parseUrl($path);
157 
158  if (empty($url['host'])) {
159  trigger_error('Container name is required.' , E_USER_WARNING);
160  return FALSE;
161  }
162 
163  try {
164  $this->initializeObjectStorage();
165  $container = $this->store->container($url['host']);
166 
167  if (empty($url['path'])) {
168  $this->dirPrefix = '';
169  }
170  else {
171  $this->dirPrefix = $url['path'];
172  }
173 
174  $sep = '/';
175 
176 
177  $dirListing = $container->objectsWithPrefix($this->dirPrefix, $sep);
178 
179  return !empty($dirListing);
180  }
181  catch (\HPCloud\Exception $e) {
182  trigger_error('Path could not be opened: ' . $e->getMessage(), E_USER_WARNING);
183  return FALSE;
184  }
185  }
186 
187  /**
188  * Fake stat data.
189  *
190  * Under certain conditions we have to return totally trumped-up
191  * stats. This generates those.
192  */
193  protected function fakeStat($dir = FALSE) {
194 
195  $request_time = time();
196 
197  // Set inode type to directory or file.
198  $type = $dir ? 040000 : 0100000;
199  // Fake world-readible
200  $mode = $type + $this->cxt('swiftfs_fake_stat_mode', 0777);
201 
202  $values = array(
203  'dev' => 0,
204  'ino' => 0,
205  'mode' => $mode,
206  'nlink' => 0,
207  'uid' => posix_getuid(),
208  'gid' => posix_getgid(),
209  'rdev' => 0,
210  'size' => 0,
211  'atime' => $request_time,
212  'mtime' => $request_time,
213  'ctime' => $request_time,
214  'blksize' => -1,
215  'blocks' => -1,
216  );
217 
218  $final = array_values($values) + $values;
219 
220  return $final;
221  }
222 
223 }