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

version» Context lines:

Roxen.git/server/modules/filesystems/filesystem.pike:1:   // This is a roxen module. Copyright © 1996 - 2001, Roxen IS.      // This is a virtual "file-system".   // It will be located somewhere in the name-space of the server.   // Also inherited by some of the other filesystems.      inherit "module";   inherit "socket";    - constant cvs_version= "$Id: filesystem.pike,v 1.139 2004/05/13 16:04:47 grubba Exp $"; + constant cvs_version= "$Id: filesystem.pike,v 1.140 2004/05/13 17:39:33 mast Exp $";   constant thread_safe=1;      #include <module.h>   #include <roxen.h>   #include <stat.h>   #include <request_trace.h>         //<locale-token project="mod_filesystem">LOCALE</locale-token>   #define LOCALE(X,Y) _DEF_LOCALE("mod_filesystem",X,Y)
Roxen.git/server/modules/filesystems/filesystem.pike:25:   #else   # define FILESYSTEM_WERR(X)   #endif      #ifdef QUOTA_DEBUG   # define QUOTA_WERR(X) werror("QUOTA: "+X+"\n")   #else   # define QUOTA_WERR(X)   #endif    - #if constant(system.normalize_path) - #define NORMALIZE_PATH(X) system.normalize_path(X) + #if constant(System.normalize_path) + #define NORMALIZE_PATH(X) System.normalize_path(X)   #else /* !constant(system.normalize_path) */   #define NORMALIZE_PATH(X) (X)   #endif /* constant(system.normalize_path) */      constant module_type = MODULE_LOCATION;   LocaleString module_name = LOCALE(51,"File systems: Normal File system");   LocaleString module_doc =   LOCALE(2,"This is the basic file system module that makes it possible "    "to mount a directory structure in the virtual file system of "    "your site.");
Roxen.git/server/modules/filesystems/filesystem.pike:53:   {    mapping res = Roxen.http_low_answer(errno, data);       if (desc) {    res->rettext = desc;    }       return res;   }    + // Note: This does a TRACE_LEAVE. + static mapping(string:mixed) errno_to_status (int err, int(0..1) create, +  RequestID id) + { +  switch (err) { +  case System.ENOENT: +  if (!create) { +  SIMPLE_TRACE_LEAVE ("File not found"); +  id->misc->error_code = Protocols.HTTP.HTTP_NOT_FOUND; +  return 0; +  } +  // Fall through. +  +  case System.ENOTDIR: +  TRACE_LEAVE(sprintf("%s: Missing intermediate.", id->method)); +  id->misc->error_code = Protocols.HTTP.HTTP_CONFLICT; +  return 0; +  +  case System.ENOSPC: +  SIMPLE_TRACE_LEAVE("%s: Insufficient space", id->method); +  return Roxen.http_status (Protocols.HTTP.DAV_STORAGE_FULL); +  +  case System.EPERM: +  TRACE_LEAVE(sprintf("%s: Permission denied", id->method)); +  return Roxen.http_status(Protocols.HTTP.HTTP_FORBIDDEN, +  "Permission denied."); +  +  case System.EEXIST: +  TRACE_LEAVE(sprintf("%s failed. Directory name already exists. ", +  id->method)); +  // FIXME: More methods are probably allowed, but what use is +  // that header anyway? +  return Roxen.http_method_not_allowed("GET, HEAD", +  "Collection already exists."); +  + #if constant(System.ENAMETOOLONG) +  case System.ENAMETOOLONG: +  SIMPLE_TRACE_LEAVE ("Path too long"); +  return Roxen.http_status (Protocols.HTTP.HTTP_URI_TOO_LONG); + #endif +  +  default: +  SIMPLE_TRACE_LEAVE ("Unexpected I/O error: %s", strerror (err)); +  return Roxen.http_status (Protocols.HTTP.HTTP_INTERNAL_ERR, +  "Unexpected I/O error: %s", +  strerror (err)); +  } + } +    static int do_stat = 1;      string status()   {    return "<h2>"+LOCALE(3,"Accesses to this filesystem")+"</h2>"+    (redirects?"<b>"+LOCALE(4,"Redirects")+"</b>: "+redirects+"<br>":"")+    (accesses?"<b>"+LOCALE(5,"Normal files")+"</b>: "+accesses+"<br>"    :LOCALE(6,"No file accesses")+"<br>")+    (query("put")&&puts?"<b>"+LOCALE(7,"PUTs")+"</b>: "+puts+"<br>":"")+    (query("put")&&mkdirs?"<b>"+LOCALE(8,"MKDIRs")+"</b>: "+mkdirs+"<br>":"")+
Roxen.git/server/modules/filesystems/filesystem.pike:542:    [to, from, id, oldf, size] = id_arr;       // Truncate at end.    data = data[..putting[from]];       if (id->misc->quota_obj &&    !id->misc->quota_obj->check_quota(oldf, sizeof(data))) {    to->close();    from->set_blocking();    m_delete(putting, from); -  // FIXME: Ought to be 507 when it's a WebDAV request? -  id->send_result(Roxen.http_status(413, "Out of disk quota.")); +  id->send_result(Roxen.http_status(507, "Out of disk quota."));    return;    }       int bytes = to->write( data );    if (bytes < sizeof(data)) {    // Out of disk!    to->close();    from->set_blocking();    m_delete(putting, from); -  // FIXME: Ought to be 507 when it's a WebDAV request? -  id->send_result(Roxen.http_status(413, "Disk full.")); +  id->send_result(Roxen.http_status(507, "Disk full."));    return;    } else {    if (id->misc->quota_obj &&    !id->misc->quota_obj->allocate(oldf, bytes)) {    to->close();    from->set_blocking();    m_delete(putting, from); -  // FIXME: Ought to be 507 when it's a WebDAV request? -  id->send_result(Roxen.http_status(413, "Out of disk quota.")); +  id->send_result(Roxen.http_status(507, "Out of disk quota."));    return;    }    if (putting[from] != 0x7fffffff) {    putting[from] -= bytes;    }    if(putting[from] <= 0) {    putting[from] = 0; // Paranoia    done_with_put( id_arr );    }    }
Roxen.git/server/modules/filesystems/filesystem.pike:679:    TRACE_ENTER(sprintf("%s: Accepted", id->method), 0);       if (code) {    chmod(norm_f, 0777 & ~(id->misc->umask || 022));    TRACE_LEAVE(sprintf("%s: Success", id->method));    TRACE_LEAVE(sprintf("%s: Success", id->method));    return Roxen.http_status(201, "Created");    }       TRACE_LEAVE(sprintf("%s: Failed", id->method)); -  id->misc->error_code = 507; -  if (err_code == - #if constant(system.ENOENT) -  system.ENOENT - #elif constant(System.ENOENT) -  System.ENOENT - #else -  2 - #endif -  ) { -  TRACE_LEAVE(sprintf("%s: Missing intermediate.", id->method)); -  return Roxen.http_status(409, "Missing intermediate."); -  } else { -  TRACE_LEAVE(sprintf("%s: Failed.", id->method)); +  return errno_to_status (err_code, 1, id);   } -  return 0; - } +       mixed find_file( string f, RequestID id )   {    TRACE_ENTER("find_file(\""+f+"\")", 0);    object o;    int size;    string tmp;    string oldf = f;    object privs;    int code;
Roxen.git/server/modules/filesystems/filesystem.pike:951:    }       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."); -  // FIXME: Ought to be 507 when it's a WebDAV request? -  return Roxen.http_status(413, "Out of disk quota."); +  return Roxen.http_status(507, "Out of disk quota.");    }       if (query("no_symlinks") && (contains_symlinks(path, oldf))) {    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);
Roxen.git/server/modules/filesystems/filesystem.pike:976:       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();    privs = 0;       TRACE_ENTER("PUT: Accepted", 0);       /* Clear the stat-cache for this file */    if (stat_cache) {    cache_set("stat_cache", f, 0);    }       if(!to)    { -  id->misc->error_code = 403; +     TRACE_LEAVE("PUT: Open failed"); -  TRACE_LEAVE("Failure"); -  return 0; +  return errno_to_status (err, 1, id);    }       // FIXME: Race-condition.    chmod(f, 0666 & ~(id->misc->umask || 022));       putting[id->my_fd] = id->misc->len;    if(id->data && strlen(id->data))    {    // FIXME: What if sizeof(id->data) > id->misc->len ?    if (id->misc->len > 0) {    putting[id->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)) {    TRACE_LEAVE("PUT: A string");    TRACE_LEAVE("PUT: Out of quota"); -  // FIXME: Ought to be 507 when it's a WebDAV request? -  return Roxen.http_status(413, "Out of disk quota."); +  return Roxen.http_status(507, "Out of disk quota.");    }    }    }    if(!putting[id->my_fd]) {    TRACE_LEAVE("PUT: Just a string");    TRACE_LEAVE("Put: Success");    if (size < 0) {    return Roxen.http_status(201, "Created.");    } else {    // FIXME: Isn't 204 better? /mast
Roxen.git/server/modules/filesystems/filesystem.pike:1035:    if(id->clientprot == "HTTP/1.1") {    id->my_fd->write("HTTP/1.1 100 Continue\r\n");    }    id->my_fd->set_id( ({ to, id->my_fd, id, URI, size }) );    id->my_fd->set_nonblocking(got_put_data, 0, done_with_put);    TRACE_LEAVE("PUT: Pipe in progress");    TRACE_LEAVE("PUT: Success so far");    return Roxen.http_pipe_in_progress();    break;    -  case "CHMOD": +  case "CHMOD": {    // Change permission of a file.    // FIXME: !!       if(!query("put"))    {    id->misc->error_code = 405;    TRACE_LEAVE("CHMOD disallowed (since PUT is disallowed)");    return 0;    }   
Roxen.git/server/modules/filesystems/filesystem.pike:1067:    SETUID_TRACE("CHMODing file", 0);       if (query("no_symlinks") && (contains_symlinks(path, oldf))) {    privs = 0;    errors++;    TRACE_LEAVE("CHMOD: Contains symlinks. Permission denied");    return Roxen.http_status(403, "Permission denied.");    }       array err = catch(chmod(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);    }       if(err)    { -  id->misc->error_code = 403; +     TRACE_LEAVE("CHMOD: Failure"); -  TRACE_LEAVE("Failure"); -  return 0; +  return errno_to_status (err_code, 0, id);    }    TRACE_LEAVE("CHMOD: Success");    TRACE_LEAVE("Success");    return Roxen.http_string_answer("Ok"); -  +  }    -  case "MV": +  case "MV": {    // This little kluge is used by ftp2 to move files.       // FIXME: Support for quota.       if(!query("put"))    {    id->misc->error_code = 405;    TRACE_LEAVE("MV disallowed (since PUT is disallowed)");    return 0;    }
Roxen.git/server/modules/filesystems/filesystem.pike:1150:    // 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)) {    TRACE_LEAVE("MV: Locked");    return ret;    }       SETUID_TRACE("Moving file", 0);       code = mv(movefrom, 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);    }       if(!code)    { -  id->misc->error_code = 403; +     TRACE_LEAVE("MV: Move failed"); -  TRACE_LEAVE("Failure"); -  return 0; +  return errno_to_status (err_code, 1, id);    }    TRACE_LEAVE("MV: Success");    TRACE_LEAVE("Success");    return Roxen.http_string_answer("Ok"); -  +  }    -  case "MOVE": +  case "MOVE": {    // This little kluge is used by NETSCAPE 4.5 and RFC 2518.       // FIXME: Support for quota.       if(!query("put"))    {    id->misc->error_code = 405;    TRACE_LEAVE("MOVE disallowed (since PUT is disallowed)");    return 0;    }
Roxen.git/server/modules/filesystems/filesystem.pike:1272:    return ([]);    }    } else {    privs = 0;    TRACE_LEAVE("MOVE: Cannot overwrite directory");    return Roxen.http_status(412);    }    }       code = mv(f, decode_path(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", f, 0);    }       if(!code)    { -  id->misc->error_code = 403; -  SIMPLE_TRACE_LEAVE("MOVE: Move failed (errno: %d)", errno()); -  TRACE_LEAVE("Failure"); -  return 0; +  SIMPLE_TRACE_LEAVE("MOVE: Move failed (errno: %d)", err_code); +  return errno_to_status (err_code, 1, id);    }    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;
Roxen.git/server/modules/filesystems/filesystem.pike:1351:    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);    -  if (!rm(f)) { +  if (!rm(f) && errno() != System.ENOENT) {    if (id->multi_status_size() > start_ms_size) { - #if constant(system.EEXIST) -  if (errno() != system.EEXIST) +  if (errno() != System.EEXIST + #if constant (System.ENOTEMPTY) +  && errno() != System.ENOTEMPTY   #endif -  +  )    { -  return Roxen.http_status(403); -  //id->set_status_for_path(query_location() + oldf, 403); +  return errno_to_status (errno(), 0, id);    }    } else { -  TRACE_LEAVE("DELETE: Failed to delete directory."); -  return Roxen.http_status(403, "Failed to delete directory."); +  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))) {    SIMPLE_TRACE_LEAVE("DELETE: Write access denied.");    return res;    }      #if 0    report_notice(LOCALE(49,"DELETING the file %s.\n"),f);
Roxen.git/server/modules/filesystems/filesystem.pike:1434:    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(combine_path(dest, "../"), 0, id);    if (mappingp(res)) return res;    string dest_path = path + dest;    catch { dest_path = decode_path(dest_path); }; +  dest_path = NORMALIZE_PATH (dest_path);    if (query("no_symlinks") && (contains_symlinks(path, dest_path))) {    errors++;    report_error(LOCALE(46,"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) {    case NEVER_OVERWRITE: -  TRACE_LEAVE("COPY: Destination already exists."); +  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.");    }    object privs;    SETUID_TRACE("Deleting destination", 0);    if (dest_st->isdir) { -  +  int start_ms_size = id->multi_status_size();    recursive_rm(dest_path, mountpoint + dest, 1, id); -  if (source_st->isdir) { +  +  werror ("dest_path %O\n", dest_path); +  if (!rm(dest_path) && errno() != System.ENOENT) {    privs = 0; -  SIMPLE_TRACE_LEAVE("COPY: Cleared directory %O", dest_path); -  SIMPLE_TRACE_LEAVE("Copy done."); -  return Roxen.http_status(204); -  } -  if (!rm(dest_path)) { -  privs = 0; -  SIMPLE_TRACE_LEAVE("COPY: Delete failed."); -  // Shouldn't this bail out? /mast - #if constant(system.EEXIST) -  if (errno() != system.EEXIST) +  if (id->multi_status_size() > start_ms_size) { +  if (errno() != System.EEXIST + #if constant (System.ENOTEMPTY) +  && errno() != System.ENOTEMPTY   #endif -  +  )    { -  id->set_status_for_path(mountpoint + dest, 403); +  TRACE_LEAVE(""); +  return errno_to_status (errno(), 0, id);    }    } else { -  SIMPLE_TRACE_LEAVE("COPY: Delete ok."); +  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 ([]); +  } +  } +  SIMPLE_TRACE_LEAVE("COPY: Delete ok.");    } else if (source_st->isdir) {    if (!rm(dest_path)) { -  SIMPLE_TRACE_LEAVE("COPY: File deletion failed."); +     privs = 0; -  // Shouldn't this bail out? /mast - #if constant(system.EEXIST) -  if (errno() != system.EEXIST) - #endif +  if (errno() != System.ENOENT)    { -  id->set_status_for_path(mountpoint + dest, 403); +  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.");    }    } 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."); -  +  TRACE_LEAVE("");    return Roxen.http_status(412, "Destination and source are different resource types.");    } else if (source_st->isdir) {    TRACE_LEAVE("Already done (both are directories)."); -  +  TRACE_LEAVE("");    return Roxen.http_status(204, "Destination already existed.");    }    break;    }    } -  +  else { +  if (res = write_access(dest, 0, id)) { +  SIMPLE_TRACE_LEAVE("COPY: Write access to file %O denied.", dest); +  return res; +  } +  } +     if (source_st->isdir) {    mkdirs++;    object privs;    SETUID_TRACE("Creating directory/collection", 0);       int code = mkdir(dest_path);    int err_code = errno();    privs = 0; -  TRACE_ENTER("COPY: Accepted", this_object()); +        if (code) {    chmod(dest_path, 0777 & ~(id->misc->umask || 022)); -  TRACE_LEAVE("COPY: Success"); +     TRACE_LEAVE("Success");    return Roxen.http_status(dest_st?204:201, "Created");    } else { -  TRACE_LEAVE("COPY: Failed"); -  TRACE_LEAVE("Failure"); -  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 errno_to_status (err_code, 1, id);    } -  } +     } else { -  if (res = write_access(dest, 0, id)) { -  SIMPLE_TRACE_LEAVE("COPY: Write access to file %O denied.", dest); -  return res; -  } +     string source_path = path + source;    catch { source_path = decode_path(source_path); }; -  +  source_path = NORMALIZE_PATH (source_path);    if (query("no_symlinks") && (contains_symlinks(path, source_path))) {    errors++;    report_error(LOCALE(46,"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."); -  // FIXME: Shouldn't this be 507? -  return Roxen.http_status(413, "Out of disk quota."); +  return Roxen.http_status(507, "Out of disk quota.");    }    object source_file = open(source_path, "r");    if (!source_file) {    TRACE_LEAVE("Failed to open source file.");    return Roxen.http_status(404);    }    object privs;    SETUID_TRACE("COPY: Copying file.", 0);    object dest_file = open(dest_path, "cwt");    privs = 0;    if (!dest_file) { -  TRACE_LEAVE("Failed to open destination file."); -  return Roxen.http_status(403); +  return errno_to_status (errno(), 1, id);    }    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) {