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:
345
functions/rm.cpp
345
functions/rm.cpp
@@ -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();
|
||||
|
Reference in New Issue
Block a user