comparison lisp/net/tramp-smb.el @ 105551:2025d7d04cb9

* net/tramp-smb.el (tramp-smb-errors): Add error messages. (tramp-smb-file-name-handler-alist): Add handlers for `add-name-to-file', `make-symbolic-link'. (tramp-smb-handle-add-name-to-file) (tramp-do-file-attributes-with-stat) (tramp-smb-handle-make-symbolic-link) (tramp-smb-get-cifs-capabilities): New defuns. (tramp-smb-handle-copy-directory, tramp-smb-handle-copy-file) (tramp-smb-handle-delete-directory, tramp-smb-handle-delete-file) (tramp-smb-handle-file-local-copy) (tramp-smb-handle-make-directory-internal) (tramp-smb-handle-rename-file, tramp-smb-handle-write-region): The file name syntax depends on cifs capabilities. (tramp-smb-handle-file-attributes); Call `tramp-do-file-attributes-with-stat' if possible. (tramp-smb-handle-insert-directory): Use posix attributes if possible. (tramp-smb-handle-set-file-modes): It is applicable for posix only.
author Michael Albinus <michael.albinus@gmx.de>
date Sun, 11 Oct 2009 14:01:43 +0000
parents e781cac84553
children 59dc4314dfa4
comparison
equal deleted inserted replaced
105550:42674c5ffbe7 105551:2025d7d04cb9
93 "NT_STATUS_DIRECTORY_NOT_EMPTY" 93 "NT_STATUS_DIRECTORY_NOT_EMPTY"
94 "NT_STATUS_DUPLICATE_NAME" 94 "NT_STATUS_DUPLICATE_NAME"
95 "NT_STATUS_FILE_IS_A_DIRECTORY" 95 "NT_STATUS_FILE_IS_A_DIRECTORY"
96 "NT_STATUS_LOGON_FAILURE" 96 "NT_STATUS_LOGON_FAILURE"
97 "NT_STATUS_NETWORK_ACCESS_DENIED" 97 "NT_STATUS_NETWORK_ACCESS_DENIED"
98 "NT_STATUS_NOT_IMPLEMENTED"
98 "NT_STATUS_NO_SUCH_FILE" 99 "NT_STATUS_NO_SUCH_FILE"
99 "NT_STATUS_OBJECT_NAME_COLLISION" 100 "NT_STATUS_OBJECT_NAME_COLLISION"
100 "NT_STATUS_OBJECT_NAME_INVALID" 101 "NT_STATUS_OBJECT_NAME_INVALID"
101 "NT_STATUS_OBJECT_NAME_NOT_FOUND" 102 "NT_STATUS_OBJECT_NAME_NOT_FOUND"
102 "NT_STATUS_SHARING_VIOLATION" 103 "NT_STATUS_SHARING_VIOLATION"
129 130
130 ;; New handlers should be added here. 131 ;; New handlers should be added here.
131 (defconst tramp-smb-file-name-handler-alist 132 (defconst tramp-smb-file-name-handler-alist
132 '( 133 '(
133 ;; `access-file' performed by default handler. 134 ;; `access-file' performed by default handler.
134 (add-name-to-file . tramp-smb-handle-copy-file) ;; we're on Windows, honey. 135 (add-name-to-file . tramp-smb-handle-add-name-to-file)
135 ;; `byte-compiler-base-file-name' performed by default handler. 136 ;; `byte-compiler-base-file-name' performed by default handler.
136 (copy-directory . tramp-smb-handle-copy-directory) 137 (copy-directory . tramp-smb-handle-copy-directory)
137 (copy-file . tramp-smb-handle-copy-file) 138 (copy-file . tramp-smb-handle-copy-file)
138 (delete-directory . tramp-smb-handle-delete-directory) 139 (delete-directory . tramp-smb-handle-delete-directory)
139 (delete-file . tramp-smb-handle-delete-file) 140 (delete-file . tramp-smb-handle-delete-file)
173 (insert-directory . tramp-smb-handle-insert-directory) 174 (insert-directory . tramp-smb-handle-insert-directory)
174 (insert-file-contents . tramp-handle-insert-file-contents) 175 (insert-file-contents . tramp-handle-insert-file-contents)
175 (load . tramp-handle-load) 176 (load . tramp-handle-load)
176 (make-directory . tramp-smb-handle-make-directory) 177 (make-directory . tramp-smb-handle-make-directory)
177 (make-directory-internal . tramp-smb-handle-make-directory-internal) 178 (make-directory-internal . tramp-smb-handle-make-directory-internal)
178 (make-symbolic-link . ignore) 179 (make-symbolic-link . tramp-smb-handle-make-symbolic-link)
179 (rename-file . tramp-smb-handle-rename-file) 180 (rename-file . tramp-smb-handle-rename-file)
180 (set-file-modes . tramp-smb-handle-set-file-modes) 181 (set-file-modes . tramp-smb-handle-set-file-modes)
181 (set-file-times . ignore) 182 (set-file-times . ignore)
182 (set-visited-file-modtime . ignore) 183 (set-visited-file-modtime . ignore)
183 (shell-command . ignore) 184 (shell-command . ignore)
207 (add-to-list 'tramp-foreign-file-name-handler-alist 208 (add-to-list 'tramp-foreign-file-name-handler-alist
208 (cons 'tramp-smb-file-name-p 'tramp-smb-file-name-handler)) 209 (cons 'tramp-smb-file-name-p 'tramp-smb-file-name-handler))
209 210
210 211
211 ;; File name primitives. 212 ;; File name primitives.
213
214 (defun tramp-smb-handle-add-name-to-file
215 (filename newname &optional ok-if-already-exists)
216 "Like `add-name-to-file' for Tramp files."
217 (unless (tramp-equal-remote filename newname)
218 (with-parsed-tramp-file-name
219 (if (tramp-tramp-file-p filename) filename newname) nil
220 (tramp-error
221 v 'file-error
222 "add-name-to-file: %s"
223 "only implemented for same method, same user, same host")))
224 (with-parsed-tramp-file-name filename v1
225 (with-parsed-tramp-file-name newname v2
226 (when (file-directory-p filename)
227 (tramp-error
228 v2 'file-error
229 "add-name-to-file: %s must not be a directory" filename))
230 (when (and (not ok-if-already-exists)
231 (file-exists-p newname)
232 (not (numberp ok-if-already-exists))
233 (y-or-n-p
234 (format
235 "File %s already exists; make it a new name anyway? "
236 newname)))
237 (tramp-error
238 v2 'file-error
239 "add-name-to-file: file %s already exists" newname))
240 ;; We must also flush the cache of the directory, because
241 ;; `file-attributes' reads the values from there.
242 (tramp-flush-file-property v2 (file-name-directory v2-localname))
243 (tramp-flush-file-property v2 v2-localname)
244 (let ((cifs (tramp-smb-get-cifs-capabilities v1)))
245 (unless
246 (tramp-smb-send-command
247 v1
248 (format
249 "%s \"%s\" \"%s\""
250 (if cifs "link" "hardlink")
251 (tramp-smb-get-localname v1-localname (not cifs))
252 (tramp-smb-get-localname v2-localname (not cifs))))
253 (tramp-error
254 v2 'file-error
255 "error with add-name-to-file, see buffer `%s' for details"
256 (buffer-name)))))))
212 257
213 (defun tramp-smb-handle-copy-directory 258 (defun tramp-smb-handle-copy-directory
214 (dirname newname &optional keep-date parents) 259 (dirname newname &optional keep-date parents)
215 "Like `copy-directory' for Tramp files." 260 "Like `copy-directory' for Tramp files."
216 (setq dirname (expand-file-name dirname) 261 (setq dirname (expand-file-name dirname)
232 (copy-directory tmpdir newname keep-date parents)) 277 (copy-directory tmpdir newname keep-date parents))
233 (delete-directory tmpdir 'recursive)))) 278 (delete-directory tmpdir 'recursive))))
234 ((or t1 t2) 279 ((or t1 t2)
235 ;; We can copy recursively. 280 ;; We can copy recursively.
236 (let ((prompt (tramp-smb-send-command v "prompt")) 281 (let ((prompt (tramp-smb-send-command v "prompt"))
237 (recurse (tramp-smb-send-command v "recurse"))) 282 (recurse (tramp-smb-send-command v "recurse"))
283 (cifs (tramp-smb-get-cifs-capabilities v)))
238 (unless (file-directory-p newname) 284 (unless (file-directory-p newname)
239 (make-directory newname parents)) 285 (make-directory newname parents))
240 (unwind-protect 286 (unwind-protect
241 (unless 287 (unless
242 (and 288 (and
243 prompt recurse 289 prompt recurse
244 (tramp-smb-send-command 290 (tramp-smb-send-command
245 v (format "cd \"%s\"" 291 v (format
246 (tramp-smb-get-localname localname t))) 292 "cd \"%s\""
293 (tramp-smb-get-localname localname (not cifs))))
247 (tramp-smb-send-command 294 (tramp-smb-send-command
248 v (format "lcd \"%s\"" (if t1 newname dirname))) 295 v (format "lcd \"%s\"" (if t1 newname dirname)))
249 (if t1 296 (if t1
250 (tramp-smb-send-command v "mget *") 297 (tramp-smb-send-command v "mget *")
251 (tramp-smb-send-command v "mput *"))) 298 (tramp-smb-send-command v "mput *")))
254 (goto-char (point-min)) 301 (goto-char (point-min))
255 (search-forward-regexp tramp-smb-errors nil t) 302 (search-forward-regexp tramp-smb-errors nil t)
256 (tramp-error 303 (tramp-error
257 v 'file-error 304 v 'file-error
258 "%s `%s'" (match-string 0) (if t1 dirname newname)))) 305 "%s `%s'" (match-string 0) (if t1 dirname newname))))
259 ;; Always go home. 306 ;; Go home.
260 (tramp-smb-send-command v (format "cd \\")) 307 (tramp-smb-send-command v (format "cd %s" (if cifs "/" "\\")))
261 ;; Toggle prompt and recurse OFF. 308 ;; Toggle prompt and recurse OFF.
262 (if prompt (tramp-smb-send-command v "prompt")) 309 (if prompt (tramp-smb-send-command v "prompt"))
263 (if recurse (tramp-smb-send-command v "recurse"))))) 310 (if recurse (tramp-smb-send-command v "recurse")))))
264 (t 311 (t
265 ;; We must do it file-wise. 312 ;; We must do it file-wise.
293 (when (and (not ok-if-already-exists) 340 (when (and (not ok-if-already-exists)
294 (file-exists-p newname)) 341 (file-exists-p newname))
295 (tramp-error v 'file-already-exists newname)) 342 (tramp-error v 'file-already-exists newname))
296 343
297 ;; We must also flush the cache of the directory, because 344 ;; We must also flush the cache of the directory, because
298 ;; file-attributes reads the values from there. 345 ;; `file-attributes' reads the values from there.
299 (tramp-flush-file-property v (file-name-directory localname)) 346 (tramp-flush-file-property v (file-name-directory localname))
300 (tramp-flush-file-property v localname) 347 (tramp-flush-file-property v localname)
301 (let ((share (tramp-smb-get-share localname)) 348 (let ((share (tramp-smb-get-share localname))
302 (file (tramp-smb-get-localname localname t))) 349 (file (tramp-smb-get-localname
350 localname (not (tramp-smb-get-cifs-capabilities v)))))
303 (unless share 351 (unless share
304 (tramp-error 352 (tramp-error
305 v 'file-error "Target `%s' must contain a share name" newname)) 353 v 'file-error "Target `%s' must contain a share name" newname))
306 (tramp-message v 0 "Copying file %s to file %s..." filename newname) 354 (tramp-message v 0 "Copying file %s to file %s..." filename newname)
307 (if (tramp-smb-send-command 355 (if (tramp-smb-send-command
326 ;; We do not want to delete "." and "..". 374 ;; We do not want to delete "." and "..".
327 (directory-files 375 (directory-files
328 directory 'full "^\\([^.]\\|\\.\\([^.]\\|\\..\\)\\).*"))) 376 directory 'full "^\\([^.]\\|\\.\\([^.]\\|\\..\\)\\).*")))
329 (with-parsed-tramp-file-name directory nil 377 (with-parsed-tramp-file-name directory nil
330 ;; We must also flush the cache of the directory, because 378 ;; We must also flush the cache of the directory, because
331 ;; file-attributes reads the values from there. 379 ;; `file-attributes' reads the values from there.
332 (tramp-flush-file-property v (file-name-directory localname)) 380 (tramp-flush-file-property v (file-name-directory localname))
333 (tramp-flush-directory-property v localname) 381 (tramp-flush-directory-property v localname)
334 (let ((dir (tramp-smb-get-localname (file-name-directory localname) t)) 382 (let ((cifs (tramp-smb-get-cifs-capabilities v)))
335 (file (file-name-nondirectory localname))) 383 (unless (tramp-smb-send-command
336 (unwind-protect 384 v (format
337 (unless (and 385 "%s \"%s\""
338 (tramp-smb-send-command v (format "cd \"%s\"" dir)) 386 (if cifs "posix_rmdir" "rmdir")
339 (tramp-smb-send-command v (format "rmdir \"%s\"" file))) 387 (tramp-smb-get-localname localname (not cifs))))
340 ;; Error. 388 ;; Error.
341 (with-current-buffer (tramp-get-connection-buffer v) 389 (with-current-buffer (tramp-get-connection-buffer v)
342 (goto-char (point-min)) 390 (goto-char (point-min))
343 (search-forward-regexp tramp-smb-errors nil t) 391 (search-forward-regexp tramp-smb-errors nil t)
344 (tramp-error 392 (tramp-error
345 v 'file-error "%s `%s'" (match-string 0) directory))) 393 v 'file-error "%s `%s'" (match-string 0) directory)))))))
346 ;; Always go home.
347 (tramp-smb-send-command v (format "cd \\")))))))
348 394
349 (defun tramp-smb-handle-delete-file (filename) 395 (defun tramp-smb-handle-delete-file (filename)
350 "Like `delete-file' for Tramp files." 396 "Like `delete-file' for Tramp files."
351 (setq filename (expand-file-name filename)) 397 (setq filename (expand-file-name filename))
352 (when (file-exists-p filename) 398 (when (file-exists-p filename)
353 (with-parsed-tramp-file-name filename nil 399 (with-parsed-tramp-file-name filename nil
354 ;; We must also flush the cache of the directory, because 400 ;; We must also flush the cache of the directory, because
355 ;; file-attributes reads the values from there. 401 ;; `file-attributes' reads the values from there.
356 (tramp-flush-file-property v (file-name-directory localname)) 402 (tramp-flush-file-property v (file-name-directory localname))
357 (tramp-flush-file-property v localname) 403 (tramp-flush-file-property v localname)
358 (let ((dir (tramp-smb-get-localname (file-name-directory localname) t)) 404 (let ((cifs (tramp-smb-get-cifs-capabilities v)))
359 (file (file-name-nondirectory localname))) 405 (unless (tramp-smb-send-command
360 (unwind-protect 406 v (format
361 (unless (and 407 "%s \"%s\""
362 (tramp-smb-send-command v (format "cd \"%s\"" dir)) 408 (if cifs "posix_unlink" "rm")
363 (tramp-smb-send-command v (format "rm \"%s\"" file))) 409 (tramp-smb-get-localname localname (not cifs))))
364 ;; Error. 410 ;; Error.
365 (with-current-buffer (tramp-get-connection-buffer v) 411 (with-current-buffer (tramp-get-connection-buffer v)
366 (goto-char (point-min)) 412 (goto-char (point-min))
367 (search-forward-regexp tramp-smb-errors nil t) 413 (search-forward-regexp tramp-smb-errors nil t)
368 (tramp-error 414 (tramp-error
369 v 'file-error "%s `%s'" (match-string 0) filename))) 415 v 'file-error "%s `%s'" (match-string 0) filename)))))))
370 ;; Always go home.
371 (tramp-smb-send-command v (format "cd \\")))))))
372 416
373 (defun tramp-smb-handle-directory-files 417 (defun tramp-smb-handle-directory-files
374 (directory &optional full match nosort) 418 (directory &optional full match nosort)
375 "Like `directory-files' for Tramp files." 419 "Like `directory-files' for Tramp files."
376 (let ((result (mapcar 'directory-file-name 420 (let ((result (mapcar 'directory-file-name
431 method user host 475 method user host
432 (tramp-run-real-handler 'expand-file-name (list localname)))))) 476 (tramp-run-real-handler 'expand-file-name (list localname))))))
433 477
434 (defun tramp-smb-handle-file-attributes (filename &optional id-format) 478 (defun tramp-smb-handle-file-attributes (filename &optional id-format)
435 "Like `file-attributes' for Tramp files." 479 "Like `file-attributes' for Tramp files."
436 ;; Reading just the filename entry via "dir localname" is not 480 (unless id-format (setq id-format 'integer))
437 ;; possible, because when filename is a directory, some smbclient
438 ;; versions return the content of the directory, and other versions
439 ;; don't. Therefore, the whole content of the upper directory is
440 ;; retrieved, and the entry of the filename is extracted from.
441 (with-parsed-tramp-file-name filename nil 481 (with-parsed-tramp-file-name filename nil
442 (with-file-property v localname (format "file-attributes-%s" id-format) 482 (with-file-property v localname (format "file-attributes-%s" id-format)
443 (let* ((entries (tramp-smb-get-file-entries 483 (if (and (tramp-smb-get-share localname)
444 (file-name-directory filename))) 484 (tramp-smb-get-cifs-capabilities v))
445 (entry (assoc (file-name-nondirectory filename) entries)) 485 (tramp-do-file-attributes-with-stat v localname id-format)
446 (uid (if (and id-format (equal id-format 'string)) "nobody" -1)) 486 ;; Reading just the filename entry via "dir localname" is not
447 (gid (if (and id-format (equal id-format 'string)) "nogroup" -1)) 487 ;; possible, because when filename is a directory, some
448 (inode (tramp-get-inode v)) 488 ;; smbclient versions return the content of the directory, and
449 (device (tramp-get-device v))) 489 ;; other versions don't. Therefore, the whole content of the
450 490 ;; upper directory is retrieved, and the entry of the filename
451 ;; Check result. 491 ;; is extracted from.
452 (when entry 492 (let* ((entries (tramp-smb-get-file-entries
453 (list (and (string-match "d" (nth 1 entry)) 493 (file-name-directory filename)))
454 t) ;0 file type 494 (entry (assoc (file-name-nondirectory filename) entries))
455 -1 ;1 link count 495 (uid (if (equal id-format 'string) "nobody" -1))
456 uid ;2 uid 496 (gid (if (equal id-format 'string) "nogroup" -1))
457 gid ;3 gid 497 (inode (tramp-get-inode v))
458 '(0 0) ;4 atime 498 (device (tramp-get-device v)))
459 (nth 3 entry) ;5 mtime 499
460 '(0 0) ;6 ctime 500 ;; Check result.
461 (nth 2 entry) ;7 size 501 (when entry
462 (nth 1 entry) ;8 mode 502 (list (and (string-match "d" (nth 1 entry))
463 nil ;9 gid weird 503 t) ;0 file type
464 inode ;10 inode number 504 -1 ;1 link count
465 device)))))) ;11 file system number 505 uid ;2 uid
506 gid ;3 gid
507 '(0 0) ;4 atime
508 (nth 3 entry) ;5 mtime
509 '(0 0) ;6 ctime
510 (nth 2 entry) ;7 size
511 (nth 1 entry) ;8 mode
512 nil ;9 gid weird
513 inode ;10 inode number
514 device))))))) ;11 file system number
515
516 (defun tramp-do-file-attributes-with-stat
517 (vec localname &optional id-format)
518 "Implement `file-attributes' for Tramp files using stat command."
519 (tramp-message vec 5 "file attributes with stat: %s" localname)
520 (with-current-buffer (tramp-get-buffer vec)
521 (let* ((file (tramp-smb-get-localname localname nil))
522 id link uid gid atime mtime ctime mode inode)
523 (tramp-smb-send-command vec (format "stat \"%s\"" file))
524
525 ;; Loop the listing.
526 (goto-char (point-min))
527 (unless (re-search-forward tramp-smb-errors nil t)
528 (while (not (eobp))
529 (cond
530 ;;File: /dbus
531 ((looking-at
532 "Size:\\s-+\\([0-9]+\\)\\s-+Blocks:\\s-+[0-9]+\\s-+\\(\\w+\\)")
533 (setq size (string-to-number (match-string 1))
534 id (if (string-equal "directory" (match-string 2)) t
535 (if (string-equal "symbolic" (match-string 2)) ""))))
536 ((looking-at
537 "Inode:\\s-+\\([0-9]+\\)\\s-+Links:\\s-+\\([0-9]+\\)")
538 (setq inode (string-to-number (match-string 1))
539 link (string-to-number (match-string 2))))
540 ((looking-at
541 "Access:\\s-+([0-9]+/\\(\\S-+\\))\\s-+Uid:\\s-+\\([0-9]+\\)\\s-+Gid:\\s-+\\([0-9]+\\)")
542 (setq mode (match-string 1)
543 uid (if (equal id-format 'string) (match-string 2)
544 (string-to-number (match-string 2)))
545 gid (if (equal id-format 'string) (match-string 3)
546 (string-to-number (match-string 3)))))
547 ((looking-at
548 "Access:\\s-+\\([0-9]+\\)-\\([0-9]+\\)-\\([0-9]+\\)\\s-+\\([0-9]+\\):\\([0-9]+\\):\\([0-9]+\\)")
549 (setq atime
550 (encode-time
551 (string-to-number (match-string 6)) ;; sec
552 (string-to-number (match-string 5)) ;; min
553 (string-to-number (match-string 4)) ;; hour
554 (string-to-number (match-string 3)) ;; day
555 (string-to-number (match-string 2)) ;; month
556 (string-to-number (match-string 1))))) ;; year
557 ((looking-at
558 "Modify:\\s-+\\([0-9]+\\)-\\([0-9]+\\)-\\([0-9]+\\)\\s-+\\([0-9]+\\):\\([0-9]+\\):\\([0-9]+\\)")
559 (setq mtime
560 (encode-time
561 (string-to-number (match-string 6)) ;; sec
562 (string-to-number (match-string 5)) ;; min
563 (string-to-number (match-string 4)) ;; hour
564 (string-to-number (match-string 3)) ;; day
565 (string-to-number (match-string 2)) ;; month
566 (string-to-number (match-string 1))))) ;; year
567 ((looking-at
568 "Change:\\s-+\\([0-9]+\\)-\\([0-9]+\\)-\\([0-9]+\\)\\s-+\\([0-9]+\\):\\([0-9]+\\):\\([0-9]+\\)")
569 (setq ctime
570 (encode-time
571 (string-to-number (match-string 6)) ;; sec
572 (string-to-number (match-string 5)) ;; min
573 (string-to-number (match-string 4)) ;; hour
574 (string-to-number (match-string 3)) ;; day
575 (string-to-number (match-string 2)) ;; month
576 (string-to-number (match-string 1)))))) ;; year
577 (forward-line))
578 ;; Return the result.
579 (list id link uid gid atime mtime ctime size mode nil inode
580 (tramp-get-device vec))))))
466 581
467 (defun tramp-smb-handle-file-directory-p (filename) 582 (defun tramp-smb-handle-file-directory-p (filename)
468 "Like `file-directory-p' for Tramp files." 583 "Like `file-directory-p' for Tramp files."
469 (and (file-exists-p filename) 584 (and (file-exists-p filename)
470 (eq ?d (aref (nth 8 (file-attributes filename)) 0)))) 585 (eq ?d (aref (nth 8 (file-attributes filename)) 0))))
478 (with-parsed-tramp-file-name filename nil 593 (with-parsed-tramp-file-name filename nil
479 (unless (file-exists-p filename) 594 (unless (file-exists-p filename)
480 (tramp-error 595 (tramp-error
481 v 'file-error 596 v 'file-error
482 "Cannot make local copy of non-existing file `%s'" filename)) 597 "Cannot make local copy of non-existing file `%s'" filename))
483 (let ((file (tramp-smb-get-localname localname t)) 598 (let ((file (tramp-smb-get-localname
599 localname (not (tramp-smb-get-cifs-capabilities v))))
484 (tmpfile (tramp-compat-make-temp-file filename))) 600 (tmpfile (tramp-compat-make-temp-file filename)))
485 (tramp-message v 4 "Fetching %s to tmp file %s..." filename tmpfile) 601 (tramp-message v 4 "Fetching %s to tmp file %s..." filename tmpfile)
486 (if (tramp-smb-send-command v (format "get \"%s\" %s" file tmpfile)) 602 (if (tramp-smb-send-command v (format "get \"%s\" %s" file tmpfile))
487 (tramp-message 603 (tramp-message
488 v 4 "Fetching %s to tmp file %s...done" filename tmpfile) 604 v 4 "Fetching %s to tmp file %s...done" filename tmpfile)
534 (when full-directory-p 650 (when full-directory-p
535 ;; Called from `dired-add-entry'. 651 ;; Called from `dired-add-entry'.
536 (setq filename (file-name-as-directory filename))) 652 (setq filename (file-name-as-directory filename)))
537 (with-parsed-tramp-file-name filename nil 653 (with-parsed-tramp-file-name filename nil
538 (save-match-data 654 (save-match-data
539 (let ((base (file-name-nondirectory filename)) 655 (let ((cifs (tramp-smb-get-cifs-capabilities v))
656 (base (file-name-nondirectory filename))
540 ;; We should not destroy the cache entry. 657 ;; We should not destroy the cache entry.
541 (entries (copy-sequence 658 (entries (copy-sequence
542 (tramp-smb-get-file-entries 659 (tramp-smb-get-file-entries
543 (file-name-directory filename))))) 660 (file-name-directory filename)))))
544 661
590 707
591 ;; Print entries. 708 ;; Print entries.
592 (mapcar 709 (mapcar
593 (lambda (x) 710 (lambda (x)
594 (when (not (zerop (length (nth 0 x)))) 711 (when (not (zerop (length (nth 0 x))))
595 (insert 712 (let ((attr
596 (format 713 (when cifs
597 "%10s %3d %-8s %-8s %8s %s %s\n" 714 (file-attributes (expand-file-name (nth 0 x)) 'string))))
598 (nth 1 x) ; mode 715 (insert
599 1 "nobody" "nogroup" 716 (format
600 (nth 2 x) ; size 717 "%10s %3d %-8s %-8s %8s %s %s\n"
601 (format-time-string 718 (or (nth 8 attr) (nth 1 x)) ; mode
602 (if (tramp-time-less-p 719 (or (nth 1 attr) 1) ; link
603 (tramp-time-subtract (current-time) (nth 3 x)) 720 (or (nth 2 attr) "nobody") ; uid
604 tramp-half-a-year) 721 (or (nth 3 attr) "nogroup") ; gid
605 "%b %e %R" 722 (nth 2 x) ; size
606 "%b %e %Y") 723 (format-time-string
607 (nth 3 x)) ; date 724 (if (tramp-time-less-p
608 (nth 0 x))) ; file name 725 (tramp-time-subtract (current-time) (nth 3 x))
609 (forward-line) 726 tramp-half-a-year)
610 (beginning-of-line))) 727 "%b %e %R"
611 entries))))) 728 "%b %e %Y")
729 (nth 3 x)) ; date
730 (nth 0 x))) ; file name
731 (forward-line)
732 (beginning-of-line))))
733 entries)))))
612 734
613 (defun tramp-smb-handle-make-directory (dir &optional parents) 735 (defun tramp-smb-handle-make-directory (dir &optional parents)
614 "Like `make-directory' for Tramp files." 736 "Like `make-directory' for Tramp files."
615 (setq dir (directory-file-name (expand-file-name dir))) 737 (setq dir (directory-file-name (expand-file-name dir)))
616 (unless (file-name-absolute-p dir) 738 (unless (file-name-absolute-p dir)
633 (setq directory (directory-file-name (expand-file-name directory))) 755 (setq directory (directory-file-name (expand-file-name directory)))
634 (unless (file-name-absolute-p directory) 756 (unless (file-name-absolute-p directory)
635 (setq directory (expand-file-name directory default-directory))) 757 (setq directory (expand-file-name directory default-directory)))
636 (with-parsed-tramp-file-name directory nil 758 (with-parsed-tramp-file-name directory nil
637 (save-match-data 759 (save-match-data
638 (let* ((file (tramp-smb-get-localname localname t))) 760 (let* ((cifs (tramp-smb-get-cifs-capabilities v))
761 (file (tramp-smb-get-localname localname (not cifs))))
639 (when (file-directory-p (file-name-directory directory)) 762 (when (file-directory-p (file-name-directory directory))
640 (tramp-smb-send-command v (format "mkdir \"%s\"" file)) 763 (tramp-smb-send-command
764 v
765 (if cifs
766 (format
767 "posix_mkdir \"%s\" %s"
768 file (tramp-decimal-to-octal (default-file-modes)))
769 (format "mkdir \"%s\"" file)))
641 ;; We must also flush the cache of the directory, because 770 ;; We must also flush the cache of the directory, because
642 ;; file-attributes reads the values from there. 771 ;; `file-attributes' reads the values from there.
643 (tramp-flush-file-property v localname) 772 (tramp-flush-file-property v (file-name-directory localname))
644 (tramp-flush-file-property v (file-name-directory localname))) 773 (tramp-flush-file-property v localname))
645 (unless (file-directory-p directory) 774 (unless (file-directory-p directory)
646 (tramp-error 775 (tramp-error
647 v 'file-error "Couldn't make directory %s" directory)))))) 776 v 'file-error "Couldn't make directory %s" directory))))))
777
778 (defun tramp-smb-handle-make-symbolic-link
779 (filename linkname &optional ok-if-already-exists)
780 "Like `make-symbolic-link' for Tramp files.
781 If LINKNAME is a non-Tramp file, it is used verbatim as the target of
782 the symlink. If LINKNAME is a Tramp file, only the localname component is
783 used as the target of the symlink.
784
785 If LINKNAME is a Tramp file and the localname component is relative, then
786 it is expanded first, before the localname component is taken. Note that
787 this can give surprising results if the user/host for the source and
788 target of the symlink differ."
789 (unless (tramp-equal-remote filename linkname)
790 (with-parsed-tramp-file-name
791 (if (tramp-tramp-file-p filename) filename linkname) nil
792 (tramp-error
793 v 'file-error
794 "make-symbolic-link: %s"
795 "only implemented for same method, same user, same host")))
796 (with-parsed-tramp-file-name filename v1
797 (with-parsed-tramp-file-name linkname v2
798 (when (file-directory-p filename)
799 (tramp-error
800 v2 'file-error
801 "make-symbolic-link: %s must not be a directory" filename))
802 (when (and (not ok-if-already-exists)
803 (file-exists-p linkname)
804 (not (numberp ok-if-already-exists))
805 (y-or-n-p
806 (format
807 "File %s already exists; make it a new name anyway? "
808 linkname)))
809 (tramp-error
810 v2 'file-error
811 "make-symbolic-link: file %s already exists" linkname))
812 (unless (tramp-smb-get-cifs-capabilities v1)
813 (tramp-error v2 'file-error "make-symbolic-link not supported"))
814 ;; We must also flush the cache of the directory, because
815 ;; `file-attributes' reads the values from there.
816 (tramp-flush-file-property v2 (file-name-directory v2-localname))
817 (tramp-flush-file-property v2 v2-localname)
818 (unless
819 (tramp-smb-send-command
820 v1
821 (format
822 "symlink \"%s\" \"%s\""
823 (tramp-smb-get-localname v1-localname nil)
824 (tramp-smb-get-localname v2-localname nil)))
825 (tramp-error
826 v2 'file-error
827 "error with make-symbolic-link, see buffer `%s' for details"
828 (buffer-name))))))
648 829
649 (defun tramp-smb-handle-rename-file 830 (defun tramp-smb-handle-rename-file
650 (filename newname &optional ok-if-already-exists) 831 (filename newname &optional ok-if-already-exists)
651 "Like `rename-file' for Tramp files." 832 "Like `rename-file' for Tramp files."
652 (setq filename (expand-file-name filename) 833 (setq filename (expand-file-name filename)
670 (with-parsed-tramp-file-name newname nil 851 (with-parsed-tramp-file-name newname nil
671 (when (and (not ok-if-already-exists) 852 (when (and (not ok-if-already-exists)
672 (file-exists-p newname)) 853 (file-exists-p newname))
673 (tramp-error v 'file-already-exists newname)) 854 (tramp-error v 'file-already-exists newname))
674 ;; We must also flush the cache of the directory, because 855 ;; We must also flush the cache of the directory, because
675 ;; file-attributes reads the values from there. 856 ;; `file-attributes' reads the values from there.
676 (tramp-flush-file-property v (file-name-directory localname)) 857 (tramp-flush-file-property v (file-name-directory localname))
677 (tramp-flush-file-property v localname) 858 (tramp-flush-file-property v localname)
678 (let ((file (tramp-smb-get-localname localname t))) 859 (let ((file (tramp-smb-get-localname
860 localname (not (tramp-smb-get-cifs-capabilities v)))))
679 (tramp-message v 0 "Copying file %s to file %s..." filename newname) 861 (tramp-message v 0 "Copying file %s to file %s..." filename newname)
680 (if (tramp-smb-send-command v (format "put %s \"%s\"" filename file)) 862 (if (tramp-smb-send-command v (format "put %s \"%s\"" filename file))
681 (tramp-message 863 (tramp-message
682 v 0 "Copying file %s to file %s...done" filename newname) 864 v 0 "Copying file %s to file %s...done" filename newname)
683 (tramp-error v 'file-error "Cannot rename `%s'" filename)))))) 865 (tramp-error v 'file-error "Cannot rename `%s'" filename))))))
685 (delete-file filename)) 867 (delete-file filename))
686 868
687 (defun tramp-smb-handle-set-file-modes (filename mode) 869 (defun tramp-smb-handle-set-file-modes (filename mode)
688 "Like `set-file-modes' for Tramp files." 870 "Like `set-file-modes' for Tramp files."
689 (with-parsed-tramp-file-name filename nil 871 (with-parsed-tramp-file-name filename nil
690 (tramp-flush-file-property v localname) 872 (when (tramp-smb-get-cifs-capabilities v)
691 (unless (tramp-smb-send-command 873 (tramp-flush-file-property v localname)
692 v (format "chmod \"%s\" %s" 874 (unless (tramp-smb-send-command
693 (tramp-smb-get-localname localname t) 875 v (format "chmod \"%s\" %s"
694 (tramp-decimal-to-octal mode))) 876 (tramp-smb-get-localname localname nil)
695 (tramp-error 877 (tramp-decimal-to-octal mode)))
696 v 'file-error "Error while changing file's mode %s" filename)))) 878 (tramp-error
879 v 'file-error "Error while changing file's mode %s" filename)))))
697 880
698 (defun tramp-smb-handle-substitute-in-file-name (filename) 881 (defun tramp-smb-handle-substitute-in-file-name (filename)
699 "Like `handle-substitute-in-file-name' for Tramp files. 882 "Like `handle-substitute-in-file-name' for Tramp files.
700 \"//\" substitutes only in the local filename part. Catches 883 \"//\" substitutes only in the local filename part. Catches
701 errors for shares like \"C$/\", which are common in Microsoft Windows." 884 errors for shares like \"C$/\", which are common in Microsoft Windows."
725 (tramp-error v 'file-error "File not overwritten"))) 908 (tramp-error v 'file-error "File not overwritten")))
726 ;; We must also flush the cache of the directory, because 909 ;; We must also flush the cache of the directory, because
727 ;; `file-attributes' reads the values from there. 910 ;; `file-attributes' reads the values from there.
728 (tramp-flush-file-property v (file-name-directory localname)) 911 (tramp-flush-file-property v (file-name-directory localname))
729 (tramp-flush-file-property v localname) 912 (tramp-flush-file-property v localname)
730 (let ((file (tramp-smb-get-localname localname t)) 913 (let ((file (tramp-smb-get-localname
914 localname (not (tramp-smb-get-cifs-capabilities v))))
731 (curbuf (current-buffer)) 915 (curbuf (current-buffer))
732 (tmpfile (tramp-compat-make-temp-file filename))) 916 (tmpfile (tramp-compat-make-temp-file filename)))
733 ;; We say `no-message' here because we don't want the visited file 917 ;; We say `no-message' here because we don't want the visited file
734 ;; modtime data to be clobbered from the temp file. We call 918 ;; modtime data to be clobbered from the temp file. We call
735 ;; `set-visited-file-modtime' ourselves later on. 919 ;; `set-visited-file-modtime' ourselves later on.
976 (cdr (assoc (downcase month) tramp-parse-time-months)) 1160 (cdr (assoc (downcase month) tramp-parse-time-months))
977 year) 1161 year)
978 '(0 0))) 1162 '(0 0)))
979 (list localname mode size mtime)))) 1163 (list localname mode size mtime))))
980 1164
1165 (defun tramp-smb-get-cifs-capabilities (vec)
1166 "Check, whether the SMB server supports POSIX commands."
1167 (with-connection-property
1168 (tramp-get-connection-process vec) "cifs-capabilities"
1169 (when (tramp-smb-send-command vec "posix")
1170 (with-current-buffer (tramp-get-buffer vec)
1171 (goto-char (point-min))
1172 (when (re-search-forward "Server supports CIFS capabilities" nil t)
1173 (member
1174 "pathnames"
1175 (split-string
1176 (buffer-substring
1177 (point) (tramp-compat-line-end-position)) nil t)))))))
1178
981 1179
982 ;; Connection functions. 1180 ;; Connection functions.
983 1181
984 (defun tramp-smb-send-command (vec command) 1182 (defun tramp-smb-send-command (vec command)
985 "Send the COMMAND to connection VEC. 1183 "Send the COMMAND to connection VEC.
1172 ;;; TODO: 1370 ;;; TODO:
1173 1371
1174 ;; * Error handling in case password is wrong. 1372 ;; * Error handling in case password is wrong.
1175 ;; * Read password from "~/.netrc". 1373 ;; * Read password from "~/.netrc".
1176 ;; * Return more comprehensive file permission string. 1374 ;; * Return more comprehensive file permission string.
1177 ;; * Handle links (FILENAME.LNK).
1178 ;; * Try to remove the inclusion of dummy "" directory. Seems to be at 1375 ;; * Try to remove the inclusion of dummy "" directory. Seems to be at
1179 ;; several places, especially in `tramp-smb-handle-insert-directory'. 1376 ;; several places, especially in `tramp-smb-handle-insert-directory'.
1180 ;; * (RMS) Use unwind-protect to clean up the state so as to make the state 1377 ;; * (RMS) Use unwind-protect to clean up the state so as to make the state
1181 ;; regular again. 1378 ;; regular again.
1182 ;; * Make it multi-hop capable. 1379 ;; * Make it multi-hop capable.