Roxen.git
/
server
/
modules
/
filesystems
/
filesystem.pike
version
»
Context lines:
10
20
40
80
file
none
3
Roxen.git/server/modules/filesystems/filesystem.pike:319:
access_as_user = query("access_as_user"); access_as_user_throw = query("access_as_user_throw"); access_as_user_db = my_configuration()->find_user_database( query("access_as_user_db") ); dotfiles = query(".files"); path = roxen_path(encode_path(query("searchpath"))); mountpoint = query("mountpoint"); stat_cache = query("stat_cache"); internal_files = map(query("internal_files"), encode_path);
-
if
(sizeof(path)
&&
!has_suffix(path
,
"/"))
path
+
= "/";
+
//
Expand cwd-relative paths, and ensure terminating slash.
+
path
=
combine
_
path
(
getcwd(),
path, "
.
/");
#if constant(System.normalize_path) if (catch { normalized_path = System.normalize_path(path + "."); }) { report_error(LOCALE(1, "Path normalization of %s: %s failed.\n"), path, mountpoint); #ifdef __NT__ normalized_path = replace(path, "/", "\\"); #else
Roxen.git/server/modules/filesystems/filesystem.pike:449:
if(stat_cache && !id->pragma["no-cache"] && (fs = cache_lookup("stat_cache", norm_f))) return fs[0]; object privs; SETUID_NT("Statting file"); /* No security currently in this function */ fs = file_stat(norm_f); privs = 0;
+
if (fs && !(fs->ino)) {
+
/* NB: NT does not have a way to get a valid ino field before
+
* Windows Server 2012. Substitute with a hash of the
+
* normalized path.
+
*
+
* NB: Use %+4c to avoid bignums (which are not supported by
+
* the ino field).
+
*/
+
sscanf(Crypto.SHA256.hash(string_to_utf8(lower_case(f))), "%+4c", fs->ino);
+
}
if(!stat_cache) return fs; cache_set("stat_cache", norm_f, ({fs})); return fs; }
-
+
//! Normalize DAVLock path identifier.
+
string resource_id(string path, RequestID|int(0..0) id)
+
{
+
if ((< "Darwin", "Win32" >)[uname()->sysname]) {
+
return ::resource_id(lower_case(path), id);
+
}
+
return ::resource_id(path, id);
+
}
+
//! Convert to filesystem encoding. //! //! @note //! Note that the @expr{"iso-8859-1"@} encoding will perform //! conversion to utf-8 for wide strings OSes other than NT. string encode_path( string p ) { if( path_encoding != "iso-8859-1" ) p = Charset.encoder( path_encoding )->feed( p )->drain();
-
#
ifndef
__NT__
+
#
if
!defined(
__NT__
) || constant(Stdio.__HAVE_UTF8_FS__)
if( String.width( p ) != 8 ) p = string_to_utf8( p );
-
#
else
+
#
endif
+
#ifdef __NT__
// NB: stat() on NT doesn't like trailing slashes (cf [bug 76]). while( strlen(p) && p[-1] == '/' ) p = p[..strlen(p)-2]; #endif return p; } //! Convert from filesystem encoding. string decode_path(string p) {
-
#
ifdef
__NT__
+
#
if
defined(
__NT__
) && !constant(Stdio.__HAVE_UTF8_FS__)
// The filesystem on NT uses wide characters. return p; #else // While filesystems on other OSes typically are 8bit. switch(lower_case(path_encoding)) { case "iso-8859-1": return p; case "utf8": case "utf-8": // NB: We assume that the filesystem will normalize // the path as appropriate. return Unicode.normalize(utf8_to_string(p), "NFC"); default: return Charset.decoder(path_encoding)->feed(p)->drain(); }
-
#endif /* !__NT__ */
+
#endif /* !__NT__
|| Stdio.__HAVE_UTF8_FS__
*/
} protected string low_real_path(string f, RequestID id) { string norm_f;
-
+
#ifdef __NT__
+
// These characters are apparently invalid in NTFS filenames.
+
if (f != replace(f, "*?|"/"", ({ "", "", "" }))) {
+
return 0;
+
}
+
#endif
if (mixed err = catch { /* NOTE: NORMALIZE_PATH() may throw errors. */ norm_f = NORMALIZE_PATH(path + encode_path(f)); #if constant(System.normalize_path) if (!has_prefix(norm_f, normalized_path) && #ifdef __NT__ (norm_f+"\\" != normalized_path) #else /* !__NT__ */ (norm_f+"/" != normalized_path) #endif /* __NT__ */
Roxen.git/server/modules/filesystems/filesystem.pike:561:
{ if (!query("put")) return 0; if (query("check_auth") && (!id->conf->authenticate( id )) ) { TRACE_LEAVE("LOCK: Permission denied"); return // FIXME: Sane realm. Roxen.http_auth_required("foo", "<h1>Permission to 'LOCK' files denied</h1>", id); }
-
register_lock(
encode_
path
(path)
, lock, id);
+
register_lock(path, lock, id);
return 0; } mapping(string:mixed) unlock_file(string path, DAVLock lock, RequestID|int(0..0) id) { if (!query("put")) return 0; if (id && query("check_auth") && (!id->conf->authenticate( id )) ) { TRACE_LEAVE("UNLOCK: Permission denied"); return // FIXME: Sane realm. Roxen.http_auth_required("foo", "<h1>Permission to 'UNLOCK' files denied</h1>", id); }
-
unregister_lock(
encode_
path
(path)
, lock, id);
+
unregister_lock(path, lock, id);
return 0; } int dir_filter_function(string f, RequestID id) { if(f[0]=='.' && !dotfiles) return 0; if(!tilde && Roxen.backup_extension(f)) return 0; return 1; }
Roxen.git/server/modules/filesystems/filesystem.pike:606:
if(query("check_auth") && (!id->conf->authenticate( id ) ) ) { SIMPLE_TRACE_LEAVE("%s: Authentication required.", id->method); // FIXME: Sane realm. // FIXME: Recursion and htaccess? return Roxen.http_auth_required("foo", sprintf("<h1>Permission to '%s' denied</h1>", id->method), id); } TRACE_LEAVE("Fall back to the default write access checks.");
-
return ::write_access(
encode_
path
(path)
, recursive, id);
+
return ::write_access(path, recursive, id);
} array find_dir( string f, RequestID id ) { array dir; FILESYSTEM_WERR("find_dir for \""+f+"\"" + (id->misc->internal_get ? " (internal)" : "")); string norm_f = real_path(f, id);
Roxen.git/server/modules/filesystems/filesystem.pike:677:
void recursive_rm(string real_dir, string virt_dir, int(0..1) check_status_needed, RequestID id) { SIMPLE_TRACE_ENTER(this, "Deleting all files in directory %O...", real_dir); foreach(get_dir(real_dir) || ({}), string fname) { string real_fname = combine_path(real_dir, fname); string virt_fname = virt_dir + "/" + decode_path(fname); Stat stat = file_stat(real_fname);
+
SIMPLE_TRACE_ENTER(this, "Deleting %s %O.",
+
stat?(stat->isdir?"directory":"file"):"missing",
+
real_fname);
if (!stat) { id->set_status_for_path(virt_fname, 404); TRACE_LEAVE("File not found."); continue; }
-
SIMPLE_TRACE_ENTER
(
this,
"Deleting %s %O.",
-
stat->isdir?"directory":"file", real_fname);
+
if
(
stat->isdir)
{
+
virt_fname
+=
"/";
+
}
int(0..1)|mapping sub_status;
-
if (
check_status_needed &&
-
mappingp(sub_status = write_access(virt_fname, 1, id))) {
+
if (mappingp(sub_status = write_access(virt_fname, 1, id))) {
id->set_status_for_path(virt_fname, sub_status->error); TRACE_LEAVE("Write access denied."); continue; } if (stat->isdir) { recursive_rm(real_fname, virt_fname, sub_status, id); } /* Clear the stat-cache for this file */ if (stat_cache) {
Roxen.git/server/modules/filesystems/filesystem.pike:716:
TRACE_LEAVE("Deletion failed."); } #if constant(System.EEXIST) else { TRACE_LEAVE("Directory not empty."); } #endif } else { deletes++;
+
unlock_path(virt_fname, id);
+
if (id->misc->quota_obj && stat->isreg()) { id->misc->quota_obj->deallocate(virt_fname, stat->size()); } TRACE_LEAVE("Ok."); } } TRACE_LEAVE("Done."); }
Roxen.git/server/modules/filesystems/filesystem.pike:855:
if (rr[1] == -3) { return(1); } } else { return(0); } } return(0); }
+
//! Return @expr{1@} if both arguments refer to the same inode.
+
int is_same_inode(Stdio.Stat a_st, Stdio.Stat b_st)
+
{
+
if (a_st == b_st) return 1;
+
if ((a_st->mode == b_st->mode) &&
+
(a_st->size == b_st->size) &&
+
(a_st->ino == b_st->ino) &&
+
(a_st->dev == b_st->dev)) {
+
return 1;
+
}
+
return 0;
+
}
+
//! @[chmod()] that doesn't throw errors. string safe_chmod(string path, int mask) { return describe_error(catch { chmod(path, mask); return 0; }); } mapping make_collection(string coll, RequestID id)
Roxen.git/server/modules/filesystems/filesystem.pike:896:
id->method)); if (id->method == "MKCOL") { return Roxen.http_status(405, "Collection already exists."); } return 0; } // Disallow if the name is locked, or if the parent directory is locked. mapping(string:mixed) ret =
-
write_access(({coll, combine_path(coll, "..")}), 0, id);
+
write_access(({coll
+ "/"
, combine_path(coll, "..
/
")}), 0, id);
if (ret) return ret; mkdirs++; object privs; SETUID_TRACE("Creating directory/collection", 0); if (query("no_symlinks") && (contains_symlinks(path, coll))) { privs = 0; errors++; report_error(LOCALE(46,"Creation of %O failed. Permission denied.\n"),
Roxen.git/server/modules/filesystems/filesystem.pike:967:
*/ #define URI combine_path(mountpoint, f, ".") string norm_f = real_path(f, id); if (!norm_f) { TRACE_LEAVE("Permission denied."); return Roxen.http_status(403, "Access forbidden by user"); }
+
#ifdef __NT__
+
foreach(f/"/", string segment) {
+
if (has_suffix(segment, " ")) {
+
// Path segments on NT may not end with space.
+
return Roxen.http_status(405, "Invalid filesystem path.");
+
}
+
}
+
#endif
+
// NOTE: Sets id->misc->stat. size = _file_size( f, id ); FILESYSTEM_WERR(sprintf("_file_size(%O, %O) ==> %d\n", f, id, size));
-
+
if(!id->misc->internal_get) {
+
if (!dotfiles && sizeof(filter(f/"/", has_prefix, "."))) {
+
TRACE_LEAVE("Path contains .-file or .-directory.");
+
return 0;
+
}
+
if (FILTER_INTERNAL_FILE (f, id)) {
+
id->misc->error_code = 405;
+
TRACE_LEAVE ("Is internal file");
+
return 0;
+
}
+
}
+
/* * FIXME: Should probably move path-info extraction here. * /grubba 1998-08-26 */ switch(id->method) { case "GET": case "HEAD": case "POST":
Roxen.git/server/modules/filesystems/filesystem.pike:1001:
case 2: TRACE_LEAVE("Is directory"); return -1; /* Is dir */ default: if( f[ -1 ] == '/' || /* Trying to access file with '/' appended */ !norm_f) { /* Or a file that is not normalizable. */ return 0; }
-
if(!id->misc->internal_get)
-
{
-
if (!dotfiles
-
&& sizeof (tmp = (id->not_query/"/")[-1])
-
&& tmp[0] == '.')
-
{
-
TRACE_LEAVE("Is .-file");
-
return 0;
-
}
-
if (FILTER_INTERNAL_FILE (f, id))
-
{
-
TRACE_LEAVE ("Is internal file");
-
return 0;
-
}
-
}
-
+
TRACE_ENTER("Opening file \"" + f + "\"", 0); SETUID_TRACE("Open file", 1); o = Stdio.File( ); if(!o->open(norm_f, "r" )) o = 0; privs = 0; if(!o || (no_symlinks && (contains_symlinks(path, f)))) {
Roxen.git/server/modules/filesystems/filesystem.pike:1073:
// RFC 2518 8.3.1: // If a server receives a MKCOL request entity type it does not support // or understand it MUST respond with a 415 (Unsupported Media Type) // status code. SIMPLE_TRACE_LEAVE ("MKCOL failed since the request has content."); return Roxen.http_status(415, "Unsupported media type."); } /* FALL_THROUGH */ case "MKDIR": #if 1
-
return
make_collection(f, id);
+
mixed
ret =
make_collection(f, id);
+
if (ret) return ret;
+
if (id->misc->error_code) {
+
return Roxen.http_status(id->misc->error_code);
+
}
+
return 0;
#else /* !1 */ if(!query("put")) { id->misc->error_code = 405; TRACE_LEAVE(sprintf("%s disallowed (since PUT is disallowed)", id->method)); return 0; }
-
if (FILTER_INTERNAL_FILE (f, id)) {
-
id->misc->error_code = 405;
-
TRACE_LEAVE(sprintf("%s disallowed (since the dir name matches internal file glob)",
-
id->method));
-
return 0;
-
}
-
+
if (size != -1) { TRACE_LEAVE(sprintf("%s failed. Directory name already exists. ", id->method)); if (id->method == "MKCOL") { return Roxen.http_status(405, "Collection already exists."); } return 0; }
-
if (mapping(string:mixed) ret = write_access(f, 0, id)) {
+
if (mapping(string:mixed) ret = write_access(f
+ "/"
, 0, id)) {
TRACE_LEAVE("MKCOL: Write access denied."); return ret; } mkdirs++; SETUID_TRACE("Creating directory/collection", 0); if (query("no_symlinks") && (contains_symlinks(path, f))) { privs = 0; errors++; report_error(LOCALE(46,"Creation of %O failed. Permission denied.\n"), f); TRACE_LEAVE(sprintf("%s: Contains symlinks. Permission denied", id->method)); return Roxen.http_status(403, "Permission denied."); }
-
+
TRACE_ENTER(sprintf("%s: Accepted", id->method), 0);
+
code = mkdir(f); int err_code = errno();
-
TRACE_ENTER(sprintf("%s: Accepted", id->method), 0);
-
+
if (code) { string msg = safe_chmod(f, 0777 & ~(id->misc->umask || 022)); privs = 0; if (msg) { TRACE_LEAVE(sprintf("%s: chmod %O failed: %s", id->method, f, msg)); } else { TRACE_LEAVE(sprintf("%s: Success", id->method)); } TRACE_LEAVE("Success"); if (id->method == "MKCOL") { return Roxen.http_status(201, "Created"); } return Roxen.http_string_answer("Ok"); } else { privs = 0;
-
SIMPLE_TRACE_LEAVE("%s: Failed (
errcode
:%d)", id->method,
errcode
);
+
SIMPLE_TRACE_LEAVE("%s: Failed (
err
:
%d
: %s
)",
+
id->method,
err_code, strerror(err_code
)
)
;
TRACE_LEAVE("Failure"); if (id->method == "MKCOL") { if (err_code == #if constant(System.ENOENT) System.ENOENT
-
#elif constant(System.ENOENT)
-
System.ENOENT
+
#else 2 #endif ) { return Roxen.http_status(409, "Missing intermediate."); } else { return Roxen.http_status(507, "Failed."); } } return 0;
Roxen.git/server/modules/filesystems/filesystem.pike:1168:
break; case "PUT": if(!query("put")) { id->misc->error_code = 405; TRACE_LEAVE("PUT disallowed"); return 0; }
-
if (FILTER_INTERNAL_FILE (f, id)) {
-
id->misc->error_code = 405;
-
TRACE_LEAVE("PUT of internal file is disallowed");
-
return 0;
-
}
-
+
if (mapping(string:mixed) ret = write_access(f, 0, id)) { TRACE_LEAVE("PUT: Locked"); return ret; }
-
+
if (size == -2) {
+
// RFC 4918 9.7.2:
+
// A PUT request to an existing collection MAY be treated as an
+
// error (405 Method Not Allowed).
+
id->misc->error_code = 405;
+
TRACE_LEAVE("PUT: Is directory.");
+
return 0;
+
}
+
puts++; QUOTA_WERR("Checking quota.\n"); if (id->misc->quota_obj && (id->misc->len > 0) && !id->misc->quota_obj->check_quota(URI, id->misc->len)) { errors++; report_warning(LOCALE(47,"Creation of %O failed. Out of quota.\n"),f); TRACE_LEAVE("PUT: Out of quota."); return Roxen.http_status(507, "Out of disk quota."); } if (query("no_symlinks") && (contains_symlinks(path, f))) { errors++; report_error(LOCALE(46,"Creation of %O failed. Permission denied.\n"),f); TRACE_LEAVE("PUT: Contains symlinks. Permission denied"); return Roxen.http_status(403, "Permission denied."); }
-
+
TRACE_ENTER("PUT: Accepted", 0);
+
SETUID_TRACE("Saving file", 0);
-
rm
(norm_f);
-
mkdirhier
(
norm
_
f
);
+
object to = Stdio.File
(
);
+
if(!to->open(
norm_f
, "wct", 0666
)
)
+
{
+
int err = to->errno()
;
+
privs = 0;
+
TRACE_LEAVE
(
"PUT: Open failed");
+
mixed ret = errno
_
to_status (err, 1, id
);
+
if (ret) return ret;
+
if (id->misc->error_code) {
+
return Roxen.http_status(id->misc->error_code);
+
}
+
return 0;
+
}
if (id->misc->quota_obj) { QUOTA_WERR("Checking if the file already existed."); if (size > 0) { QUOTA_WERR("Deallocating " + size + "bytes."); id->misc->quota_obj->deallocate(URI, size); } }
-
object to = Stdio.File();
-
-
TRACE_ENTER("PUT: Accepted", 0);
-
+
/* Clear the stat-cache for this file */ if (stat_cache) { cache_set("stat_cache", norm_f, 0); }
-
if(!to->open(norm_f, "wct", 0666))
-
{
-
int err = to->errno();
-
privs = 0;
-
TRACE_LEAVE("PUT: Open failed");
-
return errno_to_status (err, 1, id);
-
}
-
+
// FIXME: Race-condition. string msg = safe_chmod(norm_f, 0666 & ~(id->misc->umask || 022)); privs = 0; Stdio.File my_fd = id->connection(); putting[my_fd] = id->misc->len; if(strlen(id->data)) { // Note: What if sizeof(id->data) > id->misc->len ?
Roxen.git/server/modules/filesystems/filesystem.pike:1283:
// Change permission of a file. // FIXME: !! if(!query("put")) { id->misc->error_code = 405; TRACE_LEAVE("CHMOD disallowed (since PUT is disallowed)"); return 0; }
-
if (FILTER_INTERNAL_FILE (f, id)) {
-
id->misc->error_code = 405;
-
TRACE_LEAVE("CHMOD of internal file is disallowed");
-
return 0;
-
}
-
+
if (mapping(string:mixed) ret = write_access(f, 0, id)) { TRACE_LEAVE("CHMOD: Locked"); return ret; } SETUID_TRACE("CHMODing file", 0); if (query("no_symlinks") && (contains_symlinks(path, f))) { privs = 0; errors++;
Roxen.git/server/modules/filesystems/filesystem.pike:1360:
string movefrom; if(!id->misc->move_from || !has_prefix(id->misc->move_from, mountpoint) || !(movefrom = id->conf->real_file(id->misc->move_from, id))) { id->misc->error_code = 405; errors++; TRACE_LEAVE("MV: No source file"); return 0; }
+
#ifdef __NT__
+
foreach(id->misc->move_from/"/", string segment) {
+
if (has_suffix(segment, " ")) {
+
// Path segments on NT may not end with space.
+
return Roxen.http_status(405, "MV: Invalid filesystem path.");
+
}
+
}
+
#endif
+
string relative_from = id->misc->move_from[sizeof(mountpoint)..];
-
if (
FILTER
_
INTERNAL
_
FILE
(
movefrom,
id
)
||
-
FILTER_INTERNAL_FILE
(
f
, id)) {
+
if (
!dotfiles && sizeof(filter(relative
_
from/"/", has
_
prefix,
"."))) {
+
TRACE_LEAVE
(
"From-path
contains .-file or .-directory."
)
;
+
return 0;
+
}
+
if (
FILTER_INTERNAL_FILE(
relative_from
, id)) {
id->misc->error_code = 405;
-
TRACE_LEAVE("MV
to or
from internal file is disallowed");
+
TRACE_LEAVE("MV from internal file is disallowed
.
");
return 0; } if (query("no_symlinks") && ((contains_symlinks(path, f)) || (contains_symlinks(path, id->misc->move_from)))) { errors++; TRACE_LEAVE("MV: Contains symlinks. Permission denied"); return Roxen.http_status(403, "Permission denied."); }
-
//
FIXME
:
What
about
moving of directories containing locked files
?
+
//
NB
:
Consider
the
case of
moving of directories containing locked files
.
if (mapping(string:mixed) ret =
-
write_access(({ f, relative_from }),
0
, id)) {
+
write_access(({ f, relative_from }),
1
, id)) {
TRACE_LEAVE("MV: Locked"); return ret; } SETUID_TRACE("Moving file", 0); code = mv(movefrom, norm_f); int err_code = errno(); privs = 0;
Roxen.git/server/modules/filesystems/filesystem.pike:1436:
} string new_uri = id->misc["new-uri"] || ""; if (new_uri == "") { id->misc->error_code = 405; errors++; TRACE_LEAVE("MOVE: No dest file"); return 0; }
+
#ifdef __NT__
+
foreach(new_uri/"/", string segment) {
+
if (has_suffix(segment, " ")) {
+
// Path segments on NT may not end with space.
+
return Roxen.http_status(405, "MOVE: Invalid filesystem path.");
+
}
+
}
+
#endif
+
// FIXME: The code below doesn't allow for this module being overloaded. if (!has_prefix(new_uri, mountpoint)) { id->misc->error_code = 405; TRACE_LEAVE("MOVE: Dest file on other filesystem."); return(0); } new_uri = new_uri[sizeof(mountpoint)..];
-
string moveto =
path + "/" + encode
_path(new_uri);
+
string moveto =
real
_path(new_uri
, id
);
// Workaround for Linux, Tru64 and FreeBSD. if (has_suffix(moveto, "/")) { moveto = moveto[..sizeof(moveto)-2];
-
+
#if constant(System.normalize_path)
+
} else {
+
// normalize_path() may have adjusted the case of
+
// the destination filename, so restore it.
+
moveto = combine_path(moveto, "..", encode_path(basename(new_uri)));
+
#ifdef __NT__
+
moveto = replace(moveto, "/", "\\");
+
#endif
+
#endif
}
-
if (
FILTER
_
INTERNAL
_
FILE
(
f,
id
)
||
-
FILTER_INTERNAL_FILE (new_uri, id)) {
+
if (
!dotfiles && sizeof(filter(new
_
uri/"/", has
_
prefix,
"."))) {
+
TRACE_LEAVE
(
"Path
contains .-file or .-directory."
)
;
+
return 0;
+
}
+
if (
FILTER_INTERNAL_FILE (new_uri, id)) {
id->misc->error_code = 405;
-
TRACE_LEAVE("MOVE to
or from
internal file is disallowed");
+
TRACE_LEAVE("MOVE to internal file is disallowed");
return 0; } if (query("no_symlinks") && ((contains_symlinks(path, norm_f)) || (contains_symlinks(path, moveto)))) { privs = 0; errors++; TRACE_LEAVE("MOVE: Contains symlinks. Permission denied"); return Roxen.http_status(403, "Permission denied."); }
-
mapping(string:mixed) ret =
-
write_access(({
combine_path(
f,
"../"), f,
new_uri }),
0
, id);
+
// NB: Consider the case of moving of directories containing locked files.
+
mapping(string:mixed) ret = write_access(({ f, new_uri }),
1
, id);
if (ret) { TRACE_LEAVE("MOVE: Locked"); return ret; }
-
+
ret = write_access(combine_path(f, "../"), 0, id);
+
if (ret) {
+
TRACE_LEAVE("MOVE: Parent directory locked");
+
return ret;
+
}
-
size
= _
file
_
size
(
moveto
,
id
);
+
if
(norm_f
=
=
moveto) {
+
privs = 0;
+
errors++;
+
TRACE
_
LEAVE("MOVE: Source and destination are the same path.");
+
return Roxen.http
_
status
(
403
,
"Permission denied."
);
+
}
-
+
size = _file_size(new_uri, id);
+
SETUID_TRACE("Moving file", 0); if (size != -1) { // Destination exists.
-
+
TRACE_ENTER(sprintf("Destination exists: %d\n", size), 0);
int(0..1) overwrite = !id->request_headers->overwrite || id->request_headers->overwrite == "T"; if (!overwrite) { privs = 0;
-
+
TRACE_LEAVE("");
TRACE_LEAVE("MOVE disallowed (overwrite header:F)."); return Roxen.http_status(412); } if(!query("delete")) { privs = 0; id->misc->error_code = 405;
-
+
TRACE_LEAVE("");
TRACE_LEAVE("MOVE disallowed (DELE disabled)"); return 0; }
-
+
TRACE_LEAVE("Overwrite allowed.");
if (overwrite || (size > -1)) {
-
mapping
(
string:mixed
)
res
=
-
recurse_delete_files(new_uri, id);
+
Stdio.Stat src_st = stat_file
(
f, id
)
;
+
Stdio.Stat dst_st
=
stat_file(new_uri, id);
+
//
Check
that src and dst refers to different inodes.
+
// Needed on case insensitive filesystems.
+
if (!is_same_inode(src_st, dst_st)) {
+
TRACE_ENTER(sprintf("Deleting destination: %O...\n", new_uri), 0);
+
mapping(string:mixed) res =
recurse_delete_files(new_uri, id);
if (res && (!sizeof (res) || res->error >= 300)) { privs = 0;
-
+
TRACE_LEAVE("");
TRACE_LEAVE("MOVE: Recursive delete failed."); if (sizeof (res))
-
set_status_for_path
(new_uri, res->error, res->rettext);
+
set_status_for_path(new_uri,
id,
res->error, res->rettext);
return ([]); }
-
+
TRACE_LEAVE("Recursive delete ok.");
+
}
} else { privs = 0; TRACE_LEAVE("MOVE: Cannot overwrite directory"); return Roxen.http_status(412); } }
-
+
TRACE_ENTER(sprintf("MOVE: mv(%O, %O)...\n", norm_f, moveto), 0);
code = mv(norm_f, moveto); int err_code = errno(); privs = 0;
-
+
TRACE_LEAVE(sprintf("==> %d (errno: %d: %s)\n",
+
code, err_code, strerror(err_code)));
TRACE_ENTER("MOVE: Accepted", 0); moves++; /* Clear the stat-cache for this file */ if (stat_cache) { cache_set("stat_cache", new_uri, 0); cache_set("stat_cache", f, 0); } if(!code) { SIMPLE_TRACE_LEAVE("MOVE: Move failed (%s)", strerror (err_code));
-
return
errno_to_status (err_code, 1, id);
+
mixed
ret =
errno_to_status (err_code, 1, id);
+
if (ret) return ret;
+
if (id->misc->error_code) {
+
return Roxen.http_status(id->misc->error_code);
}
-
+
return 0;
+
}
TRACE_LEAVE("MOVE: Success"); TRACE_LEAVE("Success"); if (size != -1) return Roxen.http_status(204); return Roxen.http_status(201); } case "DELETE": if (size==-1) { id->misc->error_code = 404; TRACE_LEAVE("DELETE: Not found"); return 0; } if(!query("delete")) { id->misc->error_code = 405; TRACE_LEAVE("DELETE: Disabled"); return 0; }
-
if (FILTER_INTERNAL_FILE (f, id)) {
-
id->misc->error_code = 405;
-
TRACE_LEAVE("DELETE of internal file is disallowed");
-
return 0;
-
}
-
+
if (query("no_symlinks") && (contains_symlinks(path, f))) { errors++; report_error(LOCALE(48,"Deletion of %s failed. Permission denied.\n"),f); TRACE_LEAVE("DELETE: Contains symlinks"); return Roxen.http_status(403, "Permission denied."); } if ((size < 0) && (String.trim_whites(id->request_headers->depth||"infinity") != "infinity")) {
Roxen.git/server/modules/filesystems/filesystem.pike:1611:
return errno_to_status (errno(), 0, id); } } else { return errno_to_status (errno(), 0, id); } if (id->multi_status_size() > start_ms_size) { TRACE_LEAVE("DELETE: Partial failure."); return ([]); }
+
} else {
+
unlock_path(f, id);
} } else { mapping|int(0..1) res = write_access(({ combine_path(f, "../"), f }), 0, id); if (res) { SIMPLE_TRACE_LEAVE("DELETE: Write access denied."); return res; } #if 0
Roxen.git/server/modules/filesystems/filesystem.pike:1643:
if(!rm(norm_f)) { privs = 0; id->misc->error_code = 405; TRACE_LEAVE("DELETE: Failed"); return 0; } privs = 0; deletes++;
+
unlock_path(f, id);
+
if (id->misc->quota_obj && (size > 0)) { id->misc->quota_obj->deallocate(URI, size); } } TRACE_LEAVE("DELETE: Success"); return Roxen.http_status(204,(norm_f+" DELETED from the server")); default: id->misc->error_code = 501; SIMPLE_TRACE_LEAVE("%s: Not supported", id->method);
Roxen.git/server/modules/filesystems/filesystem.pike:1687:
if (query("no_symlinks") && (contains_symlinks(path, dest))) { errors++; report_error(LOCALE(57,"Copy to %O failed. Permission denied.\n"), dest); TRACE_LEAVE("COPY: Contains symlinks. Permission denied"); return Roxen.http_status(403, "Permission denied."); } Stat dest_st = stat_file(dest, id); if (dest_st) { SIMPLE_TRACE_ENTER (this, "COPY: Destination exists");
+
if (is_same_inode(source_st, dest_st)) {
+
TRACE_LEAVE("Source and destination are the same inode.");
+
TRACE_LEAVE("");
+
return Roxen.http_status(403, "Permission denied.");
+
}
+
if (has_prefix(source, dest)) {
+
TRACE_LEAVE("Destination contains source.");
+
TRACE_LEAVE("");
+
return Roxen.http_status(403, "Permission denied.");
+
}
switch(overwrite) { case NEVER_OVERWRITE: TRACE_LEAVE(""); TRACE_LEAVE(""); return Roxen.http_status(412, "Destination already exists."); case DO_OVERWRITE: if (!query("delete")) { TRACE_LEAVE("COPY: Deletion not allowed."); TRACE_LEAVE(""); return Roxen.http_status(405, "Not allowed.");
Roxen.git/server/modules/filesystems/filesystem.pike:1728:
TRACE_LEAVE(""); return errno_to_status (errno(), 0, id); } if (id->multi_status_size() > start_ms_size) { privs = 0; TRACE_LEAVE("COPY: Partial failure in destination directory delete."); TRACE_LEAVE(""); return ([]); }
+
} else {
+
unlock_path(dest, id);
} SIMPLE_TRACE_LEAVE("COPY: Delete ok."); } else if (source_st->isdir) { if (!rm(dest_path)) { privs = 0; if (errno() != System.ENOENT) { mapping(string:mixed) status = errno_to_status (errno(), 0, id); if (!status) status = (["error": id->misc->error_code]); id->set_status_for_path(mountpoint + dest, status->error, status->rettext); TRACE_LEAVE(""); return ([]); } SIMPLE_TRACE_LEAVE("COPY: File deletion failed (destination disappeared)."); } else { SIMPLE_TRACE_LEAVE("COPY: File deletion ok.");
-
+
+
unlock_path(dest, id);
} } else { SIMPLE_TRACE_LEAVE("COPY: No need to perform deletion."); } privs = 0; break; case MAYBE_OVERWRITE: if ((source_st->isreg != dest_st->isreg) || (source_st->isdir != dest_st->isdir)) { TRACE_LEAVE("COPY: Resource types for source and destination differ.");
Roxen.git/server/modules/filesystems/filesystem.pike:1767:
} else if (source_st->isdir) { TRACE_LEAVE("Already done (both are directories)."); TRACE_LEAVE(""); return Roxen.http_status(204, "Destination already existed."); } break; } } if (source_st->isdir) {
+
if (has_prefix(dest, source)) {
+
TRACE_LEAVE("Source contains destination.");
+
return Roxen.http_status(403, "Permission denied.");
+
}
+
mkdirs++; object privs; SETUID_TRACE("Creating directory/collection", 0); int code = mkdir(dest_path); int err_code = errno(); if (code) { string msg = safe_chmod(dest_path, 0777 & ~(id->misc->umask || 022)); privs = 0;