start

Portsにnetatalk 3.0.3がキタ━━━(゚∀゚)━━━ !!!!!

Portsのnetatalk3が3.0.3に更新された。

普通に更新してもいいのだが、折角なので[netatalk-ja:0155] Re: 3.0.2でPhotoshop CS5で濁音のファイルがセーブできないのパッチを適用してみる。

この問題を簡単に解説すると、netatalkがリソースフォークを処理する際にファイル名の文字コード変換(Unicode正規化含む)が正常に働かないため、リソースフォークを持ちファイル名に非ASCII文字を含むファイルが保存できないというもの。最近はリソースフォークを持つファイルも減ったようなので(かくいう私はPantherの後半の頃からMacを使い出したのでリソースフォーク全盛の頃を知らなかったりするのだが)、殆どのnetatalk環境では問題が起きないと思われる。ファイルシステムの文字コード絡みの問題なので、ZFSでUnicode正規化を強制してるような環境でも問題は起きない(まさにうちの環境w)。

尚、パッチはTrunk開発ラインに取り込まれているので、3.0.4リリースの暁には直る模様。

果てしなく行儀が悪いけど、お手軽にportsにパッチする形でインストールする。

cd /usr/ports/net/netatalk3
sudo make patch
cd work/netatalk-3.0.3
sudo curl -O http://www003.upp.so-net.ne.jp/hat/files/git-bug511-forkname.patch.gz
sudo gzip -d git-bug511-forkname.patch.gz
sudo patch -p1 < git-bug511-forkname.patch
cd ../../
sudo make
sudo make install

パッチをあてると3.0.4devとしてインストールされるので確認。

$ afpd -v
afpd 3.0.4dev - Apple Filing Protocol (AFP) daemon of Netatalk
 
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version. Please see the file COPYING for further information and details.
 
afpd has been compiled with support for these features:
...

よし、OKだ。

ZFS mirrorのプールがデグレードしていたでござる

サーバの共有フォルダに繋がらなくなってしまった。サーバ自体は見えておりpingも通るのだが、いざ繋ごうとするとタイムアウト。その時は家のネットワークを色々弄くっていたので、それが原因かな?と気にも留めなかった。

翌日になっても繋がらなかったので、原因を調べるべくsshしたが一向に応答が帰ってこない。pingは通るのに。コンソールから直接ログインを試みるも、ユーザー名入力後に応答が帰ってこずに撃沈。これはヤバい雰囲気。仕方なく電源リセット。

幸い、問題なく起動しsshも繋がったので調査開始。

まずは /var/log/messages を見てみる。

...
Apr 19 20:24:31 Freyja kernel: mpt0: request 0xffffff800082f3a0:16151 timed out for ccb 0xfffffe0007da2800 (req->ccb 0xfffffe0007da2800)
Apr 19 20:25:16 Freyja kernel: mpt0: attempting to abort req 0xffffff800082f3a0:16151 function 0
Apr 19 20:25:16 Freyja kernel: mpt0: mpt_wait_req(1) timed out
Apr 19 20:25:16 Freyja kernel: mpt0: mpt_recover_commands: abort timed-out. Resetting controller
Apr 19 20:25:16 Freyja kernel: mpt0: mpt_cam_event: 0x80
Apr 19 20:25:16 Freyja kernel: mpt0: mpt_cam_event: 0x80
Apr 19 20:25:16 Freyja kernel: mpt0: completing timedout/aborted req 0xffffff800082f3a0:16151
Apr 19 20:31:14 Freyja kernel: mpt0: request 0xffffff800082c460:17593 timed out for ccb 0xfffffe0007da2800 (req->ccb 0xfffffe0007da2800)
Apr 19 20:31:14 Freyja kernel: mpt0: attempting to abort req 0xffffff800082c460:17593 function 0
Apr 19 20:31:18 Freyja kernel: mpt0: completing timedout/aborted req 0xffffff800082c460:17593
Apr 19 20:31:18 Freyja kernel: mpt0: abort of req 0xffffff800082c460:0 completed
Apr 19 20:32:42 Freyja kernel: mpt0: request 0xffffff8000833720:17797 timed out for ccb 0xfffffe0007da2800 (req->ccb 0xfffffe0007da2800)
Apr 19 20:32:45 Freyja kernel: mpt0: attempting to abort req 0xffffff8000833720:17797 function 0
Apr 19 20:32:45 Freyja kernel: mpt0: completing timedout/aborted req 0xffffff8000833720:17797
Apr 19 20:32:45 Freyja kernel: mpt0: abort of req 0xffffff8000833720:0 completed
...

なんかヤバげなログががが。mptってことはHDDが逝っちゃった系・・・?

次にZFSプールの状態を見てみる。

$ zpool status
  pool: zroot
 state: DEGRADED
status: One or more devices could not be opened.  Sufficient replicas exist for
	the pool to continue functioning in a degraded state.
action: Attach the missing device and online it using 'zpool online'.
   see: http://www.sun.com/msg/ZFS-8000-2Q
 scan: scrub repaired 0 in 3h41m with 0 errors on Fri Nov  2 00:13:33 2012
config:

	NAME                      STATE     READ WRITE CKSUM
	zroot                     DEGRADED     0     0     0
	  mirror-0                DEGRADED     0     0     0
	    12202321002681728230  UNAVAIL      0     0     0  was /dev/da0p3
	    da0p3                 ONLINE       0     0     0

errors: No known data errors

オフフ、ミラーの片割れがUNAVAILになっとる。本来あるべきada7p3が行方不明。

camcontrol devlistで見てもada7が出てこないので本格的にお亡くなりになった?と考えつつ、最後の望みをかけてマシン開腹&ケーブルチェック。なんか微妙にSATAケーブルが緩かった気がする。いざ起動アーンドzpool status!!

  pool: zroot
 state: ONLINE
status: One or more devices is currently being resilvered.  The pool will
	continue to function, possibly in a degraded state.
action: Wait for the resilver to complete.
 scan: resilver in progress since Sun Apr 21 11:03:14 2013
    1.01G scanned out of 557G at 9.96M/s, 15h52m to go
    1.01G resilvered, 0.18% done
config:

	NAME        STATE     READ WRITE CKSUM
	zroot       ONLINE       0     0     0
	  mirror-0  ONLINE       0     0     0
	    ada7p3  ONLINE       0     0     3  (resilvering)
	    da0p3   ONLINE       0     0     0

ktkr!!

とりあえず、ハードリセットもしてしまったことだし、他のプールも含めてscrub。問題のプールは560GBで4時間ほど掛かった。

errors: No known data errors

  pool: zroot
 state: ONLINE
status: One or more devices has experienced an unrecoverable error.  An
	attempt was made to correct the error.  Applications are unaffected.
action: Determine if the device needs to be replaced, and clear the errors
	using 'zpool clear' or replace the device with 'zpool replace'.
   see: http://www.sun.com/msg/ZFS-8000-9P
 scan: scrub repaired 384K in 4h8m with 0 errors on Sun Apr 21 15:38:59 2013
config:

	NAME        STATE     READ WRITE CKSUM
	zroot       ONLINE       0     0     0
	  mirror-0  ONLINE       0     0     0
	    ada7p3  ONLINE       0     0     6
	    da0p3   ONLINE       0     0     0

errors: No known data errors

よしよし、無事修復出来たようなのでzpool clearでエラーカウントをクリアして完了っと。

しかしSATAケーブルが緩むなんて事あるんだねぇ。ラッチ付きじゃないとあかんな。

Disk Arbitration Frameworkでディスクのマウントを抑制する

物理HDDをVirtualBoxのVMに割り当てて使用中、Mac OSからそのHDDをマウントされると非常に不味いので、マウントを抑制するコマンドを作ってみた。

引数にBSDデバイス名(/dev/disk5とか)を渡してコマンドを実行すると、Mac OS側からのマウントをブロックする。デバイス名は前方一致で検索しているので、/dev/disk5と書けばdisk5全体が、/dev/disk5s1と書けばdisk5のスライス1がブロック対象となる。終了はCtrl-Cで。

どこか処理が不味いようで、何かの拍子にコマンドを終了してもFinderからはマウント不能?と認識されてしまうことがある(実際はマウントされているのでopenコマンドを使えば開ける)。こうなると再起動するしかなくなる…。AS ISでおながいします。

blockMount.c
// -*- coding: utf-8-unix -*-
// ---------------------------------------------------------
//  Block Mount Utility
//  Copyright (c) 2013 Decomo
// ---------------------------------------------------------
//  License:
//      2-clause BSD license
//  Reference:
//      https://github.com/nanoant/mountblockd
//      http://stackoverflow.com/questions/3720503/how-can-i-prevent-ejection-of-a-disk-during-an-operation-on-mac-os-x
//      http://superuser.com/questions/336455/mac-lion-fstab-is-deprecated-so-what-replaces-it-to-prevent-a-partition-from-m
//      http://www.geekpage.jp/blog/?id=2011/10/4/3
//      http://developer.apple.com/library/mac/#documentation/Darwin/Reference/DiscArbitrationFramework/
//  Compile:
//      cc BlockMount.c -g -o BlockMount -framework CoreFoundation -framework DiskArbitration
// ---------------------------------------------------------
 
#include <CoreFoundation/CoreFoundation.h>
#include <DiskArbitration/DiskArbitration.h>
#include <stdio.h>
#include <stdlib.h>
 
const char **g_blockDeviceList = NULL;
int g_blockDeviceListCount = 0;
int g_verbose = 0;
int g_run = 1;
 
DADissenterRef diskWillMount(DADiskRef disk, void *context)
{
    const char *devName = DADiskGetBSDName(disk);
    DADissenterRef dissenter = NULL;
 
    if (devName)
    {
        int block=0;
        for (int i=0; i<g_blockDeviceListCount; i++)
        {
            if (strstr(devName, g_blockDeviceList[i]))
                block=1;
        }
        if (block)
        {
            if (g_verbose)
                fprintf(stderr, "BLOCKED mount `%s'\n", devName);
            dissenter = DADissenterCreate(kCFAllocatorDefault, kDAReturnNotPermitted, NULL);
        }
    }
 
    return dissenter;
}
 
void signalHandler(int sig)
{
    switch (sig) {
    case SIGHUP:
        if (g_verbose)
            fprintf(stderr, "received SIGHUP, terminating...\n");
        CFRunLoopStop(CFRunLoopGetCurrent());
        g_run=0;
        break;
    case SIGTERM:
        if (g_verbose)
            fprintf(stderr, "received SIGTERM, terminating...\n");
        CFRunLoopStop(CFRunLoopGetCurrent());
        g_run=0;
        break;
    case SIGINT:
        if (g_verbose)
            fprintf(stderr, "received SIGINT, terminating...\n");
        CFRunLoopStop(CFRunLoopGetCurrent());
        g_run=0;
        break;
    case SIGQUIT:
        if (g_verbose)
            fprintf(stderr, "received SIGQUIT, terminating...\n");
        CFRunLoopStop(CFRunLoopGetCurrent());
        g_run=0;
        break;
    default:
        fprintf(stderr, "unhandled signal (%d) %s\n", sig, strsignal(sig));
        break;
    }
}
 
int main(int argc, const char *argv[])
{
    int usage=1;
    if (argc >= 2)
    {
        usage=0;
        if (strcmp(argv[1], "-v") == 0)
        {
            g_verbose=1;
            g_blockDeviceList = &argv[2];
            g_blockDeviceListCount = argc-2;
            if (argc == 2)
            {
                usage=1;
            }
        }
        else
        {
            g_blockDeviceList = &argv[1];
            g_blockDeviceListCount = argc-1;
        }
    }
 
    if (usage)
    {
        fprintf(stderr, "usage: %s [-v] deviceName1 [deviceName2] [deviceName3] [...] \n", argv[0]);
        return EXIT_FAILURE;
    }
 
    fprintf(stderr, "blocking mount device(s) : ");
    for (int i=0; i<g_blockDeviceListCount; i++)
    {
        fprintf(stderr, "%s ", g_blockDeviceList[i]);
    }
    fprintf(stderr, "\n");
 
    signal(SIGHUP,  signalHandler);
    signal(SIGTERM, signalHandler);
    signal(SIGINT,  signalHandler);
    signal(SIGQUIT, signalHandler);
 
    DAApprovalSessionRef session = DAApprovalSessionCreate(kCFAllocatorDefault);
    if (!session)
    {
        fprintf(stderr, "failed to create DAApprovalSession.\n");
        return EXIT_FAILURE;
    }
    else
    {
        DARegisterDiskMountApprovalCallback(session, NULL, diskWillMount, NULL);
        DAApprovalSessionScheduleWithRunLoop(session, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
 
        while (g_run)
        {
            CFRunLoopRun();
        }
 
        DAApprovalSessionUnscheduleFromRunLoop(session, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
        DAUnregisterApprovalCallback(session, diskWillMount, NULL);
        CFRelease(session);
    }
 
    return 0;
}

とまぁ、とりあえず作ってはみたものの、結局仮想マシンはVMware Fusionを常用してるので出番がないっていう。何だかんだでVirtualBoxは不安定なんだよねぇ。8 CPU以上使えるのは魅力的なんだけど。

NSArrayをFinder風の並び順でソートする

こんなファイルがあったとする。

file-a
file-1
file-01
file-2
file-10
file-b
file-ab

辞書式(普通の昇順ソート)で並べ替えると

file-01
file-1
file-10
file-2
file-a
file-ab
file-b

となるわけだが、Finderでソートすると

file-1
file-01
file-2
file-10
file-a
file-ab
file-b

となる。連続した数字部分を数値と見なし、数値順で並べてくれる。個人的にはこの方が分かりやすくて好きだ。

で、この数値順ソートをCocoaで実現するにはどーしたらいいのかなー?と思って調べていたら、Technical Q&A QA1159: Sorting Like the Finderという、そのまんまの記事がADCにあった。流石林檎様、分かっていらっしゃる。

上記Q&Aのコードを改造してNSMutableArrayのカテゴリメソッドにするとスマートに使えて良い感じ。

NSString配列のソートはもちろん、任意のオブジェクトの場合はソートに使うNSStringインスタンス変数名をsortByFinderOrderWithStringObjectKey:に渡せばおk。

#include <CoreServices/CoreServices.h>
#include <sys/param.h>
 
static CFComparisonResult CompareLikeTheFinder(const void *val1, const void *val2, void *context)
{
    SInt32          compareResult;
    CFStringRef     lhsStr;
    CFStringRef     rhsStr;
    CFIndex         lhsLen;
    CFIndex         rhsLen;
    UniChar         lhsBuf[MAXPATHLEN];
    UniChar         rhsBuf[MAXPATHLEN];
 
    // val1 is the left-hand side CFString.
    // val2 is the right-hand side CFString.
    if (context)
    {
        NSString *key = (NSString *)context;
        lhsStr = (CFStringRef)[(NSObject *)val1 valueForKey:key];
        rhsStr = (CFStringRef)[(NSObject *)val2 valueForKey:key];
    }
    else
    {
        lhsStr = (CFStringRef)val1;
        rhsStr = (CFStringRef)val2;
    }
    lhsLen = CFStringGetLength(lhsStr);
    rhsLen = CFStringGetLength(rhsStr);
 
    // Get the actual Unicode characters (UTF-16) for each string.
    CFStringGetCharacters(lhsStr, CFRangeMake(0, lhsLen), lhsBuf);
    CFStringGetCharacters(rhsStr, CFRangeMake(0, rhsLen), rhsBuf);
 
    // Do the comparison.
    UCCompareTextDefault(
                         kUCCollateComposeInsensitiveMask
                         | kUCCollateWidthInsensitiveMask
                         | kUCCollateCaseInsensitiveMask
                         | kUCCollateDigitsOverrideMask
                         | kUCCollateDigitsAsNumberMask
                         | kUCCollatePunctuationSignificantMask,
                         lhsBuf,
                         lhsLen,
                         rhsBuf,
                         rhsLen,
                         NULL,
                         &compareResult
                         );
 
    // Return the result. Conveniently, UCCompareTextDefault 
    // returns -1, 0, or +1, which matches the values for 
    // CFComparisonResult exactly.
    return (CFComparisonResult)compareResult;
}
 
static void SortCFMutableArrayLikeTheFinder(CFMutableArrayRef array, CFStringRef key)
{
    CFArraySortValues(
                      array, 
                      CFRangeMake(0, CFArrayGetCount(array)),
                      CompareLikeTheFinder,
                      key
                      );
}
 
@interface NSMutableArray (PKAdditions)
- (void)sortByFinderOrder;
- (void)sortByFinderOrderWithStringObjectKey:(NSString *)key;
@end
 
@implementation NSMutableArray (PKAdditions)
- (void)sortByFinderOrder
{
    [self sortByFinderOrderWithStringObjectKey:nil];
}
 
- (void)sortByFinderOrderWithStringObjectKey:(NSString *)key
{
    SortCFMutableArrayLikeTheFinder(self, key);
}
@end

実際のファイル名やディレクトリ名をソートする場合は、- [NSFileManager displayNameAtPath:(NSString *)path]で得られる名前をソートしないとFinder順にはならない。なぜかというと、Finderから見えるディレクトリ名はローカライズ(~/Documents → 書類 みたいなの)された物なので。

Portsにnetatalk 3.0.2がキタ━━━(゚∀゚)━━━ !!!!!

早速インスコ。portupgradeだと、なぜか「too few arguments to function 'load_volumes'」とビルドエラーになってしまうので、3.0.1を一旦アンインストールしたらすんなり入った(make cleanしても3.0.1のヘッダを見に行ってしまうっぽい?)。

3.0.1まではパスの扱いがおかしく、FreeBSDで[Homes]が実質機能しなかった訳だが3.0.2で無事に解決した。ありがとうHATさん。パスの扱いについては、今も検討が続いている模様

これでafp.confもスッキリした。

;
; Netatalk 3.x configuration file
;
 
[Global]
; Global server settings
vol preset = _DEFAULT
log file = /var/log/netatalk.log
 
[_DEFAULT]
file perm = 0600
directory perm = 0700
 
[Homes]
basedir regex = /usr/home
home name = $u
 
[Time Machine]
path = /Volumes/TimeMachine
time machine = yes
vol size limit = 2097152
 
[Data]
path = /Volumes/Data
 
[Public]
path = /usr/home/PUBLIC
file perm = 0660
directory perm = 0770

basedir regexにはシンボリックリンクではなく、実リンクで書くのがミソ。

大事なことなのでもう一度。ありがとうHATさん。

  • start.txt
  • 最終更新: 2022-07-27 15:26
  • by Decomo