KIO

chmodjob.cpp
1 /* This file is part of the KDE libraries
2  Copyright (C) 2000 Stephan Kulow <[email protected]>
3  David Faure <[email protected]>
4  Waldo Bastian <[email protected]>
5 
6  This library is free software; you can redistribute it and/or
7  modify it under the terms of the GNU Library General Public
8  License as published by the Free Software Foundation; either
9  version 2 of the License, or (at your option) any later version.
10 
11  This library is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  Library General Public License for more details.
15 
16  You should have received a copy of the GNU Library General Public License
17  along with this library; see the file COPYING.LIB. If not, write to
18  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  Boston, MA 02110-1301, USA.
20 */
21 
22 #include "chmodjob.h"
23 #include "../pathhelpers_p.h"
24 
25 #include <KLocalizedString>
26 #include <KUser>
27 #include <QDebug>
28 
29 
30 #include "listjob.h"
31 #include "job_p.h"
32 #include "jobuidelegatefactory.h"
33 #include "kioglobal_p.h"
34 
35 #include <stack>
36 
37 namespace KIO
38 {
39 
40 struct ChmodInfo {
41  QUrl url;
42  int permissions;
43 };
44 
45 enum ChmodJobState {
46  CHMODJOB_STATE_LISTING,
47  CHMODJOB_STATE_CHMODING
48 };
49 
50 class ChmodJobPrivate: public KIO::JobPrivate
51 {
52 public:
53  ChmodJobPrivate(const KFileItemList &lstItems, int permissions, int mask,
54  KUserId newOwner, KGroupId newGroup, bool recursive)
55  : state(CHMODJOB_STATE_LISTING)
56  , m_permissions(permissions)
57  , m_mask(mask)
58  , m_newOwner(newOwner)
59  , m_newGroup(newGroup)
60  , m_recursive(recursive)
61  , m_bAutoSkipFiles(false)
62  , m_lstItems(lstItems)
63  {
64  }
65 
66  ChmodJobState state;
67  int m_permissions;
68  int m_mask;
69  KUserId m_newOwner;
70  KGroupId m_newGroup;
71  bool m_recursive;
72  bool m_bAutoSkipFiles;
73  KFileItemList m_lstItems;
74  std::stack<ChmodInfo> m_infos;
75 
76  void _k_chmodNextFile();
77  void _k_slotEntries(KIO::Job *, const KIO::UDSEntryList &);
78  void _k_processList();
79 
80  Q_DECLARE_PUBLIC(ChmodJob)
81 
82  static inline ChmodJob *newJob(const KFileItemList &lstItems, int permissions, int mask,
83  KUserId newOwner, KGroupId newGroup, bool recursive, JobFlags flags)
84  {
85  ChmodJob *job = new ChmodJob(*new ChmodJobPrivate(lstItems, permissions, mask,
86  newOwner, newGroup, recursive));
88  if (!(flags & HideProgressInfo)) {
90  }
91  if (!(flags & NoPrivilegeExecution)) {
92  job->d_func()->m_privilegeExecutionEnabled = true;
93  job->d_func()->m_operationType = ChangeAttr;
94  }
95  return job;
96  }
97 };
98 
99 } // namespace KIO
100 
101 using namespace KIO;
102 
103 ChmodJob::ChmodJob(ChmodJobPrivate &dd)
104  : KIO::Job(dd)
105 {
106  QMetaObject::invokeMethod(this, "_k_processList", Qt::QueuedConnection);
107 }
108 
109 ChmodJob::~ChmodJob()
110 {
111 }
112 
113 void ChmodJobPrivate::_k_processList()
114 {
115  Q_Q(ChmodJob);
116  while (!m_lstItems.isEmpty()) {
117  const KFileItem item = m_lstItems.first();
118  if (!item.isLink()) { // don't do anything with symlinks
119  // File or directory -> remember to chmod
120  ChmodInfo info;
121  info.url = item.url();
122  // This is a toplevel file, we apply changes directly (no +X emulation here)
123  const mode_t permissions = item.permissions() & 0777; // get rid of "set gid" and other special flags
124  info.permissions = (m_permissions & m_mask) | (permissions & ~m_mask);
125  /*//qDebug() << "toplevel url:" << info.url << "\n current permissions=" << QString::number(permissions,8)
126  << "\n wanted permission=" << QString::number(m_permissions,8)
127  << "\n with mask=" << QString::number(m_mask,8)
128  << "\n with ~mask (mask bits we keep) =" << QString::number((uint)~m_mask,8)
129  << "\n bits we keep =" << QString::number(permissions & ~m_mask,8)
130  << "\n new permissions = " << QString::number(info.permissions,8);*/
131  m_infos.push(std::move(info));
132  //qDebug() << "processList : Adding info for " << info.url;
133  // Directory and recursive -> list
134  if (item.isDir() && m_recursive) {
135  //qDebug() << "ChmodJob::processList dir -> listing";
137  q->connect(listJob, SIGNAL(entries(KIO::Job*,KIO::UDSEntryList)),
138  SLOT(_k_slotEntries(KIO::Job*,KIO::UDSEntryList)));
139  q->addSubjob(listJob);
140  return; // we'll come back later, when this one's finished
141  }
142  }
143  m_lstItems.removeFirst();
144  }
145  //qDebug() << "ChmodJob::processList -> going to STATE_CHMODING";
146  // We have finished, move on
147  state = CHMODJOB_STATE_CHMODING;
148  _k_chmodNextFile();
149 }
150 
151 void ChmodJobPrivate::_k_slotEntries(KIO::Job *, const KIO::UDSEntryList &list)
152 {
155  for (; it != end; ++it) {
156  const KIO::UDSEntry &entry = *it;
157  const bool isLink = !entry.stringValue(KIO::UDSEntry::UDS_LINK_DEST).isEmpty();
158  const QString relativePath = entry.stringValue(KIO::UDSEntry::UDS_NAME);
159  if (!isLink && relativePath != QLatin1String("..")) {
160  const mode_t permissions = entry.numberValue(KIO::UDSEntry::UDS_ACCESS)
161  & 0777; // get rid of "set gid" and other special flags
162 
163  ChmodInfo info;
164  info.url = m_lstItems.first().url(); // base directory
165  info.url.setPath(concatPaths(info.url.path(), relativePath));
166  int mask = m_mask;
167  // Emulate -X: only give +x to files that had a +x bit already
168  // So the check is the opposite : if the file had no x bit, don't touch x bits
169  // For dirs this doesn't apply
170  if (!entry.isDir()) {
171  int newPerms = m_permissions & mask;
172  if ((newPerms & 0111) && !(permissions & 0111)) {
173  // don't interfere with mandatory file locking
174  if (newPerms & 02000) {
175  mask = mask & ~0101;
176  } else {
177  mask = mask & ~0111;
178  }
179  }
180  }
181  info.permissions = (m_permissions & mask) | (permissions & ~mask);
182  /*//qDebug() << info.url << "\n current permissions=" << QString::number(permissions,8)
183  << "\n wanted permission=" << QString::number(m_permissions,8)
184  << "\n with mask=" << QString::number(mask,8)
185  << "\n with ~mask (mask bits we keep) =" << QString::number((uint)~mask,8)
186  << "\n bits we keep =" << QString::number(permissions & ~mask,8)
187  << "\n new permissions = " << QString::number(info.permissions,8);*/
188  // Push this info on top of the stack so it's handled first.
189  // This way, the toplevel dirs are done last.
190  m_infos.push(std::move(info));
191  }
192  }
193 }
194 
195 void ChmodJobPrivate::_k_chmodNextFile()
196 {
197  Q_Q(ChmodJob);
198  if (!m_infos.empty()) {
199  ChmodInfo info = m_infos.top();
200  m_infos.pop();
201  // First update group / owner (if local file)
202  // (permissions have to set after, in case of suid and sgid)
203  if (info.url.isLocalFile() && (m_newOwner.isValid() || m_newGroup.isValid())) {
204  QString path = info.url.toLocalFile();
205  if (!KIOPrivate::changeOwnership(path, m_newOwner, m_newGroup)) {
206  if (!m_uiDelegateExtension) {
207  emit q->warning(q, i18n("Could not modify the ownership of file %1", path));
208  } else if (!m_bAutoSkipFiles) {
209  const QString errMsg = i18n("<qt>Could not modify the ownership of file <b>%1</b>. You have insufficient access to the file to perform the change.</qt>", path);
210  SkipDialog_Options options;
211  if (m_infos.size() > 1) {
212  options |= SkipDialog_MultipleItems;
213  }
214  const SkipDialog_Result skipResult = m_uiDelegateExtension->askSkip(q, options, errMsg);
215  switch (skipResult) {
216  case Result_AutoSkip:
217  m_bAutoSkipFiles = true;
218  // fall through
219  Q_FALLTHROUGH();
220  case Result_Skip:
221  QMetaObject::invokeMethod(q, "_k_chmodNextFile", Qt::QueuedConnection);
222  return;
223  case Result_Retry:
224  m_infos.push(std::move(info));
225  QMetaObject::invokeMethod(q, "_k_chmodNextFile", Qt::QueuedConnection);
226  return;
227  case Result_Cancel:
228  default:
229  q->setError(ERR_USER_CANCELED);
230  q->emitResult();
231  return;
232  }
233  }
234  }
235  }
236 
237  /*qDebug() << "chmod'ing" << info.url
238  << "to" << QString::number(info.permissions,8);*/
239  KIO::SimpleJob *job = KIO::chmod(info.url, info.permissions);
240  job->setParentJob(q);
241  // copy the metadata for acl and default acl
242  const QString aclString = q->queryMetaData(QStringLiteral("ACL_STRING"));
243  const QString defaultAclString = q->queryMetaData(QStringLiteral("DEFAULT_ACL_STRING"));
244  if (!aclString.isEmpty()) {
245  job->addMetaData(QStringLiteral("ACL_STRING"), aclString);
246  }
247  if (!defaultAclString.isEmpty()) {
248  job->addMetaData(QStringLiteral("DEFAULT_ACL_STRING"), defaultAclString);
249  }
250  q->addSubjob(job);
251  } else
252  // We have finished
253  {
254  q->emitResult();
255  }
256 }
257 
258 void ChmodJob::slotResult(KJob *job)
259 {
260  Q_D(ChmodJob);
261  removeSubjob(job);
262  if (job->error()) {
263  setError(job->error());
264  setErrorText(job->errorText());
265  emitResult();
266  return;
267  }
268  //qDebug() << "d->m_lstItems:" << d->m_lstItems.count();
269  switch (d->state) {
270  case CHMODJOB_STATE_LISTING:
271  d->m_lstItems.removeFirst();
272  //qDebug() << "-> processList";
273  d->_k_processList();
274  return;
275  case CHMODJOB_STATE_CHMODING:
276  //qDebug() << "-> chmodNextFile";
277  d->_k_chmodNextFile();
278  return;
279  default:
280  Q_ASSERT(false);
281  return;
282  }
283 }
284 
285 ChmodJob *KIO::chmod(const KFileItemList &lstItems, int permissions, int mask,
286  const QString &owner, const QString &group,
287  bool recursive, JobFlags flags)
288 {
289  KUserId uid = KUserId::fromName(owner);
290  KGroupId gid = KGroupId::fromName(group);
291  return ChmodJobPrivate::newJob(lstItems, permissions, mask, uid,
292  gid, recursive, flags);
293 }
294 
295 #include "moc_chmodjob.cpp"
void addMetaData(const QString &key, const QString &value)
Add key/value pair to the meta data that is sent to the slave.
Definition: job.cpp:243
bool isDir() const
Returns true if this item represents a directory.
Definition: kfileitem.cpp:1241
void emitResult()
void setUiDelegate(KJobUiDelegate *delegate)
Universal Directory Service.
Definition: udsentry.h:88
A namespace for KIO globals.
Definition: authinfo.h:34
A ListJob is allows you to get the get the content of a directory.
Definition: listjob.h:40
void removeFirst()
Hide progress information dialog, i.e.
Definition: job_base.h:287
void setError(int errorCode)
bool isDir() const
Definition: udsentry.cpp:375
mode_t permissions() const
Returns the permissions of the file (stat.st_mode containing only permissions).
Definition: kfileitem.cpp:1503
static KGroupId fromName(const QString &name)
static KUserId fromName(const QString &name)
void setErrorText(const QString &errorText)
KIOCORE_EXPORT ListJob * listRecursive(const QUrl &url, JobFlags flags=DefaultFlags, bool includeHidden=true)
The same as the previous method, but recurses subdirectories.
Definition: listjob.cpp:263
When set, notifies the slave that application/job does not want privilege execution.
Definition: job_base.h:312
This job changes permissions on a list of files or directories, optionally in a recursive manner...
Definition: chmodjob.h:40
long long numberValue(uint field, long long defaultValue=0) const
Definition: udsentry.cpp:370
bool isEmpty() const const
bool isEmpty() const const
KIOCORE_EXPORT KJobUiDelegate * createDefaultJobUiDelegate()
Convenience method: use default factory, if there&#39;s one, to create a delegate and return it...
List of KFileItems, which adds a few helper methods to QList<KFileItem>.
Definition: kfileitem.h:599
QString stringValue(uint field) const
Definition: udsentry.cpp:365
KIOCORE_EXPORT KJobTrackerInterface * getJobTracker()
Returns the job tracker to be used by all KIO jobs (in which HideProgressInfo is not set) ...
Definition: jobtracker.cpp:25
T & first()
QList::iterator end()
bool isLink() const
Returns true if this item represents a link in the UNIX sense of a link.
Definition: kfileitem.cpp:1525
KIOCORE_EXPORT ChmodJob * chmod(const KFileItemList &lstItems, int permissions, int mask, const QString &newOwner, const QString &newGroup, bool recursive, JobFlags flags=DefaultFlags)
Creates a job that changes permissions/ownership on several files or directories, optionally recursiv...
Definition: chmodjob.cpp:285
bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType type, QGenericReturnArgument ret, QGenericArgument val0, QGenericArgument val1, QGenericArgument val2, QGenericArgument val3, QGenericArgument val4, QGenericArgument val5, QGenericArgument val6, QGenericArgument val7, QGenericArgument val8, QGenericArgument val9)
QString i18n(const char *text, const TYPE &arg...)
const QList< QKeySequence > & end()
void setParentJob(Job *parentJob)
Set the parent Job.
Definition: job.cpp:214
Access permissions (part of the mode returned by stat)
Definition: udsentry.h:269
Filename - as displayed in directory listings etc.
Definition: udsentry.h:261
Name of the file where the link points to Allows to check for a symlink (don&#39;t use S_ISLNK !) ...
Definition: udsentry.h:282
RenameDialog_Result
The result of a rename or skip dialog.
The base class for all jobs.
Definition: job_base.h:57
bool removeSubjob(KJob *job) override
Mark a sub job as being done.
Definition: job.cpp:98
QUrl url() const
Returns the url of the file.
Definition: kfileitem.cpp:1494
virtual void registerJob(KJob *job)
QueuedConnection
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QList::iterator begin()
A KFileItem is a generic class to handle a file, local or remote.
Definition: kfileitem.h:47
QString errorText() const
int error() const
A simple job (one url and one command).
Definition: simplejob.h:39
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Tue Jul 14 2020 23:00:59 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.