[Review] Route stat() family of calls via wrapper ops.

php.internals

Sara Golemon

23 years ago
The streams API has, since early in its introduction had the capacity to perform stat() family calls on files under any wrapper. In theory. What's preventing wrapper->wops->url_stat from being useful is that the code in ext/standard/filestat.c has never been routed through this layer. I've put together a patch at http://169.229.139.97/test/stat-streams.diff to accomplish this and it seems to test well with local files, local symlinks, and remote ftp files (the ftp wrapper has had a limited url_stat method waiting to be used for some months now). However, since the stat() related code does a little voodoo in terms of caching results and handling multiple function calls in a single do-all, I'd like to get feedback from others on its suitability. Also, this patch extends the stream_wrapper_ops structure slightly by adding an access_check() method (for is_readable(), is_writeable(), etc...) url_stat() and access_check() are both implemented in the plainfiles wrapper with this path. The ugliest part of this patch is that it requires special treatment for the plainfiles wrapper in order to deal with symlink stat calls. An alternative (and it wouldn't take much to convince me to go the other way) would be to add a parameter to the url_stat() method to indicate if the stat is a symlink_stat. Look forward to your thoughts. -Sara

Moriyoshi Koizumi

23 years ago
<pollita@php.net> wrote:
> The ugliest part of this patch is that it requires special treatment for the > plainfiles wrapper in order to deal with symlink stat calls. An alternative > (and it wouldn't take much to convince me to go the other way) would be to > add a parameter to the url_stat() method to indicate if the stat is a > symlink_stat.
If we implement url_stat() in such way, perhaps we can live without access_check() method. But the point is, not all wrappers can accept *ix-like access control scheme.. Moriyoshi

Sara Golemon

23 years ago
> <pollita@php.net> wrote: > >> The ugliest part of this patch is that it requires special treatment >> for the plainfiles wrapper in order to deal with symlink stat calls. >> An alternative (and it wouldn't take much to convince me to go the >> other way) would be to add a parameter to the url_stat() method to >> indicate if the stat is a symlink_stat. > > If we implement url_stat() in such way, perhaps we can live without > access_check() method. >
Yeah, I got to thinking that later on.... Perhaps the cleanest method would be to pass on the filestat_type directly to the url_stat method. plainfiles can then do VCWD_ACCESS, VCWD_STAT, or VCWD_LSTAT as appropriate and other wrappers can do their thing. (additionally: http, since it allows redirection, would be able to emulate the symlink layer by returning information on the initial hit, rather than following 302 redirects) The php_stat method in filestat.c can then focus on simply managing the statecache and forming the statbuf into returnable zvals.
> But the point is, not all wrappers can accept > *ix-like access control scheme.. >
Nor can they return full stat detail. i.e.: What's the filesize of php://stdin ? Who "owns" http://example.com/foo.html ? Last I spoke to Wez on the subject he indicated that we should just attempt to return as much information as we can and default on the rest. http:// wrapper: is_writable() would always return false. is_readable() would return true *IF* a HEAD request returned 2xx or 3xx is_executable() would always return false. file_exists() would return true if a HEAD request returned 2xx, 3xx, or 4xx (not including 404) sb values such as mtime, size, etc... would be parsable from the http-response headers, while ctime, atime, inode, uid, gid would be unknown and default to 0 (or maybe -1) ftp:// wrapper: is_writeable() would return true if APPE <file> returned 2xx is_readable() would return true if RETR <file> returned 2xx is_executable() would always return false file_exists() would return true if SIZE <file> returned 2xx sb_size would be fetchable from SIZE <file>, however all other values are not reliable on all platforms and would have to return 0. -Sara

Melvyn Sopacua

23 years ago
Hi, On Monday 11 August 2003 20:37, Sara Golemon wrote:
> Nor can they return full stat detail. i.e.: What's the filesize of > php://stdin ? Who "owns" http://example.com/foo.html ? Last I spoke to > Wez on the subject he indicated that we should just attempt to return as > much information as we can and default on the rest. > > http:// wrapper: > is_writable() would always return false. > is_readable() would return true *IF* a HEAD request returned 2xx or 3xx > is_executable() would always return false. > file_exists() would return true if a HEAD request returned 2xx, 3xx, or > 4xx (not including 404)
I wouldn't agree there: http://www.example.com/file with spaces.png [1] returns invalid method: 400. I think the entire 4xx and 5xx family should return false. [1] I know the url is invalid, but if you wanna do syntax checking on urls, then you're in for a treat, especially when the url contains double-byte characters. As for 30[12], what happens when the refered document does not exist? Symlinks have a maximum number of links and return ELOOP. Does the same apply to 302 and what is the value of MAXSYMLINKS (sys/param.h) in that case?
> -Sara
-- Melvyn

Sara Golemon

23 years ago
>> http:// wrapper: >> is_writable() would always return false. >> is_readable() would return true *IF* a HEAD request returned 2xx or >> 3xx is_executable() would always return false. >> file_exists() would return true if a HEAD request returned 2xx, 3xx, >> or >> 4xx (not including 404) > > I wouldn't agree there: > http://www.example.com/file with spaces.png [1] > > returns invalid method: 400. I think the entire 4xx and 5xx family > should return false. >
*blink* Did I write that? Musta been talking to someone while I was doing it. Yes, I agree, only 2xx and 3xx should return true.
> As for 30[12], what happens when the refered document does not exist? > Symlinks have a maximum number of links and return ELOOP. Does the same > apply to 302 and what is the value of MAXSYMLINKS (sys/param.h) in that > case? >
I couldn't say without double checking the code, but there's a forward limit in http_fopen_wrapper which limits how far the http wrapper will go in following redirects. If that limit is reached the stat would fail or the is_readable would return false. (Just as it would for plainfiles) -Sara

Moriyoshi Koizumi

23 years ago
"Sara Golemon" <pollita@php.net> wrote:
> http:// wrapper: > is_writable() would always return false. > is_readable() would return true *IF* a HEAD request returned 2xx or 3xx > is_executable() would always return false. > file_exists() would return true if a HEAD request returned 2xx, 3xx, or > 4xx (not including 404) > sb values such as mtime, size, etc... would be parsable from the > http-response headers, while ctime, atime, inode, uid, gid would be > unknown and default to 0 (or maybe -1)
Just for a proposal, I made a table of the expected return values of is_*able() family. But this will be under a drastically change if we provide native WebDAV support. 200 OK 203 Non-Authoritative Information 204 No Content 205 Reset Content 206 Partial Content "r--" (in "rwx" format) 300 Multiple Choices Depending on the status of the resource referred to by a URI given in the "Location" header of the previous session. (false if "Location" is not proposed by the server) 301 Moved Permanently 302 Found 303 See Other Depending on the status of the resource redirected to 304 Not Modified "r--" 305 Use Proxy Depending on the status of a subsequent HTTP session which will take place between the agent and the proxy server. This seems irrelevant though. 400 Bad Request 401 Unauthorized 402 Payment Required 403 Forbidden 404 Not Found 405 Method Not Allowed 408 Request Timeout 410 Gone 412 Precondition Failed 414 Request-URI Too Long 415 Unsupported Media Type "---" 406 Not Acceptable "r--"; is_readable() should return true because the resource must exist as this status implys. 409 Conflict "---"; however it may return true if this is a result of temporary rw-locking failure. 411 Length Required 416 Requested Range Not Satisfiable "r--" because the resource is actually readable. Following read operations is likely to fail. 500 Internal Server Error 501 Not Implemented 503 Service Unavailable 505 HTTP Version Not Supported "---" I'm under the impression it'd be even better if we can other kinds of metadata such as MIME type with the single stat interface.
> ftp:// wrapper: > is_executable() would always return false
Since is_executable() can be applied on directories, we have to first check if the given path name specifies a directory or a normal file by a "SIZE" request.
> file_exists() would return true if SIZE <file> returned 2xx > sb_size would be fetchable from SIZE <file>, however all other values > are not reliable on all platforms and would have to return 0.
As "SIZE" cannot be used on directories (which should result in 550), a single "RNFR" (rename file from) request could make more sense here. Moriyoshi

Melvyn Sopacua

23 years ago
On Wednesday 13 August 2003 20:13, Moriyoshi Koizumi wrote:
> 304 Not Modified > > "r--"
Since we're looking at "simple http access" I don't think this should return readable. Normally you would not be able to set headers when using fopen('http://') (right?), so technically, you would not be able to get a 304 response, because that would only be returned, on requests that have a 'If-Not-Modified-Since' and family request header. IF you DO get a 304 response for whatever reason, then you still have no content at all because the content is assumed to be in the 'local browser cache'. The other route to take, would be to indeed have local cache, but I'm not sure if this is at all usefull. It would look something like this: <?php // || is just an example - but illustrates part of the problem. $fp =fopen('http://www.example.com/newsfeed.rss||/usr/local/www/data/examplefeed.rss", 'r'); ?> This would then send If-Not-Modified-Since, whith the gmtime of the filemtime of examplefeed.rss stored locally. If you get a 304, than examplefeed.rss is returned, otherwise the remote file is returned and examplefeed.rss modified accordingly. I suppose it would be useful, in environments where users are not able to use crontabs and you wanna limit the number of times the file is downloaded, but with a little playing around with fsockopen, you can do the exact same thing.
-- Melvyn