added: now we have a fourth part in permissions (guests)

e.g.: 07555 means:
           7 for owner
           5 for group
           5 for others
           5 for guests (not logged users)
added:     the sticky bit for directories
           e.g. permissions to a directory with a sticky bit set
           can be set to: 017555
rewritten: rm/mv winix functions to correctly understand the sticky bit
added:     Dir::FollowLink() recognizes ".." and "." now
           consequently System::FollowAllLinks recognizes it too
added:     umask -- calculating privileges for new files/directories
           all users have their own umask (in meta)
           and there is one in the config
           (for guests and when a user has not definied its own one)
removed:   mount option: only_root_remove



git-svn-id: svn://ttmath.org/publicrep/winix/trunk@801 e52654a7-88a9-db11-a3e9-0013d4bc506e
This commit is contained in:
2012-01-24 23:03:36 +00:00
parent 5aaab89cd8
commit 6e2d00bc5b
34 changed files with 1109 additions and 557 deletions

View File

@@ -2,13 +2,12 @@
* This file is a part of Winix
* and is not publicly distributed
*
* Copyright (c) 2008-2010, Tomasz Sowa
* Copyright (c) 2008-2012, Tomasz Sowa
* All rights reserved.
*
*/
#include <cstdio>
#include <errno.h>
#include "rm.h"
#include "core/plugin.h"
#include "core/misc.h"
@@ -27,49 +26,70 @@ Rm::Rm()
bool Rm::HasAccess(const Item & item)
bool Rm::HasAccessToDir(const Item & dir, bool only_content)
{
// !! temporarily (we're waiting for the sticky bit to be implemented)
// not logged users cannot remove anything
if( !cur->session->puser )
return false;
if( item.parent_id == -1 )
if( dir.parent_id == -1 )
{
// this is a root directory
// we can only remove the content of the root directory
// and here we check only access the the root dir
// "onlycontent" parameter should be check in post method
if( !system->HasWriteAccess(item) )
// sticky bit for a specified child will be checked later
if( !only_content || !system->HasWriteAccess(dir) )
return false;
}
else
if( only_content )
{
// sticky bit for a specified child will be checked later
if( !system->HasWriteAccess(dir) )
return false;
}
else
{
Item * last_but_one_dir = system->dirs.GetDir(item.parent_id);
Item * last_but_one_dir = system->dirs.GetDir(dir.parent_id);
if( !last_but_one_dir )
// ops, there is no a parent dir
return false;
if( !system->HasWriteAccess(*last_but_one_dir) )
if( !system->CanRemoveRenameChild(*last_but_one_dir, dir.user_id) )
return false;
}
if( system->mounts.pmount->IsPar(system->mounts.MountParOnlyRootRemove()) )
if( !cur->session->puser || !cur->session->puser->super_user )
return false;
return true;
}
/*
here we are making a little test -- this method returns false if
we are sure that the permissions is not allowed (consequently the html form
will not be displayed)
the correct checking for permissions is done later in MakePost() method
parameter 'c' to rm function means removing only the content of a directory
this parameter can be sent either by a get or a post variable
*/
bool Rm::HasAccess()
{
bool res;
if( !cur->request->is_item )
res = HasAccess(*cur->request->dir_tab.back());
if( cur->request->is_item )
{
res = system->CanRemoveRenameChild(*cur->request->dir_tab.back(), cur->request->item.user_id);
}
else
res = HasAccess(cur->request->item);
{
if( cur->request->IsParam(L"r") )
{
bool only_content = (cur->request->IsParam(L"c") || cur->request->IsPostVar(L"c"));
res = HasAccessToDir(*cur->request->dir_tab.back(), only_content);
}
else
{
log << log3 << "Rm: directories can be removed only with 'r' parameter" << logend;
slog << logerror << T("rm_use_r_option") << logend;
}
}
if( !res && cur->request->IsParam(L"jquery_upload") )
CreateJSON(res);
@@ -83,66 +103,70 @@ void Rm::Prepare()
{
// selecting files and symlinks (without directories)
content_dir_iq.SetAll(false, false);
content_dir_iq.sel_parent_id = true;
content_dir_iq.sel_type = true;
content_dir_iq.sel_url = true;
content_dir_iq.sel_file = true;
content_dir_iq.sel_parent_id = true;
content_dir_iq.sel_type = true;
content_dir_iq.sel_url = true;
content_dir_iq.sel_file = true;
content_dir_iq.sel_user_id = true;
content_dir_iq.sel_group_id = true;
content_dir_iq.sel_privileges = true;
content_dir_iq.sel_meta = true;
content_dir_iq.WhereType(Item::dir, false);
// selecting files, symlinks and directories
content_dir_iq2.SetAll(false, false);
content_dir_iq2.sel_parent_id = true;
content_dir_iq2.sel_type = true;
content_dir_iq2.sel_url = true;
content_dir_iq2.sel_file = true;
}
bool Rm::RemoveStaticFile(const std::wstring & path)
void Rm::RemoveStaticFile(const std::wstring & path)
{
if( ::RemoveFile(path) )
{
log << log2 << "Rm: removed static file: " << path << logend;
return true;
log << log2 << "Rm: static file removed: " << path << logend;
}
else
{
log << log1 << "Rm: can't remove a file: " << path << logend;
cur->request->status = WINIX_ERR_PERMISSION_DENIED;
return false;
log << log1 << "Rm: I can't remove a static file: " << path << logend;
slog << logerror << T("rm_cannot_remove_static_file") << ": " << path << logend;
}
}
void Rm::RemoveStaticFile(Item & item)
void Rm::RemoveStaticFile(const Item & item)
{
if( system->MakeFilePath(item, path, false) )
{
if( RemoveStaticFile(path) )
{
if( item.has_thumb && system->MakeFilePath(item, path, true) )
RemoveStaticFile(path);
RemoveStaticFile(path);
// we don't change item.file_path and others file_* variables
// they can be used by a plugin
}
if( item.has_thumb && system->MakeFilePath(item, path, true) )
RemoveStaticFile(path);
}
else
{
cur->request->status = WINIX_ERR_PERMISSION_DENIED;
log << log1 << "Rm: I cannot create a path to a static file, url: "
<< item.url << ", id: " << item.id << logend;
slog << logerror << T("rm_cannot_create_static_path") << ": " << item.url << logend;
}
}
void Rm::RemoveFileOrSymlink(Item & item)
/*
!! IMPROVE ME:
plugin.Call(WINIX_FILE_REMOVED, &item) is getting its parameter as non-const
in the future we can add a pointer cp1, cp2 (in plugins) but pointing to a const object
and this bool Rm::RemoveFile(Item & item) can became bool Rm::RemoveFile(const Item & item)
*/
bool Rm::RemoveFile(Item & item)
{
plugin.Call(WINIX_FILE_PREPARE_TO_REMOVE, &item);
if( db->DelItem(item) == WINIX_ERR_OK )
{
if( item.type == Item::file )
log << log2 << "Rm: deleted file ";
log << log2 << "Rm: deleted file: ";
else
log << log2 << "Rm: deleted symlink ";
log << log2 << "Rm: deleted symlink: ";
log << item.url << logend;
@@ -152,121 +176,209 @@ void Rm::RemoveFileOrSymlink(Item & item)
RemoveStaticFile(item);
plugin.Call(WINIX_FILE_REMOVED, &item);
return true;
}
return false;
}
/*
we do not modify item
it is non-const because we use plugin.Call() later with a pointer to this structure
(and we want to avoid casting)
*/
bool Rm::RemoveFileOrSymlink(Item & item, bool check_access)
{
if( item.type == Item::dir )
return false;
if( check_access )
{
Item * dir = system->dirs.GetDir(item.parent_id);
// if there is not 'dir' directory then we can simply remove 'item'
if( dir )
{
if( !system->CanRemoveRenameChild(*dir, item.user_id) )
{
log << log1 << "Rm: permission denied to remove: " << item.url << ", id: " << item.id << logend;
slog << logerror << T("rm_permission_denied_to") << ": " << item.url << logend;
return false;
}
}
}
return RemoveFile(item);
}
// for other uses (plugins etc)
void Rm::RemoveItemById(long item_id)
void Rm::RemoveItemById(long item_id, bool check_access)
{
// selecting files, symlinks and directories
rm_by_id_iq.SetAll(false, false);
rm_by_id_iq.sel_parent_id = true;
rm_by_id_iq.sel_type = true;
rm_by_id_iq.sel_url = true;
rm_by_id_iq.sel_file = true;
rm_by_id_iq.sel_parent_id = true;
rm_by_id_iq.sel_type = true;
rm_by_id_iq.sel_url = true;
rm_by_id_iq.sel_file = true;
rm_by_id_iq.sel_user_id = true;
rm_by_id_iq.sel_group_id = true;
rm_by_id_iq.sel_privileges = true;
rm_by_id_iq.sel_meta = true;
rm_by_id_iq.WhereId(item_id);
if( db->GetItem(rm_by_id_item, rm_by_id_iq) == WINIX_ERR_OK )
{
if( rm_by_id_item.type == Item::dir )
RemoveDir(rm_by_id_item);
RemoveDirTree(rm_by_id_item, true, check_access);
else
RemoveFileOrSymlink(rm_by_id_item);
RemoveFileOrSymlink(rm_by_id_item, check_access);
}
}
void Rm::RemoveDirTree(long dir_id)
bool Rm::RemoveDirFiles(long dir_id, bool check_access)
{
DirContainer::ParentIterator pnext, p = system->dirs.FindFirstChild(dir_id);
for( ; p != system->dirs.ParentEnd() ; p = pnext )
{
plugin.Call(WINIX_DIR_PREPARE_TO_REMOVE, &(*p->second));
// this iterator p will be deleted by the next DeleteDir(p->second->id)
// (the next iterator we must calculate beforehand)
pnext = system->dirs.NextChild(p);
RemoveDirTree(p->second->id);
}
content_dir_iq.WhereParentId(dir_id);
db->GetItems(content_item_tab, content_dir_iq);
for(size_t i=0 ; i<content_item_tab.size() ; ++i)
RemoveFileOrSymlink(content_item_tab[i]);
size_t removed = 0;
if( db->DelDirById(dir_id) == WINIX_ERR_OK )
for(size_t i=0 ; i<content_item_tab.size() ; ++i)
if( RemoveFileOrSymlink(content_item_tab[i], check_access) )
removed += 1;
return removed == content_item_tab.size();
}
/*
parent_dir can be null (when current_dir is the root directory)
*/
void Rm::RemoveCurrentDir(Item * parent_dir, Item * current_dir, bool check_access)
{
if( check_access )
{
if( !parent_dir || !system->CanRemoveRenameChild(*parent_dir, current_dir->user_id) )
{
log << log1 << "Rm: permission denied to directory: " << current_dir->url << logend;
slog << logerror << T("rm_permission_denied_to") << ": " << current_dir->url << logend;
return;
}
}
plugin.Call(WINIX_DIR_PREPARE_TO_REMOVE, current_dir);
if( db->DelDirById(current_dir->id) == WINIX_ERR_OK )
{
long dir_id = current_dir->id;
old_url = current_dir->url;
system->dirs.DelDir(dir_id);
// don't use current_dir pointer anymore
log << log2 << "Rm: directory removed: " << old_url << logend;
plugin.Call(WINIX_DIR_REMOVED, dir_id);
}
}
void Rm::RemoveDir(const Item & dir)
{
plugin.Call(WINIX_DIR_PREPARE_TO_REMOVE, const_cast<Item*>(&dir));
old_url = dir.url;
RemoveDirTree(dir.id);
// warning: 'dir' has been deleted so don't use the 'dir' reference
if( cur->request->status == WINIX_ERR_OK )
log << log3 << "Rm: removed directory " << old_url << logend;
}
void Rm::RemoveFile()
{
RemoveFileOrSymlink(cur->request->item);
}
void Rm::RemoveDirContent(const Item & dir)
{
content_dir_iq2.WhereParentId(dir.id);
db->GetItems(content_item_tab2, content_dir_iq2);
for(size_t i=0 ; i<content_item_tab2.size() ; ++i)
else
{
if( content_item_tab2[i].type == Item::dir )
RemoveDir(content_item_tab2[i]);
else
RemoveFileOrSymlink(content_item_tab2[i]);
log << log1 << "Rm: I cannot remove a directory: " << current_dir->url << " (database error)" << logend;
}
}
/*
parent_dir can be null (when current_dir is pointing to the root directory)
current_dir is pointing to the directory which should be deleted
(contents of this directory is removed but the directory is deleted only if remove_this_dir is true)
*/
void Rm::RemoveDirTree(Item * parent_dir, Item * current_dir, bool remove_this_dir, bool check_access)
{
if( check_access && !system->HasReadExecAccess(*current_dir) )
{
log << log1 << "Rm: permission denied to directory: " << current_dir->url << logend;
slog << logerror << T("rm_permission_denied_to") << ": " << current_dir->url << logend;
return;
}
DirContainer::ParentIterator pnext, p = system->dirs.FindFirstChild(current_dir->id);
for( ; p != system->dirs.ParentEnd() ; p = pnext )
{
Item * child_dir = &(*p->second);
// this iterator p will be invalidated by the below RemoveDirTree() call
// (the next iterator we must calculate beforehand)
pnext = system->dirs.NextChild(p);
RemoveDirTree(current_dir, child_dir, true, check_access);
}
bool all_file_removed = RemoveDirFiles(current_dir->id, check_access);
if( remove_this_dir )
{
if( all_file_removed )
{
RemoveCurrentDir(parent_dir, current_dir, check_access);
// don't use current_dir pointer anymore
}
else
{
log << log1 << "Rm: " << current_dir->url << " directory not empty" << logend;
slog << logerror << current_dir->url << T("rm_directory_not_empty") << logend;
}
}
}
void Rm::RemoveDirTree(Item & dir, bool remove_this_dir, bool check_access)
{
Item * parent = system->dirs.GetDir(dir.parent_id);
RemoveDirTree(parent, &dir, remove_this_dir, check_access);
}
void Rm::RemoveDirContent()
{
if( !cur->request->IsParam(L"r") )
{
cur->request->status = WINIX_ERR_PERMISSION_DENIED;
log << log3 << "Rm: directory content can be removed only with 'r' parameter" << logend;
slog << logerror << T("rm_content_use_r_option") << logend;
return;
}
RemoveDirContent(*cur->request->dir_tab.back());
RemoveDirTree(*cur->request->dir_tab.back(), false, true);
}
void Rm::RemoveDir()
{
if( !cur->request->IsParam(L"r") || cur->request->dir_tab.size() <= 1 )
if( !cur->request->IsParam(L"r") )
{
// we cannot remove the root directory (dir_tab.size()==1)
cur->request->status = WINIX_ERR_PERMISSION_DENIED;
log << log3 << "Rm: a directory can be removed only with 'r' parameter" << logend;
slog << logerror << T("rm_use_r_option") << logend;
return;
}
RemoveDir(*cur->request->dir_tab.back());
if( cur->request->dir_tab.size() <= 1 )
{
cur->request->status = WINIX_ERR_PERMISSION_DENIED;
log << log1 << "Rm: the root directory cannot be removed" << logend;
slog << logerror << T("rm_cannot_remove_root_dir") << logend;
return;
}
RemoveDirTree(*cur->request->dir_tab.back(), true, true);
cur->request->dir_tab.erase(--cur->request->dir_tab.end());
}
@@ -276,7 +388,6 @@ void Rm::RemoveDir()
void Rm::Clear()
{
content_item_tab.clear();
content_item_tab2.clear();
}
@@ -288,7 +399,7 @@ void Rm::CreateJSON(bool status)
else
cur->request->page << "false\n";
cur->request->page_generated = true;
cur->request->page_generated = true;
cur->request->use_html_filter = false;
}
@@ -299,11 +410,11 @@ void Rm::MakePost()
if( cur->request->is_item )
{
RemoveFile();
RemoveFileOrSymlink(cur->request->item, true);
}
else
{
if( cur->request->IsPostVar(L"onlycontent") )
if( cur->request->IsParam(L"c") || cur->request->IsPostVar(L"c") )
RemoveDirContent();
else
RemoveDir();