Commit ed0a8e90 authored by Harald Sitter's avatar Harald Sitter 🌈
Browse files

smb: do not assume rename files are different based on name

samba is transparently supporting case sensitivity/insensitivity based
on server capabilities so 'A' and 'a' may be the same file or not. To
that end when a rename operation would change the capitalization of a
file we need to do some extra work to figure out if that renaming would
constitute an overwrite or not. Specifically we'll need to stat the
source file and then compare the inode and device returned by libsmb to
figure out if they are the same file. If they are then this is an
in-place rename, not an overwrite and we'll skip over the error
conditions to do with the dst file already existing

BUG: 430585
parent e0d633c8
/*
SPDX-License-Identifier: GPL-2.0-or-later
SPDX-FileCopyrightText: 2000 Caldera Systems, Inc.
SPDX-FileCopyrightText: 2021 Harald Sitter <sitter@kde.org>
SPDX-FileContributor: Matthew Peterson <mpeterson@caldera.com>
*/
......@@ -638,6 +639,23 @@ void SMBSlave::mkdir(const QUrl &kurl, int permissions)
}
}
static bool sameInodeStat(bool hasSrcStat, const struct stat srcStat, const struct stat dstStat)
{
if (!hasSrcStat) {
return false;
}
const auto badInode = static_cast<ino_t>(-1); // Fun fact: smb actually has code paths that can return -1 cast to ino_t (i.e. unsigned) ... ... ... :(
if (srcStat.st_ino == badInode || dstStat.st_ino == badInode) {
// If either returns a bad inode we don't know and assume they aren't the same entity.
return false;
}
const bool equal = (srcStat.st_ino == dstStat.st_ino && srcStat.st_dev == dstStat.st_dev);
qDebug(KIO_SMB_LOG) << "sameInodeStat"
<< "equal" << equal << "hasSrcStat" << hasSrcStat << "srcStat.st_ino" << srcStat.st_ino << "dstStat.st_ino" << dstStat.st_ino
<< "badInode" << badInode << "srcStat.st_dev" << srcStat.st_dev << "dstStat.st_dev" << dstStat.st_dev;
return equal;
}
void SMBSlave::rename(const QUrl &ksrc, const QUrl &kdest, KIO::JobFlags flags)
{
SMBUrl src;
......@@ -651,10 +669,22 @@ void SMBSlave::rename(const QUrl &ksrc, const QUrl &kdest, KIO::JobFlags flags)
dst = kdest;
// Check to se if the destination exists
qCDebug(KIO_SMB_LOG) << "stat dst";
// Samba can be case sensitive or insensitive, depending on server capabilities and configuration. As such if the src and dst have the same case insensitive
// name we'll stat the src to ascertain its inode and device. On recent samba and windows servers these seem to provide actually valid numbers we can rely
// on. When not we can still pretend like they aren't the same file. Worst case the user gets an overwrite prompt.
// https://bugs.kde.org/show_bug.cgi?id=430585
bool hasSrcStat = false;
struct stat srcStat{};
if (src.path().compare(dst.path(), Qt::CaseInsensitive) == 0) {
qCDebug(KIO_SMB_LOG) << "smbc_rename " << "src and dst insensitive equal, performing inode comparision";
errNum = cache_stat(dst, &st);
if (errNum == 0) {
hasSrcStat = true;
srcStat = st;
}
}
errNum = cache_stat(dst, &st);
if (errNum == 0) {
if (errNum == 0 && !sameInodeStat(hasSrcStat, srcStat, st)) {
if (S_ISDIR(st.st_mode)) {
qCDebug(KIO_SMB_LOG) << "KIO::ERR_DIR_ALREADY_EXIST";
error(KIO::ERR_DIR_ALREADY_EXIST, dst.toDisplayString());
......@@ -666,6 +696,7 @@ void SMBSlave::rename(const QUrl &ksrc, const QUrl &kdest, KIO::JobFlags flags)
return;
}
}
qCDebug(KIO_SMB_LOG) << "smbc_rename " << src.toSmbcUrl() << " " << dst.toSmbcUrl();
retVal = smbc_rename(src.toSmbcUrl(), dst.toSmbcUrl());
if (retVal < 0) {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment