Androidでショートネームを取得する方法

173 views
Skip to first unread message

中井健介

unread,
Feb 23, 2011, 12:08:32 AM2/23/11
to android-g...@googlegroups.com
初めまして、1年ぐらい前から個人的にAndroid開発の勉強をしております、中井と申します。
よろしくお願いいたします。

いつもこちらのML、定例会などでAndroidの勉強をさせてもらっております。

さて、本メールの題名にある件で悩んでおりますので、質問メールを出させて頂きました。
ご指摘やお気づきの点などございましたら、教えて頂ければと思います。

単刀直入に申し上げますと、
AndroidでSDカード上のディレクトリ、ファイルのショートネームは取得出来ますでしょうか?
もし出来るとしたらその方法を教えて頂けないでしょうか?

以下は私が調査した内容です。
もちろんショートネーム取得には至っておりません。

-★-★-★-★-★-★-★-★-★-★-★-★-

まずはじめにエミュレータ(Android 2.2)を起動して、adb shellで入って、
mountコマンドを叩いて確認したところ、

# mount
... 省略 ...
/dev/block/vold/179:0 /mnt/sdcard vfat rw,dirsync,nosuid,nodev,noexec,uid=1000,g
id=1015,fmask=0702,dmask=0702,allow_utime=0020,codepage=cp437,iocharset=iso8859-
1,shortname=mixed,utf8,errors=remount-ro 0 0
... 省略 ...

と出てきました。
「shortname=mixed」という記載があるということはとれるのでは?と思い調査を開始しました。

Android-SDKにあるクラスライブラリを見る限り、そのようなAPIは見当たりませんでした。
ですので、ネイティブコード側で取得することは出来ないかと
NDK(r5b, android-8)のヘッダファイルを探していたところ

・include/linux/msdos_fs.h

#define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, struct dirent [2])
#define VFAT_IOCTL_READDIR_SHORT _IOR('r', 2, struct dirent [2])

・include/linux/dirent.h

struct dirent {
long d_ino;
__kernel_off_t d_off;
unsigned short d_reclen;
char d_name[256];
};

という定義を見つけることが出来ました。
ioctlを使えば、ショートネームが取得出来るのではないかと考え、
以下のようなコードを作成し、実行してみました(乱れたコードで申し訳ありません)。

#include "com_example_shortfilename_ShortFileName.h"
#include <jni.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <linux/dirent.h>
#include <android/log.h>

struct kernel_dirent {
long d_ino;
long d_off;
unsigned short d_reclen;
char d_name[256];
};

#define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, struct kernel_dirent [2])
#define VFAT_IOCTL_READDIR_SHORT _IOR('r', 2, struct kernel_dirent [2])

jstring Java_com_example_shortfilename_ShortFileName_getShortFileName(
JNIEnv* env,
jobject obj,
jstring longFileName) {

int fd;
struct kernel_dirent de[2] = {0};
jstring name;

fd = open("/sdcard", O_RDONLY);

if(fd == 0) {
name = (*env)->NewStringUTF(env, "open failed!!!");
} else {
int ret = ioctl(fd, VFAT_IOCTL_READDIR_BOTH, (long)de);
// int ret = ioctl(fd, VFAT_IOCTL_READDIR_SHORT, (long)de);
if(ret != -1) {
__android_log_print(
ANDROID_LOG_DEBUG,
"NDK",
"Message : ret: %d de[0]<d_ino: %d, d_off: %d, d_reclen: %d>
d[1]<d_ino: %d, d_off: %d, d_reclen: %d>",
ret,
de[0].d_ino,
de[0].d_off,
de[0].d_reclen,
de[1].d_ino,
de[1].d_off,
de[1].d_reclen);

char dname[1024] = {0};
memcpy(dname, de[0].d_name, de[0].d_reclen);
name = (*env)->NewStringUTF(env, dname);
} else
name = (*env)->NewStringUTF(env, "Failed!!!");
}

close(fd);

return name;
}

このソースファイルをNDKでビルドし、Activity側から本関数を呼び出したところ、

Message : ret: 1 de[0]<d_ino: 0, d_off: 0, d_reclen: 1> d[1]<d_ino: 0,
d_off: 0, d_reclen: 0>

とlogcatに表示されました(de[0].d_nameには'.'が格納されていました)。
ioctlの戻り値が「1」なので、成功しているのだと思われますが(fs/fat/dir.cを見てそう判断しました)、
肝心のディレクトリエントリの情報が取得出来ていません。

試しにディレクトリのパスを

/sdcard/tmp、/mnt/sdcard/tmp、/sdcard/tmp/hogehoge.bin

などいろいろ変えてみたところ(いずれも存在するパス)、ファイルパスを指定するとioctlに失敗し(戻り値:-1)、
ディレクトリパスを指定すると成功はしますが、エントリの情報が取得出来ない、という状況でした(上と同じ)。

-★-★-★-★-★-★-★-★-★-★-★-★-

何かお気づきの点などございましたら、教えて頂ければ幸いです。
よろしくお願いいたします。

Masahiko KIMOTO

unread,
Feb 23, 2011, 2:22:57 AM2/23/11
to android-g...@googlegroups.com
> ・include/linux/msdos_fs.h
>
> #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, struct dirent [2])
> #define VFAT_IOCTL_READDIR_SHORT _IOR('r', 2, struct dirent [2])
>
> ・include/linux/dirent.h
>
> struct dirent {
> long d_ino;
> __kernel_off_t d_off;
> unsigned short d_reclen;
> char d_name[256];
> };
>
> という定義を見つけることが出来ました。
> ioctlを使えば、ショートネームが取得出来るのではないかと考え、

よくわかりませんが、readdirのかわりにioctlを使うのではないでしょうか?
man opendir
man readdir
あたりを見るとよいと思います。

'.'がとれるのは、directory entryの最初の2つは'.', '..'だからではないかと。

----------------------------------------------------------------------
木本 雅彦 / Masahiko KIMOTO, Ph. D.
E-mail: kim...@ohnolab.org URL: http://www.ohnolab.org/~kimoto

中井健介

unread,
Feb 23, 2011, 3:25:22 AM2/23/11
to android-g...@googlegroups.com
木本さま

中井です。
素早い返信ありがとうございます。

> '.'がとれるのは、directory entryの最初の2つは'.', '..'だからではないかと。

たしかにそうですね!
正しく取れているということは、ショートネームも取得出来そうな気がしてきました。
ご指摘を頂いた

> man opendir
> man readdir

をチェックして、再度トライしてみます。

また質問させて頂くかも知れませんが、よろしくお願いいたします。

中井健介

unread,
Feb 24, 2011, 8:26:00 AM2/24/11
to android-g...@googlegroups.com
お世話になっております。
中井です。

昨日質問させていただいたショートネーム取得の件、解決しましたので、
報告させていただきます。

木本さまからコメントがあったopendir、readdirの使い方を調べました。
次のようなサンプルを見つけました。

for(dp=readdir(dir);dp!=NULL;dp=readdir(dir)){
printf("%s\n",dp->d_name);
}

イテレータのように繰り返し呼び出して使うんですね。

ioctlでも同じように呼び出せば、ショートネームをリストアップ出来るのではないかと考えて、以下のようなコードを作成しました。

#include "com_example_helloshortname_HelloShortFileName.h"


#include <jni.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <linux/dirent.h>
#include <android/log.h>

#define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, struct dirent [2])


#define VFAT_IOCTL_READDIR_SHORT _IOR('r', 2, struct dirent [2])

jstring JNICALL
Java_com_example_helloshortname_HelloShortFileName_getShortName(JNIEnv*
env, jobject obj, jstring param) {

int fd;
struct dirent entries[2];
jstring shortName;
const char* longName;
int ioctlRet;

longName = (*env)->GetStringUTFChars(env, param, NULL);

fd = open(longName, O_RDONLY);
if(fd == 0)
return (*env)->NewStringUTF(env, "Open failed.");

for(;;) {
// ioctlRet = ioctl(fd, VFAT_IOCTL_READDIR_SHORT, &entries);
ioctlRet = ioctl(fd, VFAT_IOCTL_READDIR_BOTH, &entries);
if(ioctlRet == -1) {
return (*env)->NewStringUTF(env, "Ioctl failed");
} else {
if(entries[0].d_reclen == 0)
return (*env)->NewStringUTF(env, "Ioctl success but data not exists");

__android_log_print(
ANDROID_LOG_DEBUG,
"NDK",
"Short name: %s, Long name: %s",
entries[0].d_name,
entries[1].d_name);
}
}

close(fd);

return (*env)->NewStringUTF(env, "Ioctl success");
}

呼び出し元としては、

getShortName("/sdcard");
getShortName("/mnt/sdcard");
getShortName("/mnt/sdcard/tmp");

などとしました。手持ちのHT-03Aで動かしてみたところ、

Short name: ., Long name:
Short name: .., Long name:
Short name: LOST.DIR, Long name:
Short name: DCIM, Long name:
Short name: DATA, Long name: data
Short name: ANDROI~1, Long name: .android_secure
Short name: DOLPHI~1, Long name: dolphinbrowser_gestures
Short name: DOWNLO~1, Long name: downloads
Short name: PICTURES, Long name: Pictures
Short name: ANDROID, Long name: Android
Short name: DOWNLOAD, Long name: download
Short name: PING.XML, Long name: ping.xml
Short name: TMP, Long name: tmp
Short name: DROPBO~1, Long name: .dropboxthumbs
Short name: MEDIA, Long name: media
Short name: DOLPHI~2, Long name: Dolphin_Browser_Mini
※ディレクトリパスは/sdcardを指定しています。

というように見事にショートネームとロングネームをリストアップすることに成功しました。取れたときは控えめに歓喜の雄叫びをあげてしまいました。

To 木本さま
コメント、本当にありがとうございました。

Reply all
Reply to author
Forward
0 new messages