* nnmaildir.el (nnmaildir--num-file, nnmaildir--mkfile,
authorPaul Jarc <prj@po.cwru.edu>
Sun, 25 Jan 2004 05:26:46 +0000 (05:26 +0000)
committerPaul Jarc <prj@po.cwru.edu>
Sun, 25 Jan 2004 05:26:46 +0000 (05:26 +0000)
nnmaildir--emlink-p, nnmaildir--eexist-p, nnmaildir--new-number):
New macros and functions.
* nnmaildir.el (nnmaildir--group-maxnum, nnmaildir--update-nov):
Handle > NLINK_MAX messages.
* nnmaildir.el (nnmaildir-request-set-mark): Use
nnmaildir--emlink-p and nnmaildir--eexist-p.

lisp/ChangeLog
lisp/nnmaildir.el

index 8d9ef31..82fc310 100644 (file)
@@ -1,3 +1,13 @@
+2004-01-25  Paul Jarc  <prj@po.cwru.edu>
+
+       * nnmaildir.el (nnmaildir--num-file, nnmaildir--mkfile,
+       nnmaildir--emlink-p, nnmaildir--eexist-p, nnmaildir--new-number):
+       New macros and functions.
+       * nnmaildir.el (nnmaildir--group-maxnum, nnmaildir--update-nov):
+       Handle > NLINK_MAX messages.
+       * nnmaildir.el (nnmaildir-request-set-mark): Use
+       nnmaildir--emlink-p and nnmaildir--eexist-p.
+
 2004-01-25  Alex Schroeder  <alex@gnu.org>
 
        * spam-stat.el (spam-stat-process-directory-age): New option.
index c6d7765..bd498a5 100644 (file)
@@ -229,7 +229,6 @@ by nnmaildir-request-article.")
 (defmacro nnmaildir--nov-dir   (dir) `(nnmaildir--subdir ,dir "nov"))
 (defmacro nnmaildir--marks-dir (dir) `(nnmaildir--subdir ,dir "marks"))
 (defmacro nnmaildir--num-dir   (dir) `(nnmaildir--subdir ,dir "num"))
-(defmacro nnmaildir--num-file  (dir) `(concat ,dir ":"))
 
 (defmacro nnmaildir--unlink (file-arg)
   `(let ((file ,file-arg))
@@ -237,20 +236,36 @@ by nnmaildir-request-article.")
 (defun nnmaildir--mkdir (dir)
   (or (file-exists-p (file-name-as-directory dir))
       (make-directory-internal (directory-file-name dir))))
+(defun nnmaildir--mkfile (file)
+  (write-region "" nil file nil 'no-message))
 (defun nnmaildir--delete-dir-files (dir ls)
   (when (file-attributes dir)
     (mapcar 'delete-file (funcall ls dir 'full "\\`[^.]" 'nosort))
     (delete-directory dir)))
 
 (defun nnmaildir--group-maxnum (server group)
-  (if (zerop (nnmaildir--grp-count group)) 0
-    (let ((x (nnmaildir--srvgrp-dir (nnmaildir--srv-dir server)
-                                   (nnmaildir--grp-name group))))
-      (setq x (nnmaildir--nndir x)
-           x (nnmaildir--num-dir x)
-           x (nnmaildir--num-file x)
-           x (file-attributes x))
-      (if x (1- (nth 1 x)) 0))))
+  (catch 'return
+    (if (zerop (nnmaildir--grp-count group)) (throw 'return 0))
+    (let ((dir (nnmaildir--srvgrp-dir (nnmaildir--srv-dir server)
+                                   (nnmaildir--grp-name group)))
+         (number-opened 1)
+         attr ino-opened nlink number-linked)
+      (setq dir (nnmaildir--nndir dir)
+           dir (nnmaildir--num-dir dir))
+      (while t
+       (setq attr (file-attributes
+                   (concat dir (number-to-string number-opened))))
+       (or attr (throw 'return (1- number-opened)))
+       (setq ino-opened (nth 10 attr)
+             nlink (nth 1 attr)
+             number-linked (+ number-opened nlink))
+       (if (or (< nlink 1) (< number-linked nlink))
+           (signal 'error '("Arithmetic overflow")))
+       (setq attr (file-attributes
+                   (concat dir (number-to-string number-linked))))
+       (or attr (throw 'return (1- number-linked)))
+       (if (/= ino-opened (nth 10 attr))
+           (setq number-opened number-linked))))))
 
 ;; Make the given server, if non-nil, be the current server.  Then make the
 ;; given group, if non-nil, be the current group of the current server.  Then
@@ -287,6 +302,56 @@ by nnmaildir-request-article.")
       (setq pos (match-end 0))))
   string)
 
+(defun nnmaildir--emlink-p (err)
+  (and (eq (car err) 'file-error)
+       (string= (caddr err) "too many links")))
+
+(defun nnmaildir--eexist-p (err)
+  (eq (car err) 'file-already-exists))
+
+(defun nnmaildir--new-number (nndir)
+  "Allocate a new article number by atomically creating a file under NNDIR."
+  (let ((numdir (nnmaildir--num-dir nndir))
+       (make-new-file t)
+       (number-open 1)
+       number-link previous-number-link path-open path-link ino-open)
+    (nnmaildir--mkdir numdir)
+    (catch 'return
+      (while t
+       (setq path-open (concat numdir (number-to-string number-open)))
+       (if (not make-new-file)
+           (setq previous-number-link number-link)
+         (nnmaildir--mkfile path-open)
+         ;; If Emacs had O_CREAT|O_EXCL, we could return number-open here.
+         (setq make-new-file nil
+               previous-number-link 0))
+       (let* ((attr (file-attributes path-open))
+              (nlink (nth 1 attr)))
+         (setq ino-open (nth 10 attr)
+               number-link (+ number-open nlink))
+         (if (or (< nlink 1) (< number-link nlink))
+             (signal 'error '("Arithmetic overflow"))))
+       (if (= number-link previous-number-link)
+           ;; We've already tried this number, in the previous loop iteration,
+           ;; and failed.
+           (signal 'error `("Corrupt internal nnmaildir data" ,path-open)))
+       (setq path-link (concat numdir (number-to-string number-link)))
+       (condition-case err
+           (progn
+             (add-name-to-file path-open path-link)
+             (throw 'return number-link))
+         (error
+          (cond
+           ((nnmaildir--emlink-p err)
+            (setq make-new-file t
+                  number-open number-link))
+           ((nnmaildir--eexist-p err)
+            (let ((attr (file-attributes path-link)))
+              (if (/= (nth 10 attr) ino-open)
+                  (setq number-open number-link
+                        number-link 0))))
+           (t (signal (car err) (cdr err))))))))))
+
 (defun nnmaildir--update-nov (server group article)
   (let ((nnheader-file-coding-system 'binary)
        (srv-dir (nnmaildir--srv-dir server))
@@ -398,30 +463,7 @@ by nnmaildir-request-article.")
                                      nnmaildir--extra)
              num (nnmaildir--art-num article))
        (unless num
-         ;; Allocate a new article number.
-         (erase-buffer)
-         (setq numdir (nnmaildir--num-dir dir)
-               file (nnmaildir--num-file numdir)
-               num -1)
-         (nnmaildir--mkdir numdir)
-         (write-region "" nil file nil 'no-message)
-         (while file
-           ;; Get the number of links to file.
-           (setq attr (nth 1 (file-attributes file)))
-           (if (= attr num)
-               ;; We've already tried this number, in the previous loop
-               ;; iteration, and failed.
-               (signal 'error `("Corrupt internal nnmaildir data" ,numdir)))
-           ;; If attr is 123, try to link file to "123".  This atomically
-           ;; increases the link count and creates the "123" link, failing
-           ;; if that link was already created by another Gnus, just after
-           ;; we stat()ed file.
-           (condition-case nil
-               (progn
-                 (add-name-to-file file (concat numdir (format "%x" attr)))
-                 (setq file nil)) ;; Stop looping.
-             (file-already-exists nil))
-           (setq num attr))
+         (setq num (nnmaildir--new-number dir))
          (setf (nnmaildir--art-num article) num))
        ;; Store this new NOV data in a file
        (erase-buffer)
@@ -1502,16 +1544,15 @@ by nnmaildir-request-article.")
                   (add-name-to-file permarkfile mfile)
                 (error
                  (cond
-                  ((eq (car err) 'file-already-exists))
+                  ((nnmaildir--eexist-p err))
                   ((and (eq (car err) 'file-error)
                         (string= (caddr err) "no such file or directory"))
                    (nnmaildir--mkdir mdir)
-                   (write-region "" nil permarkfile nil 'no-message)
+                   (nnmaildir--mkfile permarkfile)
                    (add-name-to-file permarkfile mfile))
-                  ((and (eq (car err) 'file-error)
-                        (string= (caddr err) "too many links"))
+                  ((nnmaildir--emlink-p err)
                    (let ((permarkfilenew (concat permarkfile "{new}")))
-                     (write-region "" nil permarkfilenew nil 'no-message)
+                     (nnmaildir--mkfile permarkfilenew)
                      (rename-file permarkfilenew permarkfile 'replace)
                      (add-name-to-file permarkfile mfile)))
                   (t (signal (car err) (cdr err)))))))