Roxen.git / server / modules / filesystems / filesystem.pike

version» Context lines:

Roxen.git/server/modules/filesystems/filesystem.pike:255:   {    tilde = query("tilde");    charset = query("charset");    path_encoding = query("path_encoding");    no_symlinks = query("no_symlinks");    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 = query("searchpath"); +  path = encode_path(query("searchpath"));    mountpoint = query("mountpoint");    stat_cache = query("stat_cache"); -  internal_files = query("internal_files"); +  internal_files = map(query("internal_files"), encode_path);            #if constant(System.normalize_path)    if (catch {    if ((<'/','\\'>)[path[-1]]) {    normalized_path = System.normalize_path(path + ".");    } else {    normalized_path = System.normalize_path(path);    }
Roxen.git/server/modules/filesystems/filesystem.pike:375:    privs=Privs(X, uid->uid(), uid->gid() ); \    }      mixed stat_file( string f, RequestID id )   {    Stat fs;       FILESYSTEM_WERR("stat_file for \""+f+"\"" +    (id->misc->internal_get ? " (internal)" : ""));    -  f = path+f; +  f = path + encode_path(f);       if (FILTER_INTERNAL_FILE (f, id))    return 0;       if(stat_cache && !id->pragma["no-cache"] &&    (fs=cache_lookup("stat_cache",f)))    return fs[0];    object privs;    SETUID_NT("Statting file");       /* No security currently in this function */ -  fs = file_stat(decode_path(f)); +  fs = file_stat(f);    privs = 0;    if(!stat_cache) return fs;    cache_set("stat_cache", f, ({fs}));    return fs;   }    - string decode_path( string p ) + //! 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 = Locale.Charset.encoder( path_encoding )->feed( p )->drain();   #ifndef __NT__    if( String.width( p ) != 8 )    p = string_to_utf8( p );   #else    while( strlen(p) && p[-1] == '/' )    p = p[..strlen(p)-2];   #endif    return p;   }    -  + //! Convert from filesystem encoding. + string decode_path(string p) + { + #ifdef __NT__ +  // 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__ */ + } +    string real_path(string f, RequestID id)   { -  f = normalized_path + f; +  f = normalized_path + encode_path(f);    if (FILTER_INTERNAL_FILE(f, id)) return 0;    catch { -  f = NORMALIZE_PATH(decode_path(f)); +  f = NORMALIZE_PATH(f);    if (has_prefix(f, normalized_path) ||   #ifdef __NT__    (f+"\\" == normalized_path)   #else /* !__NT__ */    (f+"/" == normalized_path)   #endif /* __NT__ */    ) {    return f;    }    };
Roxen.git/server/modules/filesystems/filesystem.pike:446:   {    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(path, lock, id); +  register_lock(encode_path(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(path, lock, id); +  unregister_lock(encode_path(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;   }      array(string) list_lock_files() {    return query("nobrowse");   }    - protected mapping(string:mixed)|int(0..1) write_access(string path, + protected variant mapping(string:mixed)|int(0..1) write_access(string path,    int(0..1) recursive,    RequestID id)   {    SIMPLE_TRACE_ENTER(this, "write_access(%O, %O, %O)\n", path, recursive, id);    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(path, recursive, id); +  return ::write_access(encode_path(path), recursive, id);   }      array find_dir( string f, RequestID id )   {    array dir;       FILESYSTEM_WERR("find_dir for \""+f+"\"" +    (id->misc->internal_get ? " (internal)" : ""));       object privs;    SETUID_NT("Read dir");       if (catch { -  f = NORMALIZE_PATH(decode_path(path + f)); +  f = NORMALIZE_PATH(path + encode_path(f));    } || !(dir = get_dir(f))) {    privs = 0;    return 0;    }    privs = 0;       if(!query("dir"))    // Access to this dir is allowed.    if(! has_value(dir, ".www_browsable"))    {
Roxen.git/server/modules/filesystems/filesystem.pike:536:       dirlists++;       // Pass _all_ files, hide none.    if(tilde && dotfiles &&    (!sizeof( internal_files ) || id->misc->internal_get))    return dir;       dir = Array.filter(dir, dir_filter_function, id);    +  if (path_encoding != "iso-8859-1") { +  dir = map(dir, decode_path); +  } +     if (!id->misc->internal_get)    foreach (internal_files, string globstr)    dir -= glob (globstr, dir);       return dir;   }      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 + "/" + fname; +  string virt_fname = virt_dir + "/" + decode_path(fname);       Stat stat = file_stat(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);    int(0..1)|mapping sub_status;
Roxen.git/server/modules/filesystems/filesystem.pike:687:    if(putting[from] <= 0) {    putting[from] = 0; // Paranoia    done_with_put( id_arr );    }    }   }      int _file_size(string X, RequestID id)   {    Stat fs; +  X = path + encode_path(X);    if( stat_cache )    {    array(Stat) cached_fs;    if(!id->pragma["no-cache"] &&    (cached_fs = cache_lookup("stat_cache", X)))    {    id->misc->stat = cached_fs[0];    return cached_fs[0] ? cached_fs[0][ST_SIZE] : -1;    }    } -  if(fs = file_stat(decode_path(X))) +  if(fs = file_stat(X))    {    id->misc->stat = fs;    if( stat_cache ) cache_set("stat_cache",(X),({fs}));    return fs[ST_SIZE];    } else if( stat_cache )    cache_set("stat_cache",(X),({0}));    return -1;   }    -  + //! Return @expr{1@} if the (virtual) @[path] from + //! the (real) @[root] follows symbolic links.   int contains_symlinks(string root, string path)   { -  +  if (has_suffix(root, "/")) { +  root = root[..<1]; +  }    foreach(path/"/" - ({ "" }), path) { -  root += "/" + path; +  root += "/" + encode_path(path);    Stat rr; -  if (rr = file_stat(decode_path(root), 1)) { +  if (rr = file_stat(root, 1)) {    if (rr[1] == -3) {    return(1);    }    } else {    return(0);    }    }    return(0);   }   
Roxen.git/server/modules/filesystems/filesystem.pike:750:    return Roxen.http_status(405, "Bad path.");    }       if(!query("put"))    {    TRACE_LEAVE(sprintf("%s disallowed (since PUT is disallowed)",    id->method));    return Roxen.http_status(405, "Disallowed.");    }    -  // FIXME: Is this the correct filename? -  int size = _file_size(norm_f, id); +  int size = _file_size(coll, id);       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;    }       // Disallow if the name is locked, or if the parent directory is locked. -  mapping(string:mixed) ret = write_access(coll, 0, id) || -  write_access(combine_path(coll, ".."), 0, id); +  mapping(string:mixed) ret = +  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:828:    string oldf = f;    object privs;    int code;       FILESYSTEM_WERR("Request for \""+f+"\"" +    (id->misc->internal_get ? " (internal)" : ""));       /* only used for the quota system, thus rather unessesary to do for    each request....    */ - #define URI combine_path(mountpoint + "/" + oldf, ".") + #define URI combine_path(mountpoint, f, ".")       string norm_f;       catch {    /* NOTE: NORMALIZE_PATH() may throw errors. */ -  f = norm_f = NORMALIZE_PATH(f = decode_path(path + f)); +  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__ */    ) {    errors++;    report_error(LOCALE(52, "Path verification of %O failed:\n"    "%O is not a prefix of %O\n"    ), oldf, normalized_path, norm_f);    TRACE_LEAVE("");    TRACE_LEAVE("Permission denied.");    return Roxen.http_status(403, "File exists, but access forbidden "    "by user");    }    -  /* Adjust not_query */ -  id->not_query = mountpoint + replace(norm_f[sizeof(normalized_path)..], -  "\\", "/"); -  if (sizeof(oldf) && (oldf[-1] == '/')) { -  id->not_query += "/"; +  // Regenerate f from norm_f. +  f = decode_path(replace(norm_f[sizeof(normalized_path)..], "\\", "/")) +  if (has_suffix(oldf, "/") && !has_suffix(f, "/")) { +  // Restore the "/" stripped by encode_path() on NT. +  f += "/";    } -  +  +  /* Adjust not_query */ +  id->not_query = mountpoint + f;   #endif /* constant(System.normalize_path) */    };       // NOTE: Sets id->misc->stat.    size = _file_size( f, id );       FILESYSTEM_WERR(sprintf("_file_size(%O, %O) ==> %d\n", f, id, size));       /*    * FIXME: Should probably move path-info extraction here.
Roxen.git/server/modules/filesystems/filesystem.pike:920:    }       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, oldf)))) +  if(!o || (no_symlinks && (contains_symlinks(path, f))))    {    errors++;    report_error(LOCALE(45,"Open of %s failed. Permission denied.\n"),f);       TRACE_LEAVE("");    TRACE_LEAVE("Permission denied.");    return Roxen.http_status(403, "File exists, but access forbidden "    "by user");    }   
Roxen.git/server/modules/filesystems/filesystem.pike:968:    // 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(oldf, id); +  return make_collection(f, id);   #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)) {
Roxen.git/server/modules/filesystems/filesystem.pike:995:    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(oldf, 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, oldf))) { +  if (query("no_symlinks") && (contains_symlinks(path, f))) {    privs = 0;    errors++;    report_error(LOCALE(46,"Creation of %O failed. Permission denied.\n"),    oldf);    TRACE_LEAVE(sprintf("%s: Contains symlinks. Permission denied",    id->method));    return Roxen.http_status(403, "Permission denied.");    }       code = mkdir(f);
Roxen.git/server/modules/filesystems/filesystem.pike:1052:    2   #endif    ) {    return Roxen.http_status(409, "Missing intermediate.");    } else {    return Roxen.http_status(507, "Failed.");    }    }    return 0;    } - #endif /* 1 */ + #endif /* !1 */    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(oldf, 0, id)) { +  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.");
Roxen.git/server/modules/filesystems/filesystem.pike:1094:       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, oldf))) { +  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.");    }       SETUID_TRACE("Saving file", 0);    -  rm(f); -  mkdirhier(f); +  rm(norm_f); +  mkdirhier(norm_f);       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 = open(f, "wct"); -  int err = errno(); +  object to = Stdio.File();       TRACE_ENTER("PUT: Accepted", 0);       /* Clear the stat-cache for this file */    if (stat_cache) { -  cache_set("stat_cache", f, 0); +  cache_set("stat_cache", norm_f, 0);    }    -  if(!to) +  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(f, 0666 & ~(id->misc->umask || 022)); +  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 ?    // This is not a problem, since that has been handled    // by the protocol module.    if (id->misc->len > 0) {    putting[my_fd] -= strlen(id->data);    }    int bytes = to->write( id->data );    if (id->misc->quota_obj) {    QUOTA_WERR("Allocating " + bytes + "bytes."); -  if (!id->misc->quota_obj->allocate(f, bytes)) { +  if (!id->misc->quota_obj->allocate(URI, bytes)) {    TRACE_LEAVE("PUT: A string");    TRACE_LEAVE("PUT: Out of quota");    return Roxen.http_status(507, "Out of disk quota.");    }    }    }    if(!putting[my_fd]) {    TRACE_LEAVE("PUT: Just a string");    TRACE_LEAVE("Put: Success");    if (size < 0) {
Roxen.git/server/modules/filesystems/filesystem.pike:1192:    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(oldf, 0, id)) { +  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, oldf))) { +  if (query("no_symlinks") && (contains_symlinks(path, f))) {    privs = 0;    errors++;    TRACE_LEAVE("CHMOD: Contains symlinks. Permission denied");    return Roxen.http_status(403, "Permission denied.");    }    -  string msg = safe_chmod(f, id->misc->mode & 0777); +  string msg = safe_chmod(norm_f, id->misc->mode & 0777);    int err_code = errno();    privs = 0;       chmods++;       TRACE_ENTER("CHMOD: Accepted", 0);       if (stat_cache) {    cache_set("stat_cache", f, 0);    }
Roxen.git/server/modules/filesystems/filesystem.pike:1273:    string relative_from = id->misc->move_from[sizeof(mountpoint)..];       if (FILTER_INTERNAL_FILE (movefrom, id) ||    FILTER_INTERNAL_FILE (f, id)) {    id->misc->error_code = 405;    TRACE_LEAVE("MV to or from internal file is disallowed");    return 0;    }       if (query("no_symlinks") && -  ((contains_symlinks(path, oldf)) || +  ((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? -  if (mapping(string:mixed) ret = write_access(oldf, 0, id) || -  write_access(relative_from, 0, id)) { +  if (mapping(string:mixed) ret = +  write_access(({ f, relative_from }), 0, id)) {    TRACE_LEAVE("MV: Locked");    return ret;    }       SETUID_TRACE("Moving file", 0);    -  code = mv(movefrom, f); +  code = mv(movefrom, norm_f);    int err_code = errno();    privs = 0;       moves++;       TRACE_ENTER("MV: Accepted", 0);       /* Clear the stat-cache for this file */    if (stat_cache) {    cache_set("stat_cache", movefrom, 0); -  cache_set("stat_cache", f, 0); +  cache_set("stat_cache", norm_f, 0);    }       if(!code)    {    TRACE_LEAVE("MV: Move failed");    return errno_to_status (err_code, 1, id);    }    TRACE_LEAVE("MV: Success");    TRACE_LEAVE("Success");    return Roxen.http_string_answer("Ok");
Roxen.git/server/modules/filesystems/filesystem.pike:1346:    return 0;    }       // 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 + "/" + new_uri; +  string moveto = path + "/" + encode_path(new_uri);       // Workaround for Linux, Tru64 and FreeBSD.    if (has_suffix(moveto, "/")) {    moveto = moveto[..sizeof(moveto)-2];    }       if (FILTER_INTERNAL_FILE (f, id) || -  FILTER_INTERNAL_FILE (moveto, id)) { +  FILTER_INTERNAL_FILE (new_uri, id)) {    id->misc->error_code = 405;    TRACE_LEAVE("MOVE to or from internal file is disallowed");    return 0;    }       if (query("no_symlinks") && -  ((contains_symlinks(path, f)) || +  ((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.");    }    -  if (mapping(string:mixed) ret = -  write_access(new_uri, 0, id) || -  write_access(oldf, 0, id)) { +  mapping(string:mixed) ret = +  write_access(({ combine_path(f, "../"), f, new_uri }), 0, id); +  if (ret) {    TRACE_LEAVE("MOVE: Locked");    return ret;    }       size = _file_size(moveto,id);       SETUID_TRACE("Moving file", 0);       if (size != -1) {    // Destination exists.
Roxen.git/server/modules/filesystems/filesystem.pike:1416:    set_status_for_path (new_uri, res->error, res->rettext);    return ([]);    }    } else {    privs = 0;    TRACE_LEAVE("MOVE: Cannot overwrite directory");    return Roxen.http_status(412);    }    }    -  code = mv(f, decode_path(moveto)); +  code = mv(norm_f, moveto);    int err_code = errno();    privs = 0;       TRACE_ENTER("MOVE: Accepted", 0);       moves++;       /* Clear the stat-cache for this file */    if (stat_cache) { -  cache_set("stat_cache", moveto, 0); +  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);    }    TRACE_LEAVE("MOVE: Success");    TRACE_LEAVE("Success");
Roxen.git/server/modules/filesystems/filesystem.pike:1460:    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, oldf))) { +  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")) {    // RFC 2518 8.6.2:    // The DELETE method on a collection MUST act as if a "Depth: infinity"    // header was used on it.    TRACE_LEAVE(sprintf("DELETE: Bad depth header: %O.",    id->request_headers->depth));    return Roxen.http_status(400, "Unsupported depth.");    }       if (size < 0) { -  mapping|int(0..1) res; -  if (mappingp(res = write_access(combine_path(oldf, "../"), 1, id)) || -  (res && mappingp(res = write_access(oldf, 1, id)))) { +  mapping|int(0..1) res = +  write_access(({ combine_path(f, "../"), f }), 1, id); +  if (mappingp(res)) {    SIMPLE_TRACE_LEAVE("DELETE: Recursive write access denied.");    return res;    }   #if 0    report_notice(LOCALE(64,"DELETING the directory %s.\n"), f);   #endif       accesses++;       SETUID_TRACE("Deleting directory", 0);       int start_ms_size = id->multi_status_size(); -  recursive_rm(f, query_location() + oldf, res, id); +  recursive_rm(norm_f, query_location() + f, res, id);    -  if (!rm(f) && errno() != System.ENOENT) { +  if (!rm(norm_f) && errno() != System.ENOENT) {    if (id->multi_status_size() > start_ms_size) {    if (errno() != System.EEXIST   #if constant (System.ENOTEMPTY)    && errno() != System.ENOTEMPTY   #endif    )    {    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 { -  mapping|int(0..1) res; -  if ((res = write_access(combine_path(oldf, "../"), 0, id)) || -  (res = write_access(oldf, 0, id))) { +  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    report_notice(LOCALE(49,"DELETING the file %s.\n"),f);   #endif       accesses++;       /* Clear the stat-cache for this file */    if (stat_cache) {    cache_set("stat_cache", f, 0);    }       SETUID_TRACE("Deleting file", 0);    -  if(!rm(f)) +  if(!rm(norm_f))    {    privs = 0;    id->misc->error_code = 405;    TRACE_LEAVE("DELETE: Failed");    return 0;    }    privs = 0;    deletes++;       if (id->misc->quota_obj && (size > 0)) { -  id->misc->quota_obj->deallocate(oldf, size); +  id->misc->quota_obj->deallocate(URI, size);    }    }    TRACE_LEAVE("DELETE: Success"); -  return Roxen.http_status(204,(f+" DELETED from the server")); +  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);    return 0;    }    TRACE_LEAVE("Not reached");    return 0;   }   
Roxen.git/server/modules/filesystems/filesystem.pike:1575:    SIMPLE_TRACE_ENTER(this, "COPY: Copy %O to %O.", source, dest);    Stat source_st = stat_file(source, id);    if (!source_st) {    TRACE_LEAVE("COPY: Source doesn't exist.");    return Roxen.http_status(404, "File not found.");    }    if (!query("put")) {    TRACE_LEAVE("COPY: Put not allowed.");    return Roxen.http_status(405, "Not allowed.");    } -  mapping|int(0..1) res = write_access(dest, 0, id) || -  write_access(combine_path(dest, "../"), 0, id); +  mapping|int(0..1) res = +  write_access(({ dest, combine_path(dest, "../")}) , 0, id);    if (mappingp(res)) return res; -  string dest_path = path + dest; -  catch { dest_path = decode_path(dest_path); }; +  string dest_path = path + encode_path(dest);    dest_path = NORMALIZE_PATH (dest_path); -  if (query("no_symlinks") && (contains_symlinks(path, dest_path))) { +  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");    switch(overwrite) {
Roxen.git/server/modules/filesystems/filesystem.pike:1691:    if (msg) {    TRACE_LEAVE(sprintf("Chmod %O failed: %s", dest_path, msg));    } else {    TRACE_LEAVE("Success");    }    return Roxen.http_status(dest_st?204:201, "Created");    } else {    return errno_to_status (err_code, 1, id);    }    } else { -  string source_path = path + source; -  catch { source_path = decode_path(source_path); }; +  string source_path = path + encode_path(source);    source_path = NORMALIZE_PATH (source_path); -  if (query("no_symlinks") && (contains_symlinks(path, source_path))) { +  if (query("no_symlinks") && (contains_symlinks(path, source))) {    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.");    }    puts++;       QUOTA_WERR("Checking quota.\n");    if (id->misc->quota_obj && (id->misc->len > 0) &&    !id->misc->quota_obj->check_quota(mountpoint + dest,    source_st->size)) {    errors++;    report_warning(LOCALE(47,"Creation of %O failed. Out of quota.\n"),    dest_path);    TRACE_LEAVE("PUT: Out of quota.");    return Roxen.http_status(507, "Out of disk quota.");    } -  object source_file = open(source_path, "r"); -  if (!source_file) { +  object source_file = Stdio.File(); +  if (!source_file->open(source_path, "r")) {    TRACE_LEAVE("Failed to open source file.");    return Roxen.http_status(404);    }    // Workaround for Linux, Tru64 and FreeBSD.    if (has_suffix(dest_path, "/")) {    dest_path = dest_path[..sizeof(dest_path)-2];    }    object privs;    SETUID_TRACE("COPY: Copying file.", 0); -  object dest_file = open(dest_path, "cwt"); +  object dest_file = Stdio.File(); +  if (!dest_file->open(dest_path, "cwt")) {    privs = 0; -  if (!dest_file) { +     return errno_to_status (errno(), 1, id);    } -  +  privs = 0;    int len = source_st->size;    while (len > 0) {    string buf = source_file->read((len > 4096)?4096:len);    if (buf && sizeof(buf)) {    int sub_len;    len -= (sub_len = sizeof(buf));    while (sub_len > 0) {    int written = dest_file->write(buf);    if ((sub_len -= written) > 0) {    if (!written) {