Branch: Tag:

2016-03-21

2016-03-21 10:14:39 by Henrik Grubbström (Grubba) <grubba@grubba.org>

Filesystem: Filesystem filename encoding consistency fixes.

Avoid double encoding of filename components.

Fixes remainder of [bug 7659 (#7659)].

262:    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);         
382:    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;
394:    SETUID_NT("Statting file");       /* No security currently in this function */ -  fs = file_stat(encode_path(f)); +  fs = file_stat(f);    privs = 0;    if(!stat_cache) return fs;    cache_set("stat_cache", f, ({fs}));
443:      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(encode_path(f)); +  f = NORMALIZE_PATH(f);    if (has_prefix(f, normalized_path) ||   #ifdef __NT__    (f+"\\" == normalized_path)
538:    SETUID_NT("Read dir");       if (catch { -  f = NORMALIZE_PATH(encode_path(path + f)); +  f = NORMALIZE_PATH(path + encode_path(f));    } || !(dir = get_dir(f))) {    privs = 0;    return 0;
724:   int _file_size(string X, RequestID id)   {    Stat fs; +  X = path + encode_path(X);    if( stat_cache )    {    array(Stat) cached_fs;
734:    return cached_fs[0] ? cached_fs[0][ST_SIZE] : -1;    }    } -  if(fs = file_stat(encode_path(X))) +  if(fs = file_stat(X))    {    id->misc->stat = fs;    if( stat_cache ) cache_set("stat_cache",(X),({fs}));
744:    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(encode_path(root), 1)) { +  if (rr = file_stat(root, 1)) {    if (rr[1] == -3) {    return(1);    }
787:    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. ",
865:    /* 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 = encode_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__
890:    "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) */    };   
957:    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);
1005:    /* FALL_THROUGH */    case "MKDIR":   #if 1 -  return make_collection(oldf, id); +  return make_collection(f, id);   #else /* !1 */    if(!query("put"))    {
1032:    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;    }
1040:    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"),
1089:    }    return 0;    } - #endif /* 1 */ + #endif /* !1 */    break;       case "PUT":
1106:    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;    }
1122:    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");
1131:       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.");
1148:       /* 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->open(f, "wct", 0666)) +  if(!to->open(norm_f, "wct", 0666))    {    int err = to->errno();    privs = 0;
1160:    }       // 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;       putting[id->my_fd] = id->misc->len;
1173:    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.");
1218:    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;   
1299:    }       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");
1307:    }       // FIXME: What about moving of directories containing locked files? -  if (mapping(string:mixed) ret = write_access(oldf, 0, id) || +  if (mapping(string:mixed) ret = write_access(f, 0, id) ||    write_access(relative_from, 0, id)) {    TRACE_LEAVE("MV: Locked");    return ret;
1315:       SETUID_TRACE("Moving file", 0);    -  code = mv(movefrom, f); +  code = mv(movefrom, norm_f);    int err_code = errno();    privs = 0;   
1326:    /* 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)
1372:    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, "/")) {
1380:    }       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++;
1397:       if (mapping(string:mixed) ret =    write_access(new_uri, 0, id) || -  write_access(oldf, 0, id)) { +  write_access(f, 0, id)) {    TRACE_LEAVE("MOVE: Locked");    return ret;    }
1442:    }    }    -  code = mv(f, encode_path(moveto)); +  code = mv(norm_f, moveto);    int err_code = errno();    privs = 0;   
1452:       /* 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);    }   
1486:    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");
1506:       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)))) { +  if (mappingp(res = write_access(combine_path(f, "../"), 1, id)) || +  (res && mappingp(res = write_access(f, 1, id)))) {    SIMPLE_TRACE_LEAVE("DELETE: Recursive write access denied.");    return res;    }
1520:    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)
1543:    }    } else {    mapping|int(0..1) res; -  if ((res = write_access(combine_path(oldf, "../"), 0, id)) || -  (res = write_access(oldf, 0, id))) { +  if ((res = write_access(combine_path(f, "../"), 0, id)) || +  (res = write_access(f, 0, id))) {    SIMPLE_TRACE_LEAVE("DELETE: Write access denied.");    return res;    }
1562:       SETUID_TRACE("Deleting file", 0);    -  if(!rm(f)) +  if(!rm(norm_f))    {    privs = 0;    id->misc->error_code = 405;
1573:    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;
1604:    mapping|int(0..1) res = write_access(dest, 0, id) ||    write_access(combine_path(dest, "../"), 0, id);    if (mappingp(res)) return res; -  string dest_path = path + dest; -  catch { dest_path = encode_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);
1717:    return errno_to_status (err_code, 1, id);    }    } else { -  string source_path = path + source; -  catch { source_path = encode_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);