Line data Source code
1 : ;;; tramp-smb.el --- Tramp access functions for SMB servers -*- lexical-binding:t -*-
2 :
3 : ;; Copyright (C) 2002-2017 Free Software Foundation, Inc.
4 :
5 : ;; Author: Michael Albinus <michael.albinus@gmx.de>
6 : ;; Keywords: comm, processes
7 : ;; Package: tramp
8 :
9 : ;; This file is part of GNU Emacs.
10 :
11 : ;; GNU Emacs is free software: you can redistribute it and/or modify
12 : ;; it under the terms of the GNU General Public License as published by
13 : ;; the Free Software Foundation, either version 3 of the License, or
14 : ;; (at your option) any later version.
15 :
16 : ;; GNU Emacs is distributed in the hope that it will be useful,
17 : ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18 : ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 : ;; GNU General Public License for more details.
20 :
21 : ;; You should have received a copy of the GNU General Public License
22 : ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
23 :
24 : ;;; Commentary:
25 :
26 : ;; Access functions for SMB servers like SAMBA or M$ Windows from Tramp.
27 :
28 : ;;; Code:
29 :
30 : (require 'tramp)
31 :
32 : ;; Define SMB method ...
33 : ;;;###tramp-autoload
34 : (defconst tramp-smb-method "smb"
35 : "Method to connect SAMBA and M$ SMB servers.")
36 :
37 : ;; ... and add it to the method list.
38 : ;;;###tramp-autoload
39 : (unless (memq system-type '(cygwin windows-nt))
40 : (add-to-list 'tramp-methods
41 : `(,tramp-smb-method
42 : ;; We define an empty command, because `tramp-smb-call-winexe'
43 : ;; opens already the powershell. Used in `tramp-handle-shell-command'.
44 : (tramp-remote-shell "")
45 : ;; This is just a guess. We don't know whether the share "C$"
46 : ;; is available for public use, and whether the user has write
47 : ;; access.
48 : (tramp-tmpdir "/C$/Temp")
49 : ;; Another guess. We might implement a better check later on.
50 : (tramp-case-insensitive t))))
51 :
52 : ;; Add a default for `tramp-default-user-alist'. Rule: For the SMB method,
53 : ;; the anonymous user is chosen.
54 : ;;;###tramp-autoload
55 : (add-to-list 'tramp-default-user-alist
56 : `(,(concat "\\`" tramp-smb-method "\\'") nil nil))
57 :
58 : ;; Add completion function for SMB method.
59 : ;;;###tramp-autoload
60 : (eval-after-load 'tramp
61 : '(tramp-set-completion-function
62 : tramp-smb-method
63 : '((tramp-parse-netrc "~/.netrc"))))
64 :
65 : ;;;###tramp-autoload
66 : (defcustom tramp-smb-program "smbclient"
67 : "Name of SMB client to run."
68 : :group 'tramp
69 : :type 'string
70 : :require 'tramp)
71 :
72 : ;;;###tramp-autoload
73 : (defcustom tramp-smb-acl-program "smbcacls"
74 : "Name of SMB acls to run."
75 : :group 'tramp
76 : :type 'string
77 : :version "24.4"
78 : :require 'tramp)
79 :
80 : ;;;###tramp-autoload
81 : (defcustom tramp-smb-conf "/dev/null"
82 : "Path of the smb.conf file.
83 : If it is nil, no smb.conf will be added to the `tramp-smb-program'
84 : call, letting the SMB client use the default one."
85 : :group 'tramp
86 : :type '(choice (const nil) (file :must-match t))
87 : :require 'tramp)
88 :
89 : (defvar tramp-smb-version nil
90 : "Version string of the SMB client.")
91 :
92 : (defconst tramp-smb-server-version
93 : "Domain=\\[[^]]*\\] OS=\\[[^]]*\\] Server=\\[[^]]*\\]"
94 : "Regexp of SMB server identification.")
95 :
96 : (defconst tramp-smb-prompt "^\\(smb:\\|PS\\) .+> \\|^\\s-+Server\\s-+Comment$"
97 : "Regexp used as prompt in smbclient or powershell.")
98 :
99 : (defconst tramp-smb-wrong-passwd-regexp
100 : (regexp-opt
101 : '("NT_STATUS_LOGON_FAILURE"
102 : "NT_STATUS_WRONG_PASSWORD"))
103 : "Regexp for login error strings of SMB servers.")
104 :
105 : (defconst tramp-smb-errors
106 : (mapconcat
107 : 'identity
108 : `(;; Connection error / timeout / unknown command.
109 : "Connection\\( to \\S-+\\)? failed"
110 : "Read from server failed, maybe it closed the connection"
111 : "Call timed out: server did not respond"
112 : "\\S-+: command not found"
113 : "Server doesn't support UNIX CIFS calls"
114 : ,(regexp-opt
115 : '(;; Samba.
116 : "ERRDOS"
117 : "ERRHRD"
118 : "ERRSRV"
119 : "ERRbadfile"
120 : "ERRbadpw"
121 : "ERRfilexists"
122 : "ERRnoaccess"
123 : "ERRnomem"
124 : "ERRnosuchshare"
125 : ;; Windows 4.0 (Windows NT), Windows 5.0 (Windows 2000),
126 : ;; Windows 5.1 (Windows XP), Windows 5.2 (Windows Server 2003),
127 : ;; Windows 6.0 (Windows Vista), Windows 6.1 (Windows 7),
128 : ;; Windows 6.3 (Windows Server 2012, Windows 10).
129 : "NT_STATUS_ACCESS_DENIED"
130 : "NT_STATUS_ACCOUNT_LOCKED_OUT"
131 : "NT_STATUS_BAD_NETWORK_NAME"
132 : "NT_STATUS_CANNOT_DELETE"
133 : "NT_STATUS_CONNECTION_REFUSED"
134 : "NT_STATUS_DIRECTORY_NOT_EMPTY"
135 : "NT_STATUS_DUPLICATE_NAME"
136 : "NT_STATUS_FILE_IS_A_DIRECTORY"
137 : "NT_STATUS_HOST_UNREACHABLE"
138 : "NT_STATUS_IMAGE_ALREADY_LOADED"
139 : "NT_STATUS_INVALID_LEVEL"
140 : "NT_STATUS_INVALID_PARAMETER_MIX"
141 : "NT_STATUS_IO_TIMEOUT"
142 : "NT_STATUS_LOGON_FAILURE"
143 : "NT_STATUS_NETWORK_ACCESS_DENIED"
144 : "NT_STATUS_NOT_IMPLEMENTED"
145 : "NT_STATUS_NO_LOGON_SERVERS"
146 : "NT_STATUS_NO_SUCH_FILE"
147 : "NT_STATUS_NO_SUCH_USER"
148 : "NT_STATUS_OBJECT_NAME_COLLISION"
149 : "NT_STATUS_OBJECT_NAME_INVALID"
150 : "NT_STATUS_OBJECT_NAME_NOT_FOUND"
151 : "NT_STATUS_PASSWORD_MUST_CHANGE"
152 : "NT_STATUS_SHARING_VIOLATION"
153 : "NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE"
154 : "NT_STATUS_UNSUCCESSFUL"
155 : "NT_STATUS_WRONG_PASSWORD")))
156 : "\\|")
157 : "Regexp for possible error strings of SMB servers.
158 : Used instead of analyzing error codes of commands.")
159 :
160 : (defconst tramp-smb-actions-with-share
161 : '((tramp-smb-prompt tramp-action-succeed)
162 : (tramp-password-prompt-regexp tramp-action-password)
163 : (tramp-wrong-passwd-regexp tramp-action-permission-denied)
164 : (tramp-smb-errors tramp-action-permission-denied)
165 : (tramp-process-alive-regexp tramp-action-process-alive))
166 : "List of pattern/action pairs.
167 : This list is used for login to SMB servers.
168 :
169 : See `tramp-actions-before-shell' for more info.")
170 :
171 : (defconst tramp-smb-actions-without-share
172 : '((tramp-password-prompt-regexp tramp-action-password)
173 : (tramp-wrong-passwd-regexp tramp-action-permission-denied)
174 : (tramp-smb-errors tramp-action-permission-denied)
175 : (tramp-process-alive-regexp tramp-action-out-of-band))
176 : "List of pattern/action pairs.
177 : This list is used for login to SMB servers.
178 :
179 : See `tramp-actions-before-shell' for more info.")
180 :
181 : (defconst tramp-smb-actions-with-tar
182 : '((tramp-password-prompt-regexp tramp-action-password)
183 : (tramp-wrong-passwd-regexp tramp-action-permission-denied)
184 : (tramp-smb-errors tramp-action-permission-denied)
185 : (tramp-process-alive-regexp tramp-smb-action-with-tar))
186 : "List of pattern/action pairs.
187 : This list is used for tar-like copy of directories.
188 :
189 : See `tramp-actions-before-shell' for more info.")
190 :
191 : (defconst tramp-smb-actions-get-acl
192 : '((tramp-password-prompt-regexp tramp-action-password)
193 : (tramp-wrong-passwd-regexp tramp-action-permission-denied)
194 : (tramp-smb-errors tramp-action-permission-denied)
195 : (tramp-process-alive-regexp tramp-smb-action-get-acl))
196 : "List of pattern/action pairs.
197 : This list is used for smbcacls actions.
198 :
199 : See `tramp-actions-before-shell' for more info.")
200 :
201 : (defconst tramp-smb-actions-set-acl
202 : '((tramp-password-prompt-regexp tramp-action-password)
203 : (tramp-wrong-passwd-regexp tramp-action-permission-denied)
204 : (tramp-smb-errors tramp-action-permission-denied)
205 : (tramp-process-alive-regexp tramp-smb-action-set-acl))
206 : "List of pattern/action pairs.
207 : This list is used for smbcacls actions.
208 :
209 : See `tramp-actions-before-shell' for more info.")
210 :
211 : ;; New handlers should be added here.
212 : ;;;###tramp-autoload
213 : (defconst tramp-smb-file-name-handler-alist
214 : '(;; `access-file' performed by default handler.
215 : (add-name-to-file . tramp-smb-handle-add-name-to-file)
216 : ;; `byte-compiler-base-file-name' performed by default handler.
217 : (copy-directory . tramp-smb-handle-copy-directory)
218 : (copy-file . tramp-smb-handle-copy-file)
219 : (delete-directory . tramp-smb-handle-delete-directory)
220 : (delete-file . tramp-smb-handle-delete-file)
221 : ;; `diff-latest-backup-file' performed by default handler.
222 : (directory-file-name . tramp-handle-directory-file-name)
223 : (directory-files . tramp-smb-handle-directory-files)
224 : (directory-files-and-attributes
225 : . tramp-handle-directory-files-and-attributes)
226 : (dired-compress-file . ignore)
227 : (dired-uncache . tramp-handle-dired-uncache)
228 : (expand-file-name . tramp-smb-handle-expand-file-name)
229 : (file-accessible-directory-p . tramp-smb-handle-file-directory-p)
230 : (file-acl . tramp-smb-handle-file-acl)
231 : (file-attributes . tramp-smb-handle-file-attributes)
232 : (file-directory-p . tramp-smb-handle-file-directory-p)
233 : (file-file-equal-p . tramp-handle-file-equal-p)
234 : (file-executable-p . tramp-handle-file-exists-p)
235 : (file-exists-p . tramp-handle-file-exists-p)
236 : (file-in-directory-p . tramp-handle-file-in-directory-p)
237 : (file-local-copy . tramp-smb-handle-file-local-copy)
238 : (file-modes . tramp-handle-file-modes)
239 : (file-name-all-completions . tramp-smb-handle-file-name-all-completions)
240 : (file-name-as-directory . tramp-handle-file-name-as-directory)
241 : (file-name-case-insensitive-p . tramp-handle-file-name-case-insensitive-p)
242 : (file-name-completion . tramp-handle-file-name-completion)
243 : (file-name-directory . tramp-handle-file-name-directory)
244 : (file-name-nondirectory . tramp-handle-file-name-nondirectory)
245 : ;; `file-name-sans-versions' performed by default handler.
246 : (file-newer-than-file-p . tramp-handle-file-newer-than-file-p)
247 : (file-notify-add-watch . tramp-handle-file-notify-add-watch)
248 : (file-notify-rm-watch . tramp-handle-file-notify-rm-watch)
249 : (file-notify-valid-p . tramp-handle-file-notify-valid-p)
250 : (file-ownership-preserved-p . ignore)
251 : (file-readable-p . tramp-handle-file-exists-p)
252 : (file-regular-p . tramp-handle-file-regular-p)
253 : (file-remote-p . tramp-handle-file-remote-p)
254 : ;; `file-selinux-context' performed by default handler.
255 : (file-symlink-p . tramp-handle-file-symlink-p)
256 : ;; `file-truename' performed by default handler.
257 : (file-writable-p . tramp-smb-handle-file-writable-p)
258 : (find-backup-file-name . tramp-handle-find-backup-file-name)
259 : ;; `find-file-noselect' performed by default handler.
260 : ;; `get-file-buffer' performed by default handler.
261 : (insert-directory . tramp-smb-handle-insert-directory)
262 : (insert-file-contents . tramp-handle-insert-file-contents)
263 : (load . tramp-handle-load)
264 : (make-auto-save-file-name . tramp-handle-make-auto-save-file-name)
265 : (make-directory . tramp-smb-handle-make-directory)
266 : (make-directory-internal . tramp-smb-handle-make-directory-internal)
267 : (make-nearby-temp-file . tramp-handle-make-nearby-temp-file)
268 : (make-symbolic-link . tramp-smb-handle-make-symbolic-link)
269 : (process-file . tramp-smb-handle-process-file)
270 : (rename-file . tramp-smb-handle-rename-file)
271 : (set-file-acl . tramp-smb-handle-set-file-acl)
272 : (set-file-modes . tramp-smb-handle-set-file-modes)
273 : (set-file-selinux-context . ignore)
274 : (set-file-times . ignore)
275 : (set-visited-file-modtime . tramp-handle-set-visited-file-modtime)
276 : (shell-command . tramp-handle-shell-command)
277 : (start-file-process . tramp-smb-handle-start-file-process)
278 : (substitute-in-file-name . tramp-smb-handle-substitute-in-file-name)
279 : (temporary-file-directory . tramp-handle-temporary-file-directory)
280 : (unhandled-file-name-directory . ignore)
281 : (vc-registered . ignore)
282 : (verify-visited-file-modtime . tramp-handle-verify-visited-file-modtime)
283 : (write-region . tramp-smb-handle-write-region))
284 : "Alist of handler functions for Tramp SMB method.
285 : Operations not mentioned here will be handled by the default Emacs primitives.")
286 :
287 : ;; Options for remote processes via winexe.
288 : ;;;###tramp-autoload
289 : (defcustom tramp-smb-winexe-program "winexe"
290 : "Name of winexe client to run.
291 : If it isn't found in the local $PATH, the absolute path of winexe
292 : shall be given. This is needed for remote processes."
293 : :group 'tramp
294 : :type 'string
295 : :version "24.3"
296 : :require 'tramp)
297 :
298 : ;;;###tramp-autoload
299 : (defcustom tramp-smb-winexe-shell-command "powershell.exe"
300 : "Shell to be used for processes on remote machines.
301 : This must be Powershell V2 compatible."
302 : :group 'tramp
303 : :type 'string
304 : :version "24.3"
305 : :require 'tramp)
306 :
307 : ;;;###tramp-autoload
308 : (defcustom tramp-smb-winexe-shell-command-switch "-file -"
309 : "Command switch used together with `tramp-smb-winexe-shell-command'.
310 : This can be used to disable echo etc."
311 : :group 'tramp
312 : :type 'string
313 : :version "24.3"
314 : :require 'tramp)
315 :
316 : ;; It must be a `defsubst' in order to push the whole code into
317 : ;; tramp-loaddefs.el. Otherwise, there would be recursive autoloading.
318 : ;;;###tramp-autoload
319 : (defsubst tramp-smb-file-name-p (filename)
320 : "Check if it's a filename for SMB servers."
321 45996 : (string= (tramp-file-name-method (tramp-dissect-file-name filename))
322 45996 : tramp-smb-method))
323 :
324 : ;;;###tramp-autoload
325 : (defun tramp-smb-file-name-handler (operation &rest args)
326 : "Invoke the SMB related OPERATION.
327 : First arg specifies the OPERATION, second arg is a list of arguments to
328 : pass to the OPERATION."
329 1 : (let ((fn (assoc operation tramp-smb-file-name-handler-alist)))
330 1 : (if fn
331 1 : (save-match-data (apply (cdr fn) args))
332 1 : (tramp-run-real-handler operation args))))
333 :
334 : ;;;###tramp-autoload
335 : (unless (memq system-type '(cygwin windows-nt))
336 : (tramp-register-foreign-file-name-handler
337 : 'tramp-smb-file-name-p 'tramp-smb-file-name-handler))
338 :
339 : ;; File name primitives.
340 :
341 : (defun tramp-smb-handle-add-name-to-file
342 : (filename newname &optional ok-if-already-exists)
343 : "Like `add-name-to-file' for Tramp files."
344 0 : (unless (tramp-equal-remote filename newname)
345 0 : (with-parsed-tramp-file-name
346 0 : (if (tramp-tramp-file-p filename) filename newname) nil
347 0 : (tramp-error
348 0 : v 'file-error
349 : "add-name-to-file: %s"
350 0 : "only implemented for same method, same user, same host")))
351 0 : (with-parsed-tramp-file-name filename v1
352 0 : (with-parsed-tramp-file-name newname v2
353 0 : (when (file-directory-p filename)
354 0 : (tramp-error
355 0 : v2 'file-error
356 0 : "add-name-to-file: %s must not be a directory" filename))
357 0 : (when (and (not ok-if-already-exists)
358 0 : (file-exists-p newname)
359 0 : (not (numberp ok-if-already-exists))
360 0 : (y-or-n-p
361 0 : (format
362 : "File %s already exists; make it a new name anyway? "
363 0 : newname)))
364 0 : (tramp-error
365 0 : v2 'file-error
366 0 : "add-name-to-file: file %s already exists" newname))
367 : ;; We must also flush the cache of the directory, because
368 : ;; `file-attributes' reads the values from there.
369 0 : (tramp-flush-file-property v2 (file-name-directory v2-localname))
370 0 : (tramp-flush-file-property v2 v2-localname)
371 0 : (unless
372 0 : (tramp-smb-send-command
373 0 : v1
374 0 : (format
375 : "%s \"%s\" \"%s\""
376 0 : (if (tramp-smb-get-cifs-capabilities v1) "link" "hardlink")
377 0 : (tramp-smb-get-localname v1)
378 0 : (tramp-smb-get-localname v2)))
379 0 : (tramp-error
380 0 : v2 'file-error
381 : "error with add-name-to-file, see buffer `%s' for details"
382 0 : (buffer-name))))))
383 :
384 : (defun tramp-smb-action-with-tar (proc vec)
385 : "Untar from connection buffer."
386 0 : (if (not (process-live-p proc))
387 0 : (throw 'tramp-action 'process-died)
388 :
389 0 : (with-current-buffer (tramp-get-connection-buffer vec)
390 0 : (goto-char (point-min))
391 0 : (when (search-forward-regexp tramp-smb-server-version nil t)
392 : ;; There might be a hidden password prompt.
393 0 : (widen)
394 0 : (forward-line)
395 0 : (tramp-message vec 6 (buffer-substring (point-min) (point)))
396 0 : (delete-region (point-min) (point))
397 0 : (throw 'tramp-action 'ok)))))
398 :
399 : (defun tramp-smb-handle-copy-directory
400 : (dirname newname &optional keep-date parents copy-contents)
401 : "Like `copy-directory' for Tramp files."
402 0 : (if copy-contents
403 : ;; We must do it file-wise.
404 0 : (tramp-run-real-handler
405 0 : 'copy-directory (list dirname newname keep-date parents copy-contents))
406 :
407 0 : (setq dirname (expand-file-name dirname)
408 0 : newname (expand-file-name newname))
409 0 : (let ((t1 (tramp-tramp-file-p dirname))
410 0 : (t2 (tramp-tramp-file-p newname)))
411 0 : (with-parsed-tramp-file-name (if t1 dirname newname) nil
412 0 : (with-tramp-progress-reporter
413 0 : v 0 (format "Copying %s to %s" dirname newname)
414 0 : (cond
415 : ;; We must use a local temporary directory.
416 0 : ((and t1 t2)
417 0 : (let ((tmpdir
418 0 : (make-temp-name
419 0 : (expand-file-name
420 0 : tramp-temp-name-prefix
421 0 : (tramp-compat-temporary-file-directory)))))
422 0 : (unwind-protect
423 0 : (progn
424 0 : (make-directory tmpdir)
425 0 : (copy-directory dirname tmpdir keep-date 'parents)
426 0 : (copy-directory
427 0 : (expand-file-name (file-name-nondirectory dirname) tmpdir)
428 0 : newname keep-date parents))
429 0 : (delete-directory tmpdir 'recursive))))
430 :
431 : ;; We can copy recursively.
432 0 : ((and (or t1 t2) (tramp-smb-get-cifs-capabilities v))
433 0 : (when (and (file-directory-p newname)
434 0 : (not (string-equal (file-name-nondirectory dirname)
435 0 : (file-name-nondirectory newname))))
436 0 : (setq newname
437 0 : (expand-file-name
438 0 : (file-name-nondirectory dirname) newname))
439 0 : (if t2 (setq v (tramp-dissect-file-name newname))))
440 0 : (if (not (file-directory-p newname))
441 0 : (make-directory newname parents))
442 :
443 : ;; Set variables for computing the prompt for reading password.
444 0 : (setq tramp-current-method method
445 0 : tramp-current-user user
446 0 : tramp-current-domain domain
447 0 : tramp-current-host host
448 0 : tramp-current-port port)
449 :
450 0 : (let* ((share (tramp-smb-get-share v))
451 0 : (localname (file-name-as-directory
452 0 : (replace-regexp-in-string
453 0 : "\\\\" "/" (tramp-smb-get-localname v))))
454 0 : (tmpdir (make-temp-name
455 0 : (expand-file-name
456 0 : tramp-temp-name-prefix
457 0 : (tramp-compat-temporary-file-directory))))
458 0 : (args (list (concat "//" host "/" share) "-E")))
459 :
460 0 : (if (not (zerop (length user)))
461 0 : (setq args (append args (list "-U" user)))
462 0 : (setq args (append args (list "-N"))))
463 :
464 0 : (when domain (setq args (append args (list "-W" domain))))
465 0 : (when port (setq args (append args (list "-p" port))))
466 0 : (when tramp-smb-conf
467 0 : (setq args (append args (list "-s" tramp-smb-conf))))
468 0 : (setq args
469 0 : (if t1
470 : ;; Source is remote.
471 0 : (append args
472 0 : (list "-D" (tramp-unquote-shell-quote-argument
473 0 : localname)
474 0 : "-c" (shell-quote-argument "tar qc - *")
475 : "|" "tar" "xfC" "-"
476 0 : (tramp-unquote-shell-quote-argument
477 0 : tmpdir)))
478 : ;; Target is remote.
479 0 : (append (list "tar" "cfC" "-"
480 0 : (tramp-unquote-shell-quote-argument dirname)
481 0 : "." "|")
482 0 : args
483 0 : (list "-D" (tramp-unquote-shell-quote-argument
484 0 : localname)
485 0 : "-c" (shell-quote-argument "tar qx -")))))
486 :
487 0 : (unwind-protect
488 0 : (with-temp-buffer
489 : ;; Set the transfer process properties.
490 0 : (tramp-set-connection-property
491 0 : v "process-name" (buffer-name (current-buffer)))
492 0 : (tramp-set-connection-property
493 0 : v "process-buffer" (current-buffer))
494 :
495 0 : (when t1
496 : ;; The smbclient tar command creates always
497 : ;; complete paths. We must emulate the
498 : ;; directory structure, and symlink to the real
499 : ;; target.
500 0 : (make-directory
501 0 : (expand-file-name
502 0 : ".." (concat tmpdir localname))
503 0 : 'parents)
504 0 : (make-symbolic-link
505 0 : newname (directory-file-name (concat tmpdir localname))))
506 :
507 : ;; Use an asynchronous processes. By this,
508 : ;; password can be handled.
509 0 : (let* ((default-directory tmpdir)
510 0 : (p (apply
511 : 'start-process
512 0 : (tramp-get-connection-name v)
513 0 : (tramp-get-connection-buffer v)
514 0 : tramp-smb-program args)))
515 :
516 0 : (tramp-message
517 0 : v 6 "%s" (mapconcat 'identity (process-command p) " "))
518 0 : (tramp-set-connection-property p "vector" v)
519 0 : (process-put p 'adjust-window-size-function 'ignore)
520 0 : (set-process-query-on-exit-flag p nil)
521 0 : (tramp-process-actions p v nil tramp-smb-actions-with-tar)
522 :
523 0 : (while (process-live-p p)
524 0 : (sit-for 0.1))
525 0 : (tramp-message v 6 "\n%s" (buffer-string))))
526 :
527 : ;; Reset the transfer process properties.
528 0 : (tramp-set-connection-property v "process-name" nil)
529 0 : (tramp-set-connection-property v "process-buffer" nil)
530 0 : (when t1 (delete-directory tmpdir 'recurse))))
531 :
532 : ;; Handle KEEP-DATE argument.
533 0 : (when keep-date
534 0 : (set-file-times
535 0 : newname
536 0 : (tramp-compat-file-attribute-modification-time
537 0 : (file-attributes dirname))))
538 :
539 : ;; Set the mode.
540 0 : (unless keep-date
541 0 : (set-file-modes newname (tramp-default-file-modes dirname)))
542 :
543 : ;; When newname did exist, we have wrong cached values.
544 0 : (when t2
545 0 : (with-parsed-tramp-file-name newname nil
546 0 : (tramp-flush-file-property v (file-name-directory localname))
547 0 : (tramp-flush-file-property v localname))))
548 :
549 : ;; We must do it file-wise.
550 : (t
551 0 : (tramp-run-real-handler
552 0 : 'copy-directory (list dirname newname keep-date parents)))))))))
553 :
554 : (defun tramp-smb-handle-copy-file
555 : (filename newname &optional ok-if-already-exists keep-date
556 : _preserve-uid-gid _preserve-extended-attributes)
557 : "Like `copy-file' for Tramp files.
558 : KEEP-DATE has no effect in case NEWNAME resides on an SMB server.
559 : PRESERVE-UID-GID and PRESERVE-EXTENDED-ATTRIBUTES are completely ignored."
560 0 : (setq filename (expand-file-name filename)
561 0 : newname (expand-file-name newname))
562 0 : (with-tramp-progress-reporter
563 0 : (tramp-dissect-file-name
564 0 : (if (tramp-tramp-file-p filename) filename newname))
565 0 : 0 (format "Copying %s to %s" filename newname)
566 :
567 0 : (if (file-directory-p filename)
568 0 : (copy-directory
569 0 : filename newname keep-date 'parents 'copy-contents)
570 :
571 0 : (let ((tmpfile (file-local-copy filename)))
572 0 : (if tmpfile
573 : ;; Remote filename.
574 0 : (condition-case err
575 0 : (rename-file tmpfile newname ok-if-already-exists)
576 : ((error quit)
577 0 : (delete-file tmpfile)
578 0 : (signal (car err) (cdr err))))
579 :
580 : ;; Remote newname.
581 0 : (when (file-directory-p newname)
582 0 : (setq newname
583 0 : (expand-file-name (file-name-nondirectory filename) newname)))
584 :
585 0 : (with-parsed-tramp-file-name newname nil
586 0 : (when (and (not ok-if-already-exists)
587 0 : (file-exists-p newname))
588 0 : (tramp-error v 'file-already-exists newname))
589 :
590 : ;; We must also flush the cache of the directory, because
591 : ;; `file-attributes' reads the values from there.
592 0 : (tramp-flush-file-property v (file-name-directory localname))
593 0 : (tramp-flush-file-property v localname)
594 0 : (unless (tramp-smb-get-share v)
595 0 : (tramp-error
596 0 : v 'file-error "Target `%s' must contain a share name" newname))
597 0 : (unless (tramp-smb-send-command
598 0 : v (format "put \"%s\" \"%s\""
599 0 : (tramp-compat-file-name-unquote filename)
600 0 : (tramp-smb-get-localname v)))
601 0 : (tramp-error
602 0 : v 'file-error "Cannot copy `%s' to `%s'" filename newname))))))
603 :
604 : ;; KEEP-DATE handling.
605 0 : (when keep-date
606 0 : (set-file-times
607 0 : newname
608 0 : (tramp-compat-file-attribute-modification-time
609 0 : (file-attributes filename))))))
610 :
611 : (defun tramp-smb-handle-delete-directory (directory &optional recursive _trash)
612 : "Like `delete-directory' for Tramp files."
613 0 : (setq directory (directory-file-name (expand-file-name directory)))
614 0 : (when (file-exists-p directory)
615 0 : (when recursive
616 0 : (mapc
617 : (lambda (file)
618 0 : (if (file-directory-p file)
619 0 : (delete-directory file recursive)
620 0 : (delete-file file)))
621 : ;; We do not want to delete "." and "..".
622 0 : (directory-files directory 'full directory-files-no-dot-files-regexp)))
623 :
624 0 : (with-parsed-tramp-file-name directory nil
625 : ;; We must also flush the cache of the directory, because
626 : ;; `file-attributes' reads the values from there.
627 0 : (tramp-flush-file-property v (file-name-directory localname))
628 0 : (tramp-flush-directory-property v localname)
629 0 : (unless (tramp-smb-send-command
630 0 : v (format
631 : "%s \"%s\""
632 0 : (if (tramp-smb-get-cifs-capabilities v) "posix_rmdir" "rmdir")
633 0 : (tramp-smb-get-localname v)))
634 : ;; Error.
635 0 : (with-current-buffer (tramp-get-connection-buffer v)
636 0 : (goto-char (point-min))
637 0 : (search-forward-regexp tramp-smb-errors nil t)
638 0 : (tramp-error
639 0 : v 'file-error "%s `%s'" (match-string 0) directory))))))
640 :
641 : (defun tramp-smb-handle-delete-file (filename &optional _trash)
642 : "Like `delete-file' for Tramp files."
643 0 : (setq filename (expand-file-name filename))
644 0 : (when (file-exists-p filename)
645 0 : (with-parsed-tramp-file-name filename nil
646 : ;; We must also flush the cache of the directory, because
647 : ;; `file-attributes' reads the values from there.
648 0 : (tramp-flush-file-property v (file-name-directory localname))
649 0 : (tramp-flush-file-property v localname)
650 0 : (unless (tramp-smb-send-command
651 0 : v (format
652 : "%s \"%s\""
653 0 : (if (tramp-smb-get-cifs-capabilities v) "posix_unlink" "rm")
654 0 : (tramp-smb-get-localname v)))
655 : ;; Error.
656 0 : (with-current-buffer (tramp-get-connection-buffer v)
657 0 : (goto-char (point-min))
658 0 : (search-forward-regexp tramp-smb-errors nil t)
659 0 : (tramp-error
660 0 : v 'file-error "%s `%s'" (match-string 0) filename))))))
661 :
662 : (defun tramp-smb-handle-directory-files
663 : (directory &optional full match nosort)
664 : "Like `directory-files' for Tramp files."
665 0 : (let ((result (mapcar 'directory-file-name
666 0 : (file-name-all-completions "" directory))))
667 : ;; Discriminate with regexp.
668 0 : (when match
669 0 : (setq result
670 0 : (delete nil
671 0 : (mapcar (lambda (x) (when (string-match match x) x))
672 0 : result))))
673 : ;; Append directory.
674 0 : (when full
675 0 : (setq result
676 0 : (mapcar
677 0 : (lambda (x) (format "%s/%s" directory x))
678 0 : result)))
679 : ;; Sort them if necessary.
680 0 : (unless nosort (setq result (sort result 'string-lessp)))
681 0 : result))
682 :
683 : (defun tramp-smb-handle-expand-file-name (name &optional dir)
684 : "Like `expand-file-name' for Tramp files."
685 : ;; If DIR is not given, use DEFAULT-DIRECTORY or "/".
686 0 : (setq dir (or dir default-directory "/"))
687 : ;; Unless NAME is absolute, concat DIR and NAME.
688 0 : (unless (file-name-absolute-p name)
689 0 : (setq name (concat (file-name-as-directory dir) name)))
690 : ;; If NAME is not a Tramp file, run the real handler.
691 0 : (if (not (tramp-tramp-file-p name))
692 0 : (tramp-run-real-handler 'expand-file-name (list name nil))
693 : ;; Dissect NAME.
694 0 : (with-parsed-tramp-file-name name nil
695 : ;; Tilde expansion if necessary. We use the user name as share,
696 : ;; which is often the case in domains.
697 0 : (when (string-match "\\`/?~\\([^/]*\\)" localname)
698 0 : (setq localname
699 0 : (replace-match
700 0 : (if (zerop (length (match-string 1 localname)))
701 0 : user
702 0 : (match-string 1 localname))
703 0 : nil nil localname)))
704 : ;; Make the file name absolute.
705 0 : (unless (tramp-run-real-handler 'file-name-absolute-p (list localname))
706 0 : (setq localname (concat "/" localname)))
707 : ;; No tilde characters in file name, do normal
708 : ;; `expand-file-name' (this does "/./" and "/../").
709 0 : (tramp-make-tramp-file-name
710 0 : method user domain host port
711 0 : (tramp-run-real-handler 'expand-file-name (list localname))))))
712 :
713 : (defun tramp-smb-action-get-acl (proc vec)
714 : "Read ACL data from connection buffer."
715 0 : (unless (process-live-p proc)
716 : ;; Accept pending output.
717 0 : (while (tramp-accept-process-output proc 0.1))
718 0 : (with-current-buffer (tramp-get-connection-buffer vec)
719 : ;; There might be a hidden password prompt.
720 0 : (widen)
721 0 : (tramp-message vec 10 "\n%s" (buffer-string))
722 0 : (goto-char (point-min))
723 0 : (while (and (not (eobp)) (not (looking-at "^REVISION:")))
724 0 : (forward-line)
725 0 : (delete-region (point-min) (point)))
726 0 : (while (and (not (eobp)) (looking-at "^.+:.+"))
727 0 : (forward-line))
728 0 : (delete-region (point) (point-max))
729 0 : (throw 'tramp-action 'ok))))
730 :
731 : (defun tramp-smb-handle-file-acl (filename)
732 : "Like `file-acl' for Tramp files."
733 0 : (with-parsed-tramp-file-name filename nil
734 0 : (with-tramp-file-property v localname "file-acl"
735 0 : (when (executable-find tramp-smb-acl-program)
736 : ;; Set variables for computing the prompt for reading password.
737 0 : (setq tramp-current-method method
738 0 : tramp-current-user user
739 0 : tramp-current-domain domain
740 0 : tramp-current-host host
741 0 : tramp-current-port port)
742 :
743 0 : (let* ((share (tramp-smb-get-share v))
744 0 : (localname (replace-regexp-in-string
745 0 : "\\\\" "/" (tramp-smb-get-localname v)))
746 0 : (args (list (concat "//" host "/" share) "-E")))
747 :
748 0 : (if (not (zerop (length user)))
749 0 : (setq args (append args (list "-U" user)))
750 0 : (setq args (append args (list "-N"))))
751 :
752 0 : (when domain (setq args (append args (list "-W" domain))))
753 0 : (when port (setq args (append args (list "-p" port))))
754 0 : (when tramp-smb-conf
755 0 : (setq args (append args (list "-s" tramp-smb-conf))))
756 0 : (setq
757 : args
758 0 : (append args (list (tramp-unquote-shell-quote-argument localname)
759 0 : "2>/dev/null")))
760 :
761 0 : (unwind-protect
762 0 : (with-temp-buffer
763 : ;; Set the transfer process properties.
764 0 : (tramp-set-connection-property
765 0 : v "process-name" (buffer-name (current-buffer)))
766 0 : (tramp-set-connection-property
767 0 : v "process-buffer" (current-buffer))
768 :
769 : ;; Use an asynchronous processes. By this, password
770 : ;; can be handled.
771 0 : (let ((p (apply
772 : 'start-process
773 0 : (tramp-get-connection-name v)
774 0 : (tramp-get-connection-buffer v)
775 0 : tramp-smb-acl-program args)))
776 :
777 0 : (tramp-message
778 0 : v 6 "%s" (mapconcat 'identity (process-command p) " "))
779 0 : (tramp-set-connection-property p "vector" v)
780 0 : (process-put p 'adjust-window-size-function 'ignore)
781 0 : (set-process-query-on-exit-flag p nil)
782 0 : (tramp-process-actions p v nil tramp-smb-actions-get-acl)
783 0 : (when (> (point-max) (point-min))
784 0 : (substring-no-properties (buffer-string)))))
785 :
786 : ;; Reset the transfer process properties.
787 0 : (tramp-set-connection-property v "process-name" nil)
788 0 : (tramp-set-connection-property v "process-buffer" nil)))))))
789 :
790 : (defun tramp-smb-handle-file-attributes (filename &optional id-format)
791 : "Like `file-attributes' for Tramp files."
792 0 : (unless id-format (setq id-format 'integer))
793 0 : (ignore-errors
794 0 : (with-parsed-tramp-file-name filename nil
795 0 : (with-tramp-file-property
796 0 : v localname (format "file-attributes-%s" id-format)
797 0 : (if (tramp-smb-get-stat-capability v)
798 0 : (tramp-smb-do-file-attributes-with-stat v id-format)
799 : ;; Reading just the filename entry via "dir localname" is not
800 : ;; possible, because when filename is a directory, some
801 : ;; smbclient versions return the content of the directory, and
802 : ;; other versions don't. Therefore, the whole content of the
803 : ;; upper directory is retrieved, and the entry of the filename
804 : ;; is extracted from.
805 0 : (let* ((entries (tramp-smb-get-file-entries
806 0 : (file-name-directory filename)))
807 0 : (entry (assoc (file-name-nondirectory filename) entries))
808 0 : (uid (if (equal id-format 'string) "nobody" -1))
809 0 : (gid (if (equal id-format 'string) "nogroup" -1))
810 0 : (inode (tramp-get-inode v))
811 0 : (device (tramp-get-device v)))
812 :
813 : ;; Check result.
814 0 : (when entry
815 0 : (list (and (string-match "d" (nth 1 entry))
816 0 : t) ;0 file type
817 : -1 ;1 link count
818 0 : uid ;2 uid
819 0 : gid ;3 gid
820 : '(0 0) ;4 atime
821 0 : (nth 3 entry) ;5 mtime
822 : '(0 0) ;6 ctime
823 0 : (nth 2 entry) ;7 size
824 0 : (nth 1 entry) ;8 mode
825 : nil ;9 gid weird
826 0 : inode ;10 inode number
827 0 : device)))))))) ;11 file system number
828 :
829 : (defun tramp-smb-do-file-attributes-with-stat (vec &optional id-format)
830 : "Implement `file-attributes' for Tramp files using stat command."
831 0 : (tramp-message
832 0 : vec 5 "file attributes with stat: %s" (tramp-file-name-localname vec))
833 0 : (with-current-buffer (tramp-get-connection-buffer vec)
834 0 : (let* (size id link uid gid atime mtime ctime mode inode)
835 0 : (when (tramp-smb-send-command
836 0 : vec (format "stat \"%s\"" (tramp-smb-get-localname vec)))
837 :
838 : ;; Loop the listing.
839 0 : (goto-char (point-min))
840 0 : (unless (re-search-forward tramp-smb-errors nil t)
841 0 : (while (not (eobp))
842 0 : (cond
843 0 : ((looking-at
844 0 : "Size:\\s-+\\([0-9]+\\)\\s-+Blocks:\\s-+[0-9]+\\s-+\\(\\w+\\)")
845 0 : (setq size (string-to-number (match-string 1))
846 0 : id (if (string-equal "directory" (match-string 2)) t
847 0 : (if (string-equal "symbolic" (match-string 2)) ""))))
848 0 : ((looking-at
849 0 : "Inode:\\s-+\\([0-9]+\\)\\s-+Links:\\s-+\\([0-9]+\\)")
850 0 : (setq inode (string-to-number (match-string 1))
851 0 : link (string-to-number (match-string 2))))
852 0 : ((looking-at
853 0 : "Access:\\s-+([0-9]+/\\(\\S-+\\))\\s-+Uid:\\s-+\\([0-9]+\\)\\s-+Gid:\\s-+\\([0-9]+\\)")
854 0 : (setq mode (match-string 1)
855 0 : uid (if (equal id-format 'string) (match-string 2)
856 0 : (string-to-number (match-string 2)))
857 0 : gid (if (equal id-format 'string) (match-string 3)
858 0 : (string-to-number (match-string 3)))))
859 0 : ((looking-at
860 0 : "Access:\\s-+\\([0-9]+\\)-\\([0-9]+\\)-\\([0-9]+\\)\\s-+\\([0-9]+\\):\\([0-9]+\\):\\([0-9]+\\)")
861 0 : (setq atime
862 0 : (encode-time
863 0 : (string-to-number (match-string 6)) ;; sec
864 0 : (string-to-number (match-string 5)) ;; min
865 0 : (string-to-number (match-string 4)) ;; hour
866 0 : (string-to-number (match-string 3)) ;; day
867 0 : (string-to-number (match-string 2)) ;; month
868 0 : (string-to-number (match-string 1))))) ;; year
869 0 : ((looking-at
870 0 : "Modify:\\s-+\\([0-9]+\\)-\\([0-9]+\\)-\\([0-9]+\\)\\s-+\\([0-9]+\\):\\([0-9]+\\):\\([0-9]+\\)")
871 0 : (setq mtime
872 0 : (encode-time
873 0 : (string-to-number (match-string 6)) ;; sec
874 0 : (string-to-number (match-string 5)) ;; min
875 0 : (string-to-number (match-string 4)) ;; hour
876 0 : (string-to-number (match-string 3)) ;; day
877 0 : (string-to-number (match-string 2)) ;; month
878 0 : (string-to-number (match-string 1))))) ;; year
879 0 : ((looking-at
880 0 : "Change:\\s-+\\([0-9]+\\)-\\([0-9]+\\)-\\([0-9]+\\)\\s-+\\([0-9]+\\):\\([0-9]+\\):\\([0-9]+\\)")
881 0 : (setq ctime
882 0 : (encode-time
883 0 : (string-to-number (match-string 6)) ;; sec
884 0 : (string-to-number (match-string 5)) ;; min
885 0 : (string-to-number (match-string 4)) ;; hour
886 0 : (string-to-number (match-string 3)) ;; day
887 0 : (string-to-number (match-string 2)) ;; month
888 0 : (string-to-number (match-string 1)))))) ;; year
889 0 : (forward-line))
890 : ;; Return the result.
891 0 : (list id link uid gid atime mtime ctime size mode nil inode
892 0 : (tramp-get-device vec)))))))
893 :
894 : (defun tramp-smb-handle-file-directory-p (filename)
895 : "Like `file-directory-p' for Tramp files."
896 0 : (and (file-exists-p filename)
897 0 : (eq ?d
898 0 : (aref (tramp-compat-file-attribute-modes (file-attributes filename))
899 0 : 0))))
900 :
901 : (defun tramp-smb-handle-file-local-copy (filename)
902 : "Like `file-local-copy' for Tramp files."
903 0 : (with-parsed-tramp-file-name filename nil
904 0 : (unless (file-exists-p filename)
905 0 : (tramp-error
906 0 : v tramp-file-missing
907 0 : "Cannot make local copy of non-existing file `%s'" filename))
908 0 : (let ((tmpfile (tramp-compat-make-temp-file filename)))
909 0 : (with-tramp-progress-reporter
910 0 : v 3 (format "Fetching %s to tmp file %s" filename tmpfile)
911 0 : (unless (tramp-smb-send-command
912 0 : v (format "get \"%s\" \"%s\""
913 0 : (tramp-smb-get-localname v) tmpfile))
914 : ;; Oops, an error. We shall cleanup.
915 0 : (delete-file tmpfile)
916 0 : (tramp-error
917 0 : v 'file-error "Cannot make local copy of file `%s'" filename)))
918 0 : tmpfile)))
919 :
920 : ;; This function should return "foo/" for directories and "bar" for
921 : ;; files.
922 : (defun tramp-smb-handle-file-name-all-completions (filename directory)
923 : "Like `file-name-all-completions' for Tramp files."
924 0 : (all-completions
925 0 : filename
926 0 : (with-parsed-tramp-file-name (expand-file-name directory) nil
927 0 : (with-tramp-file-property v localname "file-name-all-completions"
928 0 : (save-match-data
929 0 : (delete-dups
930 0 : (mapcar
931 : (lambda (x)
932 0 : (list
933 0 : (if (string-match "d" (nth 1 x))
934 0 : (file-name-as-directory (nth 0 x))
935 0 : (nth 0 x))))
936 0 : (tramp-smb-get-file-entries directory))))))))
937 :
938 : (defun tramp-smb-handle-file-writable-p (filename)
939 : "Like `file-writable-p' for Tramp files."
940 0 : (if (file-exists-p filename)
941 0 : (string-match
942 : "w"
943 0 : (or (tramp-compat-file-attribute-modes (file-attributes filename)) ""))
944 0 : (let ((dir (file-name-directory filename)))
945 0 : (and (file-exists-p dir)
946 0 : (file-writable-p dir)))))
947 :
948 : (defun tramp-smb-handle-insert-directory
949 : (filename switches &optional wildcard full-directory-p)
950 : "Like `insert-directory' for Tramp files."
951 0 : (setq filename (expand-file-name filename))
952 0 : (unless switches (setq switches ""))
953 : ;; Mark trailing "/".
954 0 : (when (and (zerop (length (file-name-nondirectory filename)))
955 0 : (not full-directory-p))
956 0 : (setq switches (concat switches "F")))
957 0 : (if full-directory-p
958 : ;; Called from `dired-add-entry'.
959 0 : (setq filename (file-name-as-directory filename))
960 0 : (setq filename (directory-file-name filename)))
961 0 : (with-parsed-tramp-file-name filename nil
962 0 : (with-tramp-progress-reporter v 0 (format "Opening directory %s" filename)
963 0 : (save-match-data
964 0 : (let ((base (file-name-nondirectory filename))
965 : ;; We should not destroy the cache entry.
966 0 : (entries (copy-sequence
967 0 : (tramp-smb-get-file-entries
968 0 : (file-name-directory filename)))))
969 :
970 0 : (when wildcard
971 0 : (string-match "\\." base)
972 0 : (setq base (replace-match "\\\\." nil nil base))
973 0 : (string-match "\\*" base)
974 0 : (setq base (replace-match ".*" nil nil base))
975 0 : (string-match "\\?" base)
976 0 : (setq base (replace-match ".?" nil nil base)))
977 :
978 : ;; Filter entries.
979 0 : (setq entries
980 0 : (delq
981 : nil
982 0 : (if (or wildcard (zerop (length base)))
983 : ;; Check for matching entries.
984 0 : (mapcar
985 : (lambda (x)
986 0 : (when (string-match
987 0 : (format "^%s" base) (nth 0 x))
988 0 : x))
989 0 : entries)
990 : ;; We just need the only and only entry FILENAME.
991 0 : (list (assoc base entries)))))
992 :
993 : ;; Sort entries.
994 0 : (setq entries
995 0 : (sort
996 0 : entries
997 : (lambda (x y)
998 0 : (if (string-match "t" switches)
999 : ;; Sort by date.
1000 0 : (time-less-p (nth 3 y) (nth 3 x))
1001 : ;; Sort by name.
1002 0 : (string-lessp (nth 0 x) (nth 0 y))))))
1003 :
1004 : ;; Handle "-F" switch.
1005 0 : (when (string-match "F" switches)
1006 0 : (mapc
1007 : (lambda (x)
1008 0 : (when (not (zerop (length (car x))))
1009 0 : (cond
1010 0 : ((char-equal ?d (string-to-char (nth 1 x)))
1011 0 : (setcar x (concat (car x) "/")))
1012 0 : ((char-equal ?x (string-to-char (nth 1 x)))
1013 0 : (setcar x (concat (car x) "*"))))))
1014 0 : entries))
1015 :
1016 : ;; Print entries.
1017 0 : (mapc
1018 : (lambda (x)
1019 0 : (when (not (zerop (length (nth 0 x))))
1020 0 : (when (string-match "l" switches)
1021 0 : (let ((attr
1022 0 : (when (tramp-smb-get-stat-capability v)
1023 0 : (ignore-errors
1024 0 : (file-attributes filename 'string)))))
1025 0 : (insert
1026 0 : (format
1027 : "%10s %3d %-8s %-8s %8s %s "
1028 0 : (or (tramp-compat-file-attribute-modes attr) (nth 1 x))
1029 0 : (or (tramp-compat-file-attribute-link-number attr) 1)
1030 0 : (or (tramp-compat-file-attribute-user-id attr) "nobody")
1031 0 : (or (tramp-compat-file-attribute-group-id attr) "nogroup")
1032 0 : (or (tramp-compat-file-attribute-size attr) (nth 2 x))
1033 0 : (format-time-string
1034 0 : (if (time-less-p (time-subtract (current-time) (nth 3 x))
1035 0 : tramp-half-a-year)
1036 : "%b %e %R"
1037 0 : "%b %e %Y")
1038 0 : (nth 3 x)))))) ; date
1039 :
1040 : ;; We mark the file name. The inserted name could be
1041 : ;; from somewhere else, so we use the relative file name
1042 : ;; of `default-directory'.
1043 0 : (let ((start (point)))
1044 0 : (insert
1045 0 : (format
1046 : "%s\n"
1047 0 : (file-relative-name
1048 0 : (expand-file-name
1049 0 : (nth 0 x) (file-name-directory filename))
1050 0 : (when full-directory-p (file-name-directory filename)))))
1051 0 : (put-text-property start (1- (point)) 'dired-filename t))
1052 0 : (forward-line)
1053 0 : (beginning-of-line)))
1054 0 : entries))))))
1055 :
1056 : (defun tramp-smb-handle-make-directory (dir &optional parents)
1057 : "Like `make-directory' for Tramp files."
1058 0 : (setq dir (directory-file-name (expand-file-name dir)))
1059 0 : (unless (file-name-absolute-p dir)
1060 0 : (setq dir (expand-file-name dir default-directory)))
1061 0 : (with-parsed-tramp-file-name dir nil
1062 0 : (save-match-data
1063 0 : (let* ((ldir (file-name-directory dir)))
1064 : ;; Make missing directory parts.
1065 0 : (when (and parents
1066 0 : (tramp-smb-get-share v)
1067 0 : (not (file-directory-p ldir)))
1068 0 : (make-directory ldir parents))
1069 : ;; Just do it.
1070 0 : (when (file-directory-p ldir)
1071 0 : (make-directory-internal dir))
1072 0 : (unless (file-directory-p dir)
1073 0 : (tramp-error v 'file-error "Couldn't make directory %s" dir))))))
1074 :
1075 : (defun tramp-smb-handle-make-directory-internal (directory)
1076 : "Like `make-directory-internal' for Tramp files."
1077 0 : (setq directory (directory-file-name (expand-file-name directory)))
1078 0 : (unless (file-name-absolute-p directory)
1079 0 : (setq directory (expand-file-name directory default-directory)))
1080 0 : (with-parsed-tramp-file-name directory nil
1081 0 : (save-match-data
1082 0 : (let* ((file (tramp-smb-get-localname v)))
1083 0 : (when (file-directory-p (file-name-directory directory))
1084 0 : (tramp-smb-send-command
1085 0 : v
1086 0 : (if (tramp-smb-get-cifs-capabilities v)
1087 0 : (format "posix_mkdir \"%s\" %o" file (default-file-modes))
1088 0 : (format "mkdir \"%s\"" file)))
1089 : ;; We must also flush the cache of the directory, because
1090 : ;; `file-attributes' reads the values from there.
1091 0 : (tramp-flush-file-property v (file-name-directory localname))
1092 0 : (tramp-flush-file-property v localname))
1093 0 : (unless (file-directory-p directory)
1094 0 : (tramp-error
1095 0 : v 'file-error "Couldn't make directory %s" directory))))))
1096 :
1097 : (defun tramp-smb-handle-make-symbolic-link
1098 : (filename linkname &optional ok-if-already-exists)
1099 : "Like `make-symbolic-link' for Tramp files.
1100 : If LINKNAME is a non-Tramp file, it is used verbatim as the target of
1101 : the symlink. If LINKNAME is a Tramp file, only the localname component is
1102 : used as the target of the symlink.
1103 :
1104 : If LINKNAME is a Tramp file and the localname component is relative, then
1105 : it is expanded first, before the localname component is taken. Note that
1106 : this can give surprising results if the user/host for the source and
1107 : target of the symlink differ."
1108 0 : (unless (tramp-equal-remote filename linkname)
1109 0 : (with-parsed-tramp-file-name
1110 0 : (if (tramp-tramp-file-p filename) filename linkname) nil
1111 0 : (tramp-error
1112 0 : v 'file-error
1113 : "make-symbolic-link: %s"
1114 0 : "only implemented for same method, same user, same host")))
1115 0 : (with-parsed-tramp-file-name filename v1
1116 0 : (with-parsed-tramp-file-name linkname v2
1117 0 : (when (file-directory-p filename)
1118 0 : (tramp-error
1119 0 : v2 'file-error
1120 0 : "make-symbolic-link: %s must not be a directory" filename))
1121 0 : (when (and (not ok-if-already-exists)
1122 0 : (file-exists-p linkname)
1123 0 : (not (numberp ok-if-already-exists))
1124 0 : (y-or-n-p
1125 0 : (format
1126 : "File %s already exists; make it a new name anyway? "
1127 0 : linkname)))
1128 0 : (tramp-error v2 'file-already-exists linkname))
1129 0 : (unless (tramp-smb-get-cifs-capabilities v1)
1130 0 : (tramp-error v2 'file-error "make-symbolic-link not supported"))
1131 : ;; We must also flush the cache of the directory, because
1132 : ;; `file-attributes' reads the values from there.
1133 0 : (tramp-flush-file-property v2 (file-name-directory v2-localname))
1134 0 : (tramp-flush-file-property v2 v2-localname)
1135 0 : (unless
1136 0 : (tramp-smb-send-command
1137 0 : v1
1138 0 : (format
1139 : "symlink \"%s\" \"%s\""
1140 0 : (tramp-smb-get-localname v1)
1141 0 : (tramp-smb-get-localname v2)))
1142 0 : (tramp-error
1143 0 : v2 'file-error
1144 : "error with make-symbolic-link, see buffer `%s' for details"
1145 0 : (buffer-name))))))
1146 :
1147 : (defun tramp-smb-handle-process-file
1148 : (program &optional infile destination display &rest args)
1149 : "Like `process-file' for Tramp files."
1150 : ;; The implementation is not complete yet.
1151 0 : (when (and (numberp destination) (zerop destination))
1152 0 : (error "Implementation does not handle immediate return"))
1153 :
1154 0 : (with-parsed-tramp-file-name default-directory nil
1155 0 : (let* ((name (file-name-nondirectory program))
1156 0 : (name1 name)
1157 : (i 0)
1158 : input tmpinput outbuf command ret)
1159 :
1160 : ;; Determine input.
1161 0 : (when infile
1162 0 : (setq infile (expand-file-name infile))
1163 0 : (if (tramp-equal-remote default-directory infile)
1164 : ;; INFILE is on the same remote host.
1165 0 : (setq input (with-parsed-tramp-file-name infile nil localname))
1166 : ;; INFILE must be copied to remote host.
1167 0 : (setq input (tramp-make-tramp-temp-file v)
1168 : tmpinput
1169 0 : (tramp-make-tramp-file-name method user domain host port input))
1170 0 : (copy-file infile tmpinput t))
1171 : ;; Transform input into a filename powershell does understand.
1172 0 : (setq input (format "//%s%s" host input)))
1173 :
1174 : ;; Determine output.
1175 0 : (cond
1176 : ;; Just a buffer.
1177 0 : ((bufferp destination)
1178 0 : (setq outbuf destination))
1179 : ;; A buffer name.
1180 0 : ((stringp destination)
1181 0 : (setq outbuf (get-buffer-create destination)))
1182 : ;; (REAL-DESTINATION ERROR-DESTINATION)
1183 0 : ((consp destination)
1184 : ;; output.
1185 0 : (cond
1186 0 : ((bufferp (car destination))
1187 0 : (setq outbuf (car destination)))
1188 0 : ((stringp (car destination))
1189 0 : (setq outbuf (get-buffer-create (car destination))))
1190 0 : ((car destination)
1191 0 : (setq outbuf (current-buffer))))
1192 : ;; stderr.
1193 0 : (tramp-message v 2 "%s" "STDERR not supported"))
1194 : ;; 't
1195 0 : (destination
1196 0 : (setq outbuf (current-buffer))))
1197 :
1198 : ;; Construct command.
1199 0 : (setq command (mapconcat 'identity (cons program args) " ")
1200 0 : command (if input
1201 0 : (format
1202 : "get-content %s | & %s"
1203 0 : (tramp-smb-shell-quote-argument input) command)
1204 0 : (format "& %s" command)))
1205 :
1206 0 : (while (get-process name1)
1207 : ;; NAME must be unique as process name.
1208 0 : (setq i (1+ i)
1209 0 : name1 (format "%s<%d>" name i)))
1210 :
1211 : ;; Set the new process properties.
1212 0 : (tramp-set-connection-property v "process-name" name1)
1213 0 : (tramp-set-connection-property
1214 0 : v "process-buffer"
1215 0 : (or outbuf (generate-new-buffer tramp-temp-buffer-name)))
1216 :
1217 : ;; Call it.
1218 0 : (condition-case nil
1219 0 : (with-current-buffer (tramp-get-connection-buffer v)
1220 : ;; Preserve buffer contents.
1221 0 : (narrow-to-region (point-max) (point-max))
1222 0 : (tramp-smb-call-winexe v)
1223 0 : (when (tramp-smb-get-share v)
1224 0 : (tramp-smb-send-command
1225 0 : v (format "cd \"//%s%s\"" host (file-name-directory localname))))
1226 0 : (tramp-smb-send-command v command)
1227 : ;; Preserve command output.
1228 0 : (narrow-to-region (point-max) (point-max))
1229 0 : (let ((p (tramp-get-connection-process v)))
1230 0 : (tramp-smb-send-command v "exit $lasterrorcode")
1231 0 : (while (process-live-p p)
1232 0 : (sleep-for 0.1)
1233 0 : (setq ret (process-exit-status p))))
1234 0 : (delete-region (point-min) (point-max))
1235 0 : (widen))
1236 :
1237 : ;; When the user did interrupt, we should do it also. We use
1238 : ;; return code -1 as marker.
1239 : (quit
1240 0 : (setq ret -1))
1241 : ;; Handle errors.
1242 : (error
1243 0 : (setq ret 1)))
1244 :
1245 : ;; We should redisplay the output.
1246 0 : (when (and display outbuf (get-buffer-window outbuf t)) (redisplay))
1247 :
1248 : ;; Cleanup. We remove all file cache values for the connection,
1249 : ;; because the remote process could have changed them.
1250 0 : (tramp-set-connection-property v "process-name" nil)
1251 0 : (tramp-set-connection-property v "process-buffer" nil)
1252 0 : (when tmpinput (delete-file tmpinput))
1253 0 : (unless outbuf
1254 0 : (kill-buffer (tramp-get-connection-property v "process-buffer" nil)))
1255 :
1256 0 : (unless process-file-side-effects
1257 0 : (tramp-flush-directory-property v ""))
1258 :
1259 : ;; Return exit status.
1260 0 : (if (equal ret -1)
1261 0 : (keyboard-quit)
1262 0 : ret))))
1263 :
1264 : (defun tramp-smb-handle-rename-file
1265 : (filename newname &optional ok-if-already-exists)
1266 : "Like `rename-file' for Tramp files."
1267 0 : (setq filename (expand-file-name filename)
1268 0 : newname (expand-file-name newname))
1269 :
1270 0 : (when (and (not ok-if-already-exists)
1271 0 : (file-exists-p newname))
1272 0 : (tramp-error
1273 0 : (tramp-dissect-file-name
1274 0 : (if (tramp-tramp-file-p filename) filename newname))
1275 0 : 'file-already-exists newname))
1276 :
1277 0 : (with-tramp-progress-reporter
1278 0 : (tramp-dissect-file-name
1279 0 : (if (tramp-tramp-file-p filename) filename newname))
1280 0 : 0 (format "Renaming %s to %s" filename newname)
1281 :
1282 0 : (if (and (not (file-exists-p newname))
1283 0 : (tramp-equal-remote filename newname)
1284 0 : (string-equal
1285 0 : (tramp-smb-get-share (tramp-dissect-file-name filename))
1286 0 : (tramp-smb-get-share (tramp-dissect-file-name newname))))
1287 : ;; We can rename directly.
1288 0 : (with-parsed-tramp-file-name filename v1
1289 0 : (with-parsed-tramp-file-name newname v2
1290 :
1291 : ;; We must also flush the cache of the directory, because
1292 : ;; `file-attributes' reads the values from there.
1293 0 : (tramp-flush-file-property v1 (file-name-directory v1-localname))
1294 0 : (tramp-flush-file-property v1 v1-localname)
1295 0 : (tramp-flush-file-property v2 (file-name-directory v2-localname))
1296 0 : (tramp-flush-file-property v2 v2-localname)
1297 0 : (unless (tramp-smb-get-share v2)
1298 0 : (tramp-error
1299 0 : v2 'file-error "Target `%s' must contain a share name" newname))
1300 0 : (unless (tramp-smb-send-command
1301 0 : v2 (format "rename \"%s\" \"%s\""
1302 0 : (tramp-smb-get-localname v1)
1303 0 : (tramp-smb-get-localname v2)))
1304 0 : (tramp-error v2 'file-error "Cannot rename `%s'" filename))))
1305 :
1306 : ;; We must rename via copy.
1307 0 : (copy-file
1308 0 : filename newname ok-if-already-exists 'keep-time 'preserve-uid-gid)
1309 0 : (if (file-directory-p filename)
1310 0 : (delete-directory filename 'recursive)
1311 0 : (delete-file filename)))))
1312 :
1313 : (defun tramp-smb-action-set-acl (proc vec)
1314 : "Read ACL data from connection buffer."
1315 0 : (unless (process-live-p proc)
1316 : ;; Accept pending output.
1317 0 : (while (tramp-accept-process-output proc 0.1))
1318 0 : (with-current-buffer (tramp-get-connection-buffer vec)
1319 0 : (tramp-message vec 10 "\n%s" (buffer-string))
1320 0 : (throw 'tramp-action 'ok))))
1321 :
1322 : (defun tramp-smb-handle-set-file-acl (filename acl-string)
1323 : "Like `set-file-acl' for Tramp files."
1324 0 : (ignore-errors
1325 0 : (with-parsed-tramp-file-name filename nil
1326 0 : (when (and (stringp acl-string) (executable-find tramp-smb-acl-program))
1327 : ;; Set variables for computing the prompt for reading password.
1328 0 : (setq tramp-current-method method
1329 0 : tramp-current-user user
1330 0 : tramp-current-domain domain
1331 0 : tramp-current-host host
1332 0 : tramp-current-port port)
1333 0 : (tramp-set-file-property v localname "file-acl" 'undef)
1334 :
1335 0 : (let* ((share (tramp-smb-get-share v))
1336 0 : (localname (replace-regexp-in-string
1337 0 : "\\\\" "/" (tramp-smb-get-localname v)))
1338 0 : (args (list (concat "//" host "/" share) "-E" "-S"
1339 0 : (replace-regexp-in-string
1340 0 : "\n" "," acl-string))))
1341 :
1342 0 : (if (not (zerop (length user)))
1343 0 : (setq args (append args (list "-U" user)))
1344 0 : (setq args (append args (list "-N"))))
1345 :
1346 0 : (when domain (setq args (append args (list "-W" domain))))
1347 0 : (when port (setq args (append args (list "-p" port))))
1348 0 : (when tramp-smb-conf
1349 0 : (setq args (append args (list "-s" tramp-smb-conf))))
1350 0 : (setq
1351 : args
1352 0 : (append args (list (tramp-unquote-shell-quote-argument localname)
1353 : "&&" "echo" "tramp_exit_status" "0"
1354 0 : "||" "echo" "tramp_exit_status" "1")))
1355 :
1356 0 : (unwind-protect
1357 0 : (with-temp-buffer
1358 : ;; Set the transfer process properties.
1359 0 : (tramp-set-connection-property
1360 0 : v "process-name" (buffer-name (current-buffer)))
1361 0 : (tramp-set-connection-property
1362 0 : v "process-buffer" (current-buffer))
1363 :
1364 : ;; Use an asynchronous processes. By this, password can
1365 : ;; be handled.
1366 0 : (let ((p (apply
1367 : 'start-process
1368 0 : (tramp-get-connection-name v)
1369 0 : (tramp-get-connection-buffer v)
1370 0 : tramp-smb-acl-program args)))
1371 :
1372 0 : (tramp-message
1373 0 : v 6 "%s" (mapconcat 'identity (process-command p) " "))
1374 0 : (tramp-set-connection-property p "vector" v)
1375 0 : (process-put p 'adjust-window-size-function 'ignore)
1376 0 : (set-process-query-on-exit-flag p nil)
1377 0 : (tramp-process-actions p v nil tramp-smb-actions-set-acl)
1378 0 : (goto-char (point-max))
1379 0 : (unless (re-search-backward "tramp_exit_status [0-9]+" nil t)
1380 0 : (tramp-error
1381 0 : v 'file-error
1382 0 : "Couldn't find exit status of `%s'" tramp-smb-acl-program))
1383 0 : (skip-chars-forward "^ ")
1384 0 : (when (zerop (read (current-buffer)))
1385 : ;; Success.
1386 0 : (tramp-set-file-property v localname "file-acl" acl-string)
1387 0 : t)))
1388 :
1389 : ;; Reset the transfer process properties.
1390 0 : (tramp-set-connection-property v "process-name" nil)
1391 0 : (tramp-set-connection-property v "process-buffer" nil)))))))
1392 :
1393 : (defun tramp-smb-handle-set-file-modes (filename mode)
1394 : "Like `set-file-modes' for Tramp files."
1395 0 : (with-parsed-tramp-file-name filename nil
1396 0 : (when (tramp-smb-get-cifs-capabilities v)
1397 0 : (tramp-flush-file-property v localname)
1398 0 : (unless (tramp-smb-send-command
1399 0 : v (format "chmod \"%s\" %o" (tramp-smb-get-localname v) mode))
1400 0 : (tramp-error
1401 0 : v 'file-error "Error while changing file's mode %s" filename)))))
1402 :
1403 : ;; We use BUFFER also as connection buffer during setup. Because of
1404 : ;; this, its original contents must be saved, and restored once
1405 : ;; connection has been setup.
1406 : (defun tramp-smb-handle-start-file-process (name buffer program &rest args)
1407 : "Like `start-file-process' for Tramp files."
1408 0 : (with-parsed-tramp-file-name default-directory nil
1409 0 : (let* ((buffer
1410 0 : (if buffer
1411 0 : (get-buffer-create buffer)
1412 : ;; BUFFER can be nil. We use a temporary buffer.
1413 0 : (generate-new-buffer tramp-temp-buffer-name)))
1414 0 : (command (mapconcat 'identity (cons program args) " "))
1415 0 : (bmp (and (buffer-live-p buffer) (buffer-modified-p buffer)))
1416 0 : (name1 name)
1417 : (i 0))
1418 0 : (unwind-protect
1419 0 : (save-excursion
1420 0 : (save-restriction
1421 0 : (while (get-process name1)
1422 : ;; NAME must be unique as process name.
1423 0 : (setq i (1+ i)
1424 0 : name1 (format "%s<%d>" name i)))
1425 : ;; Set the new process properties.
1426 0 : (tramp-set-connection-property v "process-name" name1)
1427 0 : (tramp-set-connection-property v "process-buffer" buffer)
1428 : ;; Activate narrowing in order to save BUFFER contents.
1429 0 : (with-current-buffer (tramp-get-connection-buffer v)
1430 0 : (let ((buffer-undo-list t))
1431 0 : (narrow-to-region (point-max) (point-max))
1432 0 : (tramp-smb-call-winexe v)
1433 0 : (when (tramp-smb-get-share v)
1434 0 : (tramp-smb-send-command
1435 0 : v (format
1436 : "cd \"//%s%s\""
1437 0 : host (file-name-directory localname))))
1438 0 : (tramp-message v 6 "(%s); exit" command)
1439 0 : (tramp-send-string v command)))
1440 : ;; Return value.
1441 0 : (tramp-get-connection-process v)))
1442 :
1443 : ;; Save exit.
1444 0 : (with-current-buffer (tramp-get-connection-buffer v)
1445 0 : (if (string-match tramp-temp-buffer-name (buffer-name))
1446 0 : (progn
1447 0 : (set-process-buffer (tramp-get-connection-process v) nil)
1448 0 : (kill-buffer (current-buffer)))
1449 0 : (set-buffer-modified-p bmp)))
1450 0 : (tramp-set-connection-property v "process-name" nil)
1451 0 : (tramp-set-connection-property v "process-buffer" nil)))))
1452 :
1453 : (defun tramp-smb-handle-substitute-in-file-name (filename)
1454 : "Like `handle-substitute-in-file-name' for Tramp files.
1455 : \"//\" substitutes only in the local filename part. Catches
1456 : errors for shares like \"C$/\", which are common in Microsoft Windows."
1457 : ;; Check, whether the local part is a quoted file name.
1458 0 : (if (tramp-compat-file-name-quoted-p filename)
1459 0 : filename
1460 0 : (with-parsed-tramp-file-name filename nil
1461 : ;; Ignore in LOCALNAME everything before "//".
1462 0 : (when (and (stringp localname) (string-match ".+?/\\(/\\|~\\)" localname))
1463 0 : (setq filename
1464 0 : (concat (file-remote-p filename)
1465 0 : (replace-match "\\1" nil nil localname)))))
1466 0 : (condition-case nil
1467 0 : (tramp-run-real-handler 'substitute-in-file-name (list filename))
1468 0 : (error filename))))
1469 :
1470 : (defun tramp-smb-handle-write-region
1471 : (start end filename &optional append visit lockname mustbenew)
1472 : "Like `write-region' for Tramp files."
1473 0 : (setq filename (expand-file-name filename))
1474 0 : (with-parsed-tramp-file-name filename nil
1475 0 : (when (and mustbenew (file-exists-p filename)
1476 0 : (or (eq mustbenew 'excl)
1477 0 : (not
1478 0 : (y-or-n-p
1479 0 : (format "File %s exists; overwrite anyway? " filename)))))
1480 0 : (tramp-error v 'file-already-exists filename))
1481 :
1482 : ;; We must also flush the cache of the directory, because
1483 : ;; `file-attributes' reads the values from there.
1484 0 : (tramp-flush-file-property v (file-name-directory localname))
1485 0 : (tramp-flush-file-property v localname)
1486 0 : (let ((curbuf (current-buffer))
1487 0 : (tmpfile (tramp-compat-make-temp-file filename)))
1488 0 : (when (and append (file-exists-p filename))
1489 0 : (copy-file filename tmpfile 'ok))
1490 : ;; We say `no-message' here because we don't want the visited file
1491 : ;; modtime data to be clobbered from the temp file. We call
1492 : ;; `set-visited-file-modtime' ourselves later on.
1493 0 : (tramp-run-real-handler
1494 0 : 'write-region (list start end tmpfile append 'no-message lockname))
1495 :
1496 0 : (with-tramp-progress-reporter
1497 0 : v 3 (format "Moving tmp file %s to %s" tmpfile filename)
1498 0 : (unwind-protect
1499 0 : (unless (tramp-smb-send-command
1500 0 : v (format "put %s \"%s\""
1501 0 : tmpfile (tramp-smb-get-localname v)))
1502 0 : (tramp-error v 'file-error "Cannot write `%s'" filename))
1503 0 : (delete-file tmpfile)))
1504 :
1505 0 : (unless (equal curbuf (current-buffer))
1506 0 : (tramp-error
1507 0 : v 'file-error
1508 0 : "Buffer has changed from `%s' to `%s'" curbuf (current-buffer)))
1509 0 : (when (eq visit t)
1510 0 : (set-visited-file-modtime)))))
1511 :
1512 :
1513 : ;; Internal file name functions.
1514 :
1515 : (defun tramp-smb-get-share (vec)
1516 : "Returns the share name of LOCALNAME."
1517 0 : (save-match-data
1518 0 : (let ((localname (tramp-file-name-unquote-localname vec)))
1519 0 : (when (string-match "^/?\\([^/]+\\)/" localname)
1520 0 : (match-string 1 localname)))))
1521 :
1522 : (defun tramp-smb-get-localname (vec)
1523 : "Returns the file name of LOCALNAME.
1524 : If VEC has no cifs capabilities, exchange \"/\" by \"\\\\\"."
1525 0 : (save-match-data
1526 0 : (let ((localname (tramp-file-name-unquote-localname vec)))
1527 0 : (setq
1528 : localname
1529 0 : (if (string-match "^/?[^/]+\\(/.*\\)" localname)
1530 : ;; There is a share, separated by "/".
1531 0 : (if (not (tramp-smb-get-cifs-capabilities vec))
1532 0 : (mapconcat
1533 0 : (lambda (x) (if (equal x ?/) "\\" (char-to-string x)))
1534 0 : (match-string 1 localname) "")
1535 0 : (match-string 1 localname))
1536 : ;; There is just a share.
1537 0 : (if (string-match "^/?\\([^/]+\\)$" localname)
1538 0 : (match-string 1 localname)
1539 0 : "")))
1540 :
1541 : ;; Sometimes we have discarded `substitute-in-file-name'.
1542 0 : (when (string-match "\\(\\$\\$\\)\\(/\\|$\\)" localname)
1543 0 : (setq localname (replace-match "$" nil nil localname 1)))
1544 :
1545 0 : localname)))
1546 :
1547 : ;; Share names of a host are cached. It is very unlikely that the
1548 : ;; shares do change during connection.
1549 : (defun tramp-smb-get-file-entries (directory)
1550 : "Read entries which match DIRECTORY.
1551 : Either the shares are listed, or the `dir' command is executed.
1552 : Result is a list of (LOCALNAME MODE SIZE MONTH DAY TIME YEAR)."
1553 0 : (with-parsed-tramp-file-name (file-name-as-directory directory) nil
1554 0 : (setq localname (or localname "/"))
1555 0 : (with-tramp-file-property v localname "file-entries"
1556 0 : (with-current-buffer (tramp-get-connection-buffer v)
1557 0 : (let* ((share (tramp-smb-get-share v))
1558 0 : (cache (tramp-get-connection-property v "share-cache" nil))
1559 : res entry)
1560 :
1561 0 : (if (and (not share) cache)
1562 : ;; Return cached shares.
1563 0 : (setq res cache)
1564 :
1565 : ;; Read entries.
1566 0 : (if share
1567 0 : (tramp-smb-send-command
1568 0 : v (format "dir \"%s*\"" (tramp-smb-get-localname v)))
1569 : ;; `tramp-smb-maybe-open-connection' lists also the share names.
1570 0 : (tramp-smb-maybe-open-connection v))
1571 :
1572 : ;; Loop the listing.
1573 0 : (goto-char (point-min))
1574 0 : (if (re-search-forward tramp-smb-errors nil t)
1575 0 : (tramp-error v 'file-error "%s `%s'" (match-string 0) directory)
1576 0 : (while (not (eobp))
1577 0 : (setq entry (tramp-smb-read-file-entry share))
1578 0 : (forward-line)
1579 0 : (when entry (push entry res))))
1580 :
1581 : ;; Cache share entries.
1582 0 : (unless share
1583 0 : (tramp-set-connection-property v "share-cache" res)))
1584 :
1585 : ;; Add directory itself.
1586 0 : (push '("" "drwxrwxrwx" 0 (0 0)) res)
1587 :
1588 : ;; Return entries.
1589 0 : (delq nil res))))))
1590 :
1591 : ;; Return either a share name (if SHARE is nil), or a file name.
1592 : ;;
1593 : ;; If shares are listed, the following format is expected:
1594 : ;;
1595 : ;; Disk| - leading spaces
1596 : ;; [^|]+| - share name, 14 char
1597 : ;; .* - comment
1598 : ;;
1599 : ;; Entries provided by smbclient DIR aren't fully regular.
1600 : ;; They should have the format
1601 : ;;
1602 : ;; \s-\{2,2} - leading spaces
1603 : ;; \S-\(.*\S-\)\s-* - file name, 30 chars, left bound
1604 : ;; \s-+[ADHRSV]* - permissions, 7 chars, right bound
1605 : ;; \s- - space delimiter
1606 : ;; \s-+[0-9]+ - size, 8 chars, right bound
1607 : ;; \s-\{2,2\} - space delimiter
1608 : ;; \w\{3,3\} - weekday
1609 : ;; \s- - space delimiter
1610 : ;; \w\{3,3\} - month
1611 : ;; \s- - space delimiter
1612 : ;; [ 12][0-9] - day
1613 : ;; \s- - space delimiter
1614 : ;; [0-9]\{2,2\}:[0-9]\{2,2\}:[0-9]\{2,2\} - time
1615 : ;; \s- - space delimiter
1616 : ;; [0-9]\{4,4\} - year
1617 : ;;
1618 : ;; samba/src/client.c (http://samba.org/doxygen/samba/client_8c-source.html)
1619 : ;; has function display_finfo:
1620 : ;;
1621 : ;; d_printf(" %-30s%7.7s %8.0f %s",
1622 : ;; finfo->name,
1623 : ;; attrib_string(finfo->mode),
1624 : ;; (double)finfo->size,
1625 : ;; asctime(LocalTime(&t)));
1626 : ;;
1627 : ;; in Samba 1.9, there's the following code:
1628 : ;;
1629 : ;; DEBUG(0,(" %-30s%7.7s%10d %s",
1630 : ;; CNV_LANG(finfo->name),
1631 : ;; attrib_string(finfo->mode),
1632 : ;; finfo->size,
1633 : ;; asctime(LocalTime(&t))));
1634 : ;;
1635 : ;; Problems:
1636 : ;; * Modern regexp constructs, like spy groups and counted repetitions, aren't
1637 : ;; available in older Emacsen.
1638 : ;; * The length of constructs (file name, size) might exceed the default.
1639 : ;; * File names might contain spaces.
1640 : ;; * Permissions might be empty.
1641 : ;;
1642 : ;; So we try to analyze backwards.
1643 : (defun tramp-smb-read-file-entry (share)
1644 : "Parse entry in SMB output buffer.
1645 : If SHARE is result, entries are of type dir. Otherwise, shares are listed.
1646 : Result is the list (LOCALNAME MODE SIZE MTIME)."
1647 : ;; We are called from `tramp-smb-get-file-entries', which sets the
1648 : ;; current buffer.
1649 0 : (let ((line (buffer-substring (point) (point-at-eol)))
1650 : localname mode size month day hour min sec year mtime)
1651 :
1652 0 : (if (not share)
1653 :
1654 : ;; Read share entries.
1655 0 : (when (string-match "^Disk|\\([^|]+\\)|" line)
1656 0 : (setq localname (match-string 1 line)
1657 : mode "dr-xr-xr-x"
1658 0 : size 0))
1659 :
1660 : ;; Real listing.
1661 0 : (cl-block nil
1662 :
1663 : ;; year.
1664 0 : (if (string-match "\\([0-9]+\\)$" line)
1665 0 : (setq year (string-to-number (match-string 1 line))
1666 0 : line (substring line 0 -5))
1667 0 : (cl-return))
1668 :
1669 : ;; time.
1670 0 : (if (string-match "\\([0-9]+\\):\\([0-9]+\\):\\([0-9]+\\)$" line)
1671 0 : (setq hour (string-to-number (match-string 1 line))
1672 0 : min (string-to-number (match-string 2 line))
1673 0 : sec (string-to-number (match-string 3 line))
1674 0 : line (substring line 0 -9))
1675 0 : (cl-return))
1676 :
1677 : ;; day.
1678 0 : (if (string-match "\\([0-9]+\\)$" line)
1679 0 : (setq day (string-to-number (match-string 1 line))
1680 0 : line (substring line 0 -3))
1681 0 : (cl-return))
1682 :
1683 : ;; month.
1684 0 : (if (string-match "\\(\\w+\\)$" line)
1685 0 : (setq month (match-string 1 line)
1686 0 : line (substring line 0 -4))
1687 0 : (cl-return))
1688 :
1689 : ;; weekday.
1690 0 : (if (string-match "\\(\\w+\\)$" line)
1691 0 : (setq line (substring line 0 -5))
1692 0 : (cl-return))
1693 :
1694 : ;; size.
1695 0 : (if (string-match "\\([0-9]+\\)$" line)
1696 0 : (let ((length (- (max 10 (1+ (length (match-string 1 line)))))))
1697 0 : (setq size (string-to-number (match-string 1 line)))
1698 0 : (when (string-match "\\([ADHRSV]+\\)" (substring line length))
1699 0 : (setq length (+ length (match-end 0))))
1700 0 : (setq line (substring line 0 length)))
1701 0 : (cl-return))
1702 :
1703 : ;; mode: ARCH, DIR, HIDDEN, RONLY, SYSTEM, VOLID.
1704 0 : (if (string-match "\\([ADHRSV]+\\)?$" line)
1705 0 : (setq
1706 0 : mode (or (match-string 1 line) "")
1707 0 : mode (save-match-data (format
1708 : "%s%s"
1709 0 : (if (string-match "D" mode) "d" "-")
1710 0 : (mapconcat
1711 : (lambda (_x) "") " "
1712 0 : (concat "r" (if (string-match "R" mode) "-" "w") "x"))))
1713 0 : line (substring line 0 -6))
1714 0 : (cl-return))
1715 :
1716 : ;; localname.
1717 0 : (if (string-match "^\\s-+\\(\\S-\\(.*\\S-\\)?\\)\\s-*$" line)
1718 0 : (setq localname (match-string 1 line))
1719 0 : (cl-return))))
1720 :
1721 0 : (when (and localname mode size)
1722 0 : (setq mtime
1723 0 : (if (and sec min hour day month year)
1724 0 : (encode-time
1725 0 : sec min hour day
1726 0 : (cdr (assoc (downcase month) parse-time-months))
1727 0 : year)
1728 0 : '(0 0)))
1729 0 : (list localname mode size mtime))))
1730 :
1731 : (defun tramp-smb-get-cifs-capabilities (vec)
1732 : "Check, whether the SMB server supports POSIX commands."
1733 : ;; When we are not logged in yet, we return nil.
1734 0 : (if (process-live-p (tramp-get-connection-process vec))
1735 0 : (with-tramp-connection-property
1736 0 : (tramp-get-connection-process vec) "cifs-capabilities"
1737 0 : (save-match-data
1738 0 : (when (tramp-smb-send-command vec "posix")
1739 0 : (with-current-buffer (tramp-get-connection-buffer vec)
1740 0 : (goto-char (point-min))
1741 0 : (when
1742 0 : (re-search-forward "Server supports CIFS capabilities" nil t)
1743 0 : (member
1744 : "pathnames"
1745 0 : (split-string
1746 0 : (buffer-substring (point) (point-at-eol)) nil 'omit)))))))))
1747 :
1748 : (defun tramp-smb-get-stat-capability (vec)
1749 : "Check, whether the SMB server supports the STAT command."
1750 : ;; When we are not logged in yet, we return nil.
1751 0 : (if (and (tramp-smb-get-share vec)
1752 0 : (process-live-p (tramp-get-connection-process vec)))
1753 0 : (with-tramp-connection-property
1754 0 : (tramp-get-connection-process vec) "stat-capability"
1755 0 : (tramp-smb-send-command vec "stat \"/\""))))
1756 :
1757 :
1758 : ;; Connection functions.
1759 :
1760 : (defun tramp-smb-send-command (vec command)
1761 : "Send the COMMAND to connection VEC.
1762 : Returns nil if there has been an error message from smbclient."
1763 0 : (tramp-smb-maybe-open-connection vec)
1764 0 : (tramp-message vec 6 "%s" command)
1765 0 : (tramp-send-string vec command)
1766 0 : (tramp-smb-wait-for-output vec))
1767 :
1768 : (defun tramp-smb-maybe-open-connection (vec &optional argument)
1769 : "Maybe open a connection to HOST, log in as USER, using `tramp-smb-program'.
1770 : Does not do anything if a connection is already open, but re-opens the
1771 : connection if a previous connection has died for some reason.
1772 : If ARGUMENT is non-nil, use it as argument for
1773 : `tramp-smb-winexe-program', and suppress any checks."
1774 0 : (let* ((share (tramp-smb-get-share vec))
1775 0 : (buf (tramp-get-connection-buffer vec))
1776 0 : (p (get-buffer-process buf)))
1777 :
1778 : ;; Check whether we still have the same smbclient version.
1779 : ;; Otherwise, we must delete the connection cache, because
1780 : ;; capabilities migh have changed.
1781 0 : (unless (or argument (processp p))
1782 0 : (let ((default-directory (tramp-compat-temporary-file-directory))
1783 0 : (command (concat tramp-smb-program " -V")))
1784 :
1785 0 : (unless tramp-smb-version
1786 0 : (unless (executable-find tramp-smb-program)
1787 0 : (tramp-error
1788 0 : vec 'file-error
1789 0 : "Cannot find command %s in %s" tramp-smb-program exec-path))
1790 0 : (setq tramp-smb-version (shell-command-to-string command))
1791 0 : (tramp-message vec 6 command)
1792 0 : (tramp-message vec 6 "\n%s" tramp-smb-version)
1793 0 : (if (string-match "[ \t\n\r]+\\'" tramp-smb-version)
1794 0 : (setq tramp-smb-version
1795 0 : (replace-match "" nil nil tramp-smb-version))))
1796 :
1797 0 : (unless (string-equal
1798 0 : tramp-smb-version
1799 0 : (tramp-get-connection-property
1800 0 : vec "smbclient-version" tramp-smb-version))
1801 0 : (tramp-flush-directory-property vec "")
1802 0 : (tramp-flush-connection-property vec))
1803 :
1804 0 : (tramp-set-connection-property
1805 0 : vec "smbclient-version" tramp-smb-version)))
1806 :
1807 : ;; If too much time has passed since last command was sent, look
1808 : ;; whether there has been an error message; maybe due to
1809 : ;; connection timeout.
1810 0 : (with-current-buffer buf
1811 0 : (goto-char (point-min))
1812 0 : (when (and (> (tramp-time-diff
1813 0 : (current-time)
1814 0 : (tramp-get-connection-property
1815 0 : p "last-cmd-time" '(0 0 0)))
1816 0 : 60)
1817 0 : (process-live-p p)
1818 0 : (re-search-forward tramp-smb-errors nil t))
1819 0 : (delete-process p)
1820 0 : (setq p nil)))
1821 :
1822 : ;; Check whether it is still the same share.
1823 0 : (unless (and (process-live-p p)
1824 0 : (or argument
1825 0 : (string-equal
1826 0 : share
1827 0 : (tramp-get-connection-property p "smb-share" ""))))
1828 :
1829 0 : (save-match-data
1830 : ;; There might be unread output from checking for share names.
1831 0 : (when buf (with-current-buffer buf (erase-buffer)))
1832 0 : (when (and p (processp p)) (delete-process p))
1833 :
1834 0 : (let* ((user (tramp-file-name-user vec))
1835 0 : (host (tramp-file-name-host vec))
1836 0 : (domain (tramp-file-name-domain vec))
1837 0 : (port (tramp-file-name-port vec))
1838 : args)
1839 :
1840 0 : (cond
1841 0 : (argument
1842 0 : (setq args (list (concat "//" host))))
1843 0 : (share
1844 0 : (setq args (list (concat "//" host "/" share))))
1845 : (t
1846 0 : (setq args (list "-g" "-L" host ))))
1847 :
1848 0 : (if (not (zerop (length user)))
1849 0 : (setq args (append args (list "-U" user)))
1850 0 : (setq args (append args (list "-N"))))
1851 :
1852 0 : (when domain (setq args (append args (list "-W" domain))))
1853 0 : (when port (setq args (append args (list "-p" port))))
1854 0 : (when tramp-smb-conf
1855 0 : (setq args (append args (list "-s" tramp-smb-conf))))
1856 0 : (when argument
1857 0 : (setq args (append args (list argument))))
1858 :
1859 : ;; OK, let's go.
1860 0 : (with-tramp-progress-reporter
1861 0 : vec 3
1862 0 : (format "Opening connection for //%s%s/%s"
1863 0 : (if (not (zerop (length user))) (concat user "@") "")
1864 0 : host (or share ""))
1865 :
1866 0 : (let* ((coding-system-for-read nil)
1867 0 : (process-connection-type tramp-process-connection-type)
1868 0 : (p (let ((default-directory
1869 0 : (tramp-compat-temporary-file-directory)))
1870 0 : (apply #'start-process
1871 0 : (tramp-get-connection-name vec)
1872 0 : (tramp-get-connection-buffer vec)
1873 0 : (if argument
1874 0 : tramp-smb-winexe-program tramp-smb-program)
1875 0 : args))))
1876 :
1877 0 : (tramp-message
1878 0 : vec 6 "%s" (mapconcat 'identity (process-command p) " "))
1879 0 : (tramp-set-connection-property p "vector" vec)
1880 0 : (process-put p 'adjust-window-size-function 'ignore)
1881 0 : (set-process-query-on-exit-flag p nil)
1882 :
1883 : ;; Set variables for computing the prompt for reading password.
1884 0 : (setq tramp-current-method tramp-smb-method
1885 0 : tramp-current-user user
1886 0 : tramp-current-domain domain
1887 0 : tramp-current-host host
1888 0 : tramp-current-port port)
1889 :
1890 0 : (condition-case err
1891 0 : (let (tramp-message-show-message)
1892 : ;; Play login scenario.
1893 0 : (tramp-process-actions
1894 0 : p vec nil
1895 0 : (if (or argument share)
1896 0 : tramp-smb-actions-with-share
1897 0 : tramp-smb-actions-without-share))
1898 :
1899 : ;; Check server version.
1900 0 : (unless argument
1901 0 : (with-current-buffer (tramp-get-connection-buffer vec)
1902 0 : (goto-char (point-min))
1903 0 : (search-forward-regexp tramp-smb-server-version nil t)
1904 0 : (let ((smbserver-version (match-string 0)))
1905 0 : (unless
1906 0 : (string-equal
1907 0 : smbserver-version
1908 0 : (tramp-get-connection-property
1909 0 : vec "smbserver-version" smbserver-version))
1910 0 : (tramp-flush-directory-property vec "")
1911 0 : (tramp-flush-connection-property vec))
1912 0 : (tramp-set-connection-property
1913 0 : vec "smbserver-version" smbserver-version))))
1914 :
1915 : ;; Set chunksize to 1. smbclient reads its input
1916 : ;; character by character; if we send the string
1917 : ;; at once, it is read painfully slow.
1918 0 : (tramp-set-connection-property p "smb-share" share)
1919 0 : (tramp-set-connection-property p "chunksize" 1)
1920 :
1921 : ;; Set connection-local variables.
1922 0 : (tramp-set-connection-local-variables vec)
1923 :
1924 : ;; Mark it as connected.
1925 0 : (tramp-set-connection-property p "connected" t))
1926 :
1927 : ;; Check for the error reason. If it was due to wrong
1928 : ;; password, reestablish the connection. We cannot
1929 : ;; handle this in `tramp-process-actions', because
1930 : ;; smbclient does not ask for the password, again.
1931 : (error
1932 0 : (with-current-buffer (tramp-get-connection-buffer vec)
1933 0 : (goto-char (point-min))
1934 0 : (if (and (bound-and-true-p auth-sources)
1935 0 : (search-forward-regexp
1936 0 : tramp-smb-wrong-passwd-regexp nil t))
1937 : ;; Disable `auth-source' and `password-cache'.
1938 0 : (let (auth-sources)
1939 0 : (tramp-message
1940 0 : vec 3 "Retry connection with new password")
1941 0 : (tramp-cleanup-connection vec t)
1942 0 : (tramp-smb-maybe-open-connection vec argument))
1943 : ;; Propagate the error.
1944 0 : (signal (car err) (cdr err)))))))))))))
1945 :
1946 : ;; We don't use timeouts. If needed, the caller shall wrap around.
1947 : (defun tramp-smb-wait-for-output (vec)
1948 : "Wait for output from smbclient command.
1949 : Returns nil if an error message has appeared."
1950 0 : (with-current-buffer (tramp-get-connection-buffer vec)
1951 0 : (let ((p (get-buffer-process (current-buffer)))
1952 0 : (found (progn (goto-char (point-min))
1953 0 : (re-search-forward tramp-smb-prompt nil t)))
1954 0 : (err (progn (goto-char (point-min))
1955 0 : (re-search-forward tramp-smb-errors nil t)))
1956 : buffer-read-only)
1957 :
1958 : ;; Algorithm: get waiting output. See if last line contains
1959 : ;; `tramp-smb-prompt' sentinel or `tramp-smb-errors' strings.
1960 : ;; If not, wait a bit and again get waiting output.
1961 0 : (while (and (not found) (not err) (process-live-p p))
1962 :
1963 : ;; Accept pending output.
1964 0 : (tramp-accept-process-output p 0.1)
1965 :
1966 : ;; Search for prompt.
1967 0 : (goto-char (point-min))
1968 0 : (setq found (re-search-forward tramp-smb-prompt nil t))
1969 :
1970 : ;; Search for errors.
1971 0 : (goto-char (point-min))
1972 0 : (setq err (re-search-forward tramp-smb-errors nil t)))
1973 :
1974 : ;; When the process is still alive, read pending output.
1975 0 : (while (and (not found) (process-live-p p))
1976 :
1977 : ;; Accept pending output.
1978 0 : (tramp-accept-process-output p 0.1)
1979 :
1980 : ;; Search for prompt.
1981 0 : (goto-char (point-min))
1982 0 : (setq found (re-search-forward tramp-smb-prompt nil t)))
1983 :
1984 0 : (tramp-message vec 6 "\n%s" (buffer-string))
1985 :
1986 : ;; Remove prompt.
1987 0 : (when found
1988 0 : (goto-char (point-max))
1989 0 : (re-search-backward tramp-smb-prompt nil t)
1990 0 : (delete-region (point) (point-max)))
1991 :
1992 : ;; Return value is whether no error message has appeared.
1993 0 : (not err))))
1994 :
1995 : (defun tramp-smb-kill-winexe-function ()
1996 : "Send SIGKILL to the winexe process."
1997 0 : (ignore-errors
1998 0 : (let ((p (get-buffer-process (current-buffer))))
1999 0 : (when (process-live-p p)
2000 0 : (signal-process (process-id p) 'SIGINT)))))
2001 :
2002 : (defun tramp-smb-call-winexe (vec)
2003 : "Apply a remote command, if possible, using `tramp-smb-winexe-program'."
2004 :
2005 : ;; Check for program.
2006 0 : (unless (executable-find tramp-smb-winexe-program)
2007 0 : (tramp-error
2008 0 : vec 'file-error "Cannot find program: %s" tramp-smb-winexe-program))
2009 :
2010 : ;; winexe does not supports ports.
2011 0 : (when (tramp-file-name-port vec)
2012 0 : (tramp-error vec 'file-error "Port not supported for remote processes"))
2013 :
2014 0 : (tramp-smb-maybe-open-connection
2015 0 : vec
2016 0 : (format
2017 : "%s %s"
2018 0 : tramp-smb-winexe-shell-command tramp-smb-winexe-shell-command-switch))
2019 :
2020 0 : (set (make-local-variable 'kill-buffer-hook)
2021 0 : '(tramp-smb-kill-winexe-function))
2022 :
2023 : ;; Suppress "^M". Shouldn't we specify utf8?
2024 0 : (set-process-coding-system (tramp-get-connection-process vec) 'raw-text-dos)
2025 :
2026 : ;; Set width to 128. This avoids mixing prompt and long error messages.
2027 0 : (tramp-smb-send-command vec "$rawui = (Get-Host).UI.RawUI")
2028 0 : (tramp-smb-send-command vec "$bufsize = $rawui.BufferSize")
2029 0 : (tramp-smb-send-command vec "$winsize = $rawui.WindowSize")
2030 0 : (tramp-smb-send-command vec "$bufsize.Width = 128")
2031 0 : (tramp-smb-send-command vec "$winsize.Width = 128")
2032 0 : (tramp-smb-send-command vec "$rawui.BufferSize = $bufsize")
2033 0 : (tramp-smb-send-command vec "$rawui.WindowSize = $winsize"))
2034 :
2035 : (defun tramp-smb-shell-quote-argument (s)
2036 : "Similar to `shell-quote-argument', but uses windows cmd syntax."
2037 0 : (let ((system-type 'ms-dos))
2038 0 : (tramp-unquote-shell-quote-argument s)))
2039 :
2040 : (add-hook 'tramp-unload-hook
2041 : (lambda ()
2042 : (unload-feature 'tramp-smb 'force)))
2043 :
2044 : (provide 'tramp-smb)
2045 :
2046 : ;;; TODO:
2047 :
2048 : ;; * Return more comprehensive file permission string.
2049 : ;;
2050 : ;; * Try to remove the inclusion of dummy "" directory. Seems to be at
2051 : ;; several places, especially in `tramp-smb-handle-insert-directory'.
2052 : ;;
2053 : ;; * Ignore case in file names.
2054 :
2055 : ;;; tramp-smb.el ends here
|