Well, with no guarantees or promises whatsoever...here's my first attempt. I'm certain someone else can come up with a much more robust solution but it's a starting point.

Since I typically see a batch of the error messages during an FTS update, my current usage is:

1.  doveadm fts rescan -u user-to-fix
2.  Perform a fts search to do a full mailbox scan
3. Check the mail log to see the date/time of the errors (I usually have a window open with tail -f) 4. Something like "grep 'Mar 3 20:17' mail.log > mail.err" gives me a starting point

execute dovesisfix and see if it helps.

--
Daniel

On 3/3/2015 1:08 PM, Daniel Miller wrote:
This seems simple enough...I'm just not script wizard. If someone can throw together a starting point I can test and tweak it from there. It seems to me:

1.  Read /var/mail/mail.err or specified logfile
2. For each "failed: read(/var/mail/attachments/aa/bb/attachmentHash-userHash" line,
    a. Confirm /var/mail/attachments/aa/bb/hashes/attachmentHash exists
i. If attachmentHash is missing display such for possible backup searching.
    b. create link attachmentHash-userHash to hashes/attachmentHash
3.  Continue to end of file

Can this be done via "pure" BASH?  Need sed/awk as well?


#!/bin/bash

# These variables need to be customized for your particular installation
LOGFILE='/var/log/mail.err'
ATTACHMENT_STORAGE_BASE='/var/mail/attachments'

# These variables are based on current Dovecot behaviour and should not require 
changing
HASH_FOLDER='hashes'

# Initialization
PREVIOUS_ERR=''
ERR=''

function usage
{
    echo "Dovecot Single-Instance-Storage Attachment Repair"
    echo "usage: dovesisfix [-d] [-t] [-v] [-h]"
    echo "   -t | --test-only   perform logfile analysis and show steps to be 
taken without any on-disk modification"
    echo "   -v | --verbose     provide verbose messages at each step"
    echo "   -d | --debug               provide additional debug messages"
    echo "   -h | --help                this screen"
}

while [ "$1" != "" ]; do
    case $1 in
        -d | --debug )          DEBUG=1
                                VERBOSE=1
                                ;;
        -t | --test-only )      TESTMODE=1
                                ;;
        -v | --verbose )        VERBOSE=1
                                ;;
        -h | --help )           usage
                                exit
                                ;;
        * )                     usage
                                exit 1
    esac
    shift
done

while read -r LINE
do
    ERR=$LINE

    # Format of log line has date, host, process, user, mail storage file, and 
then the
    # attachment path failure, followed by a duplicate of the path as an 
argument to open,
    # and then final details.

    # Verify this line is indeed a dovecot attachment error.  Don't look for 
"dovecot" specifically
    # in case that name was changed - but the individual worker names are 
probably safe searches.
    # So we test against "attachments-connector" - hopefully that's good enough.
    TEST=$(echo "$ERR" | sed -n "s|.*attachments-connector.*|1|p")
    if [ "$TEST" != "1" ]; then
        # Not found - not relevant
        if [ "$DEBUG" = 1 ]; then
            echo "Skipping non-relevant log line. $ERR"
        fi
        continue
    fi

    # Remove prefacing details from log line - find start of attachment path 
within log line
    # This is a greedy match - so the second attachment path is returned along 
with the trailing info
    ATTACH_LINE_FILTER="s|.*$ATTACHMENT_STORAGE_BASE||"
    ATTACH_LINE=$(echo "$ERR" | sed "$ATTACH_LINE_FILTER")

    # Now extract the aa/bb/ prefix, the base attachment file name, and user 
hash
    CATEGORY_PATH="${ATTACH_LINE:1:5}"
    BASE_HASH="${ATTACH_LINE:7:40}"
    USER_HASH="${ATTACH_LINE:48:32}"

    
ATTACH_SOURCE="$ATTACHMENT_STORAGE_BASE/$CATEGORY_PATH/$HASH_FOLDER/$BASE_HASH"
    
ATTACH_TARGET="$ATTACHMENT_STORAGE_BASE/$CATEGORY_PATH/$BASE_HASH-$USER_HASH"

    # There appear to be duplicate lines - so to try to filter some out.
    if [ "$PREVIOUS_TARGET" = "$ATTACH_TARGET" ]; then
        if [ "$DEBUG" = 1 ]; then
            echo "Skipping duplicate log line for $ATTACH_SOURCE-$ATTACH_TARGET"
        fi
        continue
    fi
    PREVIOUS_TARGET=$ATTACH_TARGET

    # If in debug/verbose mode show operation about to occur
    if [ "$VERBOSE" = 1 ]; then
        echo "The file $ATTACH_SOURCE must be linked to $ATTACH_TARGET"
    fi

    # Verify that source exists
    if [ ! -f "$ATTACH_SOURCE" ]; then
        echo "ERROR: File $ATTACH_SOURCE does not exist. You must restore this 
from a backup and run this utility again."
    fi
    # This is a Good Thing.
    if [ "$DEBUG" = 1 ]; then
        echo "The file $ATTACH_SOURCE appears to be a valid file."
    fi

    # Check if user link mysteriously reappeared
    if [ -f "$ATTACH_TARGET" ]; then
        echo "INFO: File $ATTACH_TARGET exists. This may mean the fault has 
been previously corrected. Clearing/rotating the logfile $LOGFILE is 
appropriate now."
        continue
    fi

    # Prepare to create user link
    LINK_LINE="$ATTACH_SOURCE $ATTACH_TARGET"
    if [ "$DEBUG" = 1 ]; then
        echo "About to execute command: ln $LINK_LINE"
    fi

    # If test mode, do nothing
    if [ "$TESTMODE" = 1 ]; then
        continue
    fi

    # There's probably more tests I could/should do - but I don't know how
    # So...if we're not in test mode...time to do it to it.
    LINK_CREATED=$(ln $LINK_LINE)
    if [ "$VERBOSE" = 1 ]; then
        echo "Repair result for $ATTACH_TARGET - $LINK_CREATED"
    fi

done < "$LOGFILE"

Reply via email to