When using Adapter (especially list), sometimes(actually very frequently) data of only one item(row) is changed.

In this case, usually, changing one-under-lying data is easy.

But, updating only one list item is different story.

To update that item to the screen, - changing look-and-feel regarding changed item -  easiest way is calling notifyDataSetChanged().

But, this re-bind views of all list items. This is wasteful.

To avoid this, we should get View of item associated, and then modify it directly.

At first glance, it is easy and "Item position to View" map seems to be useful. But, it's NOT.

Why?

AdapterView usually reuse Views. So, several positions become to share same view at "position to View" map.
That is, whenever try to update item at specific item, we need to check that that item has valid visible View or not.

But, this is not-easy at current(API 15) Android API.


My suggestion is using "View to position" map(henceforth VPMap).

At AdapterView, View is reused. So, Adapter doesn't have lot's of different Views.

So, key size of the VPMap is not large (usually at most 20~30).

And we can easily update recent valid visible position of View easily at getView() of Adapter.

See below sample code.

private final HashMap<View, Integer> mView2PosMap = new HashMap<View, Integer>();
...
public void
setItemActive(int pos) {
    if (pos == mActivePos)
        return;
    View v = Utils.findKey(mView2PosMap, mActivePos);
    if (null != v)
        setToInactive(v);
    v = Utils.findKey(mView2PosMap, pos);
    if (null != v)
        setToActive(v);

    mActivePos = pos;
}
...
@Override
public View
getView(int position, View convertView, ViewGroup parent) {
    View v;
    if (null != convertView)
        v = convertView;
    else
        v = UiUtils.inflateLayout(mContext, R.layout.row);

    mView2PosMap.put(v, position);
    ...
}
...
public static <k,v> K
findKey(HashMap<View, Integer> map, V value) {
    Iterator<k> iter = map.keySet().iterator();
    while(iter.hasNext()) {
        K key = iter.next();
        if (map.get(key).equals(value))
            return key;
    }
    return null;
}

Like above, with this VPMap we can easily find associated visible View and can update only one-updated-item-View without calling 'notifyDataSetChanged()'.


Sometimes developer may need to use MediaPlayer directly instead of using VideoView.

In this case, developer should keep belows in his/her mind.

<NOTE : Gingerbread and ICS are a little different (ICS is more flexible. But, the more strict is the better.>

 - setDisplay Should be called after surface of SurfaceHolder is created.

 - video should be started after it's video size is known (after onVideoSizeChanged is called.)

 - setDisplay() should be called before prepare()


Let's summarize it.


onSurfaceCreate -> setDisplay() -> prepare()/prepareAsync()

: onVideoSizechanged

: onPrepared


=> Now, MediaPlayer is fully prepared to be started.


Tested and verified at Android 4.0.3


When using SlidingDrawer over SurfaceView, SlidingDrawer is cropped by SurfaceView while opening and closing even if SlidingDrawer is over SurfaceView.


Try with below layout.

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <SurfaceView android:id="@+id/surface" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" /> <SlidingDrawer ... />

</RelativeLayout>

In my opinion, that's because SufaceView holding the visible region and display on it.

And, in case of SlidingDrawer, opening and closing area is not known by WindowManager.
So, it may not be considered when holding region by SurfaceView.


Based on above hypothesis, I tried with below layout and confirmed that it works as expected.

The difference is just put a dummy View that covers all SurfaceView to let WindowManager know there is window over SurfaceView.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >
    <SurfaceView
        android:id="@+id/surface"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        />
    <View
        android:id="@+id/touch_ground"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />
    <SlidingDrawer
        ...
        />
</RelativeLayout>

I don't want to tell that this is a kind of bug of Android framework. It's just common-type of issue when things are in together.

I hope that this post is helpful to others.

In case that sqlite database is quite big, preparation for using cursor requires lots of time-consuming-operation.

So, developer may considering about asynchronous way - showing progress while preparing cursor in background.

Then, what is 'Really' time-consuming-operation in preparing cursor?

As my understanding, in Android sqlite cursor, the answer is first 'fillWindow' operation of SQLiteCursor.


Notable thing is, at sqlite cursor, creating cursor doesn't do any real (or practical) thing.

So, creating cursor is done very quickly.

By the way, Sqlite cursor do 'fillWindow' operation at the moment of first 'getCount()' call - See SQLiteCursor.java.

Therefore, to prepare cursor in background, not only creating cursor but also calling first 'getCount()' function are required.


Here is sample function for reference.

    public void
    reloadCursorAsync() {
        DiagAsyncTask.Worker worker = new DiagAsyncTask.Worker() {
            private Cursor newCursor;
            @Override
            public void onPostExecute(DiagAsyncTask task, Err result) {
                changeCursor(newCursor);
            }
            @Override
            public Err doBackgroundWork(DiagAsyncTask task, Object... objs) {
                newCursor = createCursor();
                newCursor.getCount();
                return Err.NO_ERR;
            }
        };
        new DiagAsyncTask(mContext, worker,
                          DiagAsyncTask.Style.SPIN,
                          R.string.loading, false)
        .execute();
    }



Android document says several advantages of android:targetSdkVersion. But, I faced following issue.


Here is my case.


android:minSdkVersion="10"

android:targetSdkVersion="15"

...

<activity ...

android:configChanges="orientation|keyboardHidden"

...


Since v3.2 ScreenSize is additionally required at configChanges to handle orientation change.

But, in this case, minSdkVersion is "10" so, ScreenSize cannot be used.

The problem is that targetSdkVersion is "15".

So, framework doesn't call onConfigurationChanged() callback when orientation is changed.

This is unavoidable and unexpected.


Eventually, I gave up using 'targetSdkVersion'.


I'm not sure there are any other workarounds for these cases, but after experiencing above case, I am trying to Not-To-Use targetSdkVersion option...

This post is history of registering Android App to Google Play.

I hope that this post is useful to developers who has a plan to register App to Google Play.



I registered one free App. to Google Play and after some time - about a-week-after? - I received below E-mail from Google.




This is a notification that your application, Youtube Music Player, with package ID free.yhc.youtube.musicplayer, has been removed from the Google Play Store.


REASON FOR REMOVAL: Violation of the intellectual property and impersonation or deceptive behavior provisions of the Content Policy.

All violations are tracked. Serious or repeated violations of any nature will result in the termination of your developer account, and investigation and possible termination of related Google accounts.

If your developer account is still in good standing, you may revise and upload a policy compliant version of your application as a new package name. Before uploading any new applications, please review the Developer Distribution Agreement and Content Policy.

If you feel we have made this determination in error, you can visit the Google Play Help Center article for additional information regarding this removal.

The Google Play Team




Issued application is the one below


YoutubeMusicPlayer-v1.3.0-2.apk


Main feature of this application is "making playlist of youtube videos and play those continuously without video (only audio) in background.


Actually at that moment, I didn't have any idea regarding what are problems.

So, some E-mails are additionally sent/received

Here is E-mail thread between Google and me.




Hi Google

I received below mail.

Could you describe details about the violations of application free.yhc.youtube.musicplayer.

Actually, I do not know what are issued points.

My developer account is yhcting77@gmail.com.

Thank you in advance and sorry for inconvenience.

YH Cho.




Hi Ting,

Thank you for your note.

We appreciate the opportunity to review your appeal regarding the removal
of your app from Google Play. After an investigation, we affirm our
initial decision and will not be reinstating your application at this
time.

If your developer account is still in good standing, and the nature of
your application allows for you to upload a new, compliant version of this
application to Google Play; please apply the following Content Policy
guidelines to future releases:

Google Play Android Developer Program Policy
(see "Impersonation or Deceptive Behavior"):
http://play.google.com/about/developer-content-policy.html

Regards,
The Google Play Team




Hi Google.


I'm sorry for continuously bothering you.
I reviewed "Google Play Android Developer Program Policy".
In my opinion at this moment, my violation is - concretely -  "Youtube-TM-like-application-icon and App name are used, and this may lead users to misunderstand this App. has some relation with Google.".
Am I right?
As my previous e-mail, please let me know What are issued points and problems concretely and that will be very helpful for me to update application to obey Google's policy.
I really would like to go with Google's Policy but to do it, at first I should know "What is real problem".

Thank you in advance and I'm sorry again for bothering you.
Regards.
YH Cho.




Hi Ting,

Thank you for your note. Your application's icon and title could be
misleading to some users and lead them to think that your app is
authorized by another organization. Please be aware of the "Impersonation
or Deceptive Behavior" section of the Content Policy as you publish your
applications:
http://www.android.com/us/developer-content-policy.html#impersonation



Regards,
The Google Play Team



As described as above E-mail thread, at this moment, I think app name and app icon is mainly issued points of policy violation.

So, I will register App with fix of known issued point - app icon and name.

But, I am not sure that icon and name are really all issued cases.

If there are any other issues I will update those here to leave history and data to help understanding regarding Google Play Policy.

Now, app is re-registered to Google Play. Here



Developer cannot guess when Java GC is run. So, in Android, Bitmap class has special interface 'recycle' to reuse memory as soon as possible when Bitmap is not used anymore.

Based on similar reason, we need to recycle ImageView.


But, here are some points to consider when recycling memory of ImageView.

* ImageView has drawable - most case it is instance of BitmapDrawable - regardless of it's input-resource-type - ex setImageResource(),  setImageURI() or setImageBitmap(). And, ImageView has BitmapDrawable not only the case of setImageBitmap but also some other cases - ex setImageResource.



* Sample code to recycle Bitmap of ImageView is something like below


        // true == (v instanceof ImageView)

        Drawable d = v.getDrawable();

        if (d instanceof BitmapDrawable) { // to make sure.

            BitmapDrawable bmd = (BitmapDrawable)drawable;

            Bitmap bitmap = bmd.getBitmap();

            bitmap.recycle();

        }


* setImageDrawable(), setImageResource() and setImageURI() compares new-input and current used one.

So, if new one is same with current one, ImageView doesn't do anything. (See ImageView.java for details.)


* ImageView doesn't provide any interface to compare that given one is same with current one or not.


Because of above 4 reasons, recycling memory of ImageView is not straight-forward.

Simplest way, in my opinion, is feed newly-created-Bitmap-instance to ImageView whenever changing drawable, and recycle old one.

Because drawable is newly-created-instance, we don't need to compare new one with old one.

So, blind recycling old one, is ok in this case.


Here is sample function.


    public static void

    setThumbnailImageView(ImageView v, byte[] imgdata) {

        Bitmap thumbnailBm;

        if (null != imgdata && imgdata.length > 0)

            thumbnailBm = BitmapFactory.decodeByteArray(imgdata, 0, imgdata.length);

        else

            thumbnailBm = BitmapFactory.decodeResource(Utils.getAppContext().getResources(),

                                                       R.drawable.ic_unknown_image);


        // Recycle old Bitmap

        Drawable drawable = v.getDrawable();

        if (drawable instanceof BitmapDrawable) { // to make sure.

            BitmapDrawable bmd = (BitmapDrawable)drawable;

            Bitmap bitmap = bmd.getBitmap();

            bitmap.recycle();

        }


        // change to new one.

        v.setImageBitmap(thumbnailBm);

    }

현재 영문 번역이 진행중입니다.... (English translation is under construction.)


Big Picture

-----------


     0 +-----------------------+      Block group details

| |     /

  1024 +-----------------------+    / +-------------------------------+

| super block |   / | Super block copy |

  2048 +-----------------------+  / +-------------------------------+

| | / |    Group Descriptor Table |

+-----------------------+/ +-------------------------------+

| Block group 0 | | Block Bitmap |

+-----------------------+ +-------------------------------+

| Block group 1 | | Inode Bitmap |

+-----------------------+ +-------------------------------+

| ... | | Inode Record |

+-----------------------+ +-------------------------------+

| Block group n | +---+--| |-----+

+-----------------------+ |   | | Inode Table |     |

|   | | |<-+  |

|   | +-------------------------------+  |  |

|   | | .... |  |  |

|   | +-------------------------------+  |  |

|   +->| File Content |  |  |

| +-------------------------------+  |  |

| | ... |  |  |

| +-------------------------------+  |  |

+----->| Directory entries |--+  |

+-------------------------------+     |

| ... |     |

+-------------------------------+     |

| File Content |<----+

+-------------------------------+



ext4 raw block에 대해서 분석해 놓은 자료를 찾기 힘든관계로, kernel source를 분석한 내용을 바탕으로 정리해 본다.

(특히 directory쪽에 집중했다.)

==> 개인적인 분석에 근거한 것이므로 정확도는 보장할 수 없다는... -_-;

In my case, it's very difficult to find documents regarding ext4 raw block. So, I described some parts of them based on Linux kernel source codes. (especially, focusing on directory part).
==> This is just personal analysis. So, I can't guarantee correctness.. :-)


File system의 기본 정보는 super block에 저장되어 있다. ext4 super block에 대한 detail은 Linux kernel에서 "struct ext4_super_block"을 참고하면되고 여기서는 몇몇개만 언급해 보면

s_log_block_size    : file system block size (단위 KB). 보통 4. 즉 4KB

s_blocks_per_group  : 하나의 block group을 이루는 block 개수

s_inodes_per_group  :

s_inode_size        : ext4의 경우 256

s_inodes_count      : total inode count

Super block contains basic information of file system. For ext4 super block, struct ext4_super_block of Linux kernel source, is good reference for details. Let me describe some of them.

s_log_block_size    : size of file system block size (KB). usually 4 - 4KB

s_blocks_per_group  : number of blocks in a 'block group'

s_inodes_per_group  :

s_inode_size        : 256 in case of ext4

s_inodes_count      : total inode count



ext4의 경우 기존의 indirect block의 개념 + extent라는 개념을 사용하고 있다.

ext4 uses existing indirect-block + extent.

(details for indirect block : http://computer-forensics.sans.org/blog/2008/12/24/understanding-indirect-blocks-in-unix-file-systems/)


먼저 http://computer-forensics.sans.org/blog/2010/12/20/digital-forensics-understanding-ext4-part-1-extents <= 이곳의 내용을 읽어보도록 하자.

Please read above link before moving forward.


아래는 ext4.h 에서 발췌한 내용이다.

Followings comes from ext4.h


... (생략 skip) ...

/*

 * Constants relative to the data blocks

 */

#define EXT4_NDIR_BLOCKS 12

#define EXT4_IND_BLOCK EXT4_NDIR_BLOCKS

#define EXT4_DIND_BLOCK (EXT4_IND_BLOCK + 1)

#define EXT4_TIND_BLOCK (EXT4_DIND_BLOCK + 1)

#define EXT4_N_BLOCKS (EXT4_TIND_BLOCK + 1)


...(생략 skip)...

struct ext4_inode {

... (생략 skip) ...

__le32 i_blocks_lo; /* Blocks count */

... (생략 skip) ...

__le32 i_block[EXT4_N_BLOCKS]; /* pointers to blocks */

... (생략 skip) ...

}


ext4의 inode에서 실제 block을 나타내기 위해서 60 bytes (sizeof i_block)를 사용하고 있는다.

여기서 EXT4_N_BLOCKS == 15 이므로 15 * 4 = 60 이 block을 가리키는 포인터로 사용된다.

전통적인 direct/indirect block을 위해서라면, 15 index를 사용할 수 있다.

60 bytes (sizeof i_block) is used to represent real block at ext4-inode.

EXT4_N_BLOCKS == 15. Therefore 15 * 4 = 60 is used as pointers indicating real block.

For direct/indirect block, 15 indexes are available.


i_blocks_lo 의 경우 i_blocks_high와 함께 이 inode에 할당된 block의 count를 가지고 있다.

여기서 주의할 점의 여기서 사용되는 block의 size는 일반적인 super block에 저장된 그 block size(보통 4KB)가 아니라, 512 bytes라는 점이다. ext4/inode.c를 보면, "<< 9" 혹은 ">> 9"등의 표현을 종종 볼 수 있는데, 이것이 바로 512 bytes 단위를 표현하기 위한 것이다. 추가적으로 아래를 참고하자.
inode_add_bytes() : fs/stat.c ==> 512라는 숫자가 직접적으로 등장하기도 한다.
reserve_backup_gdb() : ext4/resize.c ==> "inode->i_blocks += reserved_gdb * sb->s_blocksize >> 9;"

i_blocks_lo and i_blocks_high has count of blocks allocated for the inode.

One thing to note, is block size here is NOT 4KB - usually mentioned at super block - but 512 bytes.

Expressions like "<< 9" and ">> 9" are often shown at ext4/inode.c, and this represents 512 bytes block size.

For additional information, please see

inode_add_bytes() : fs/stat.c ==> number '512' is shown up explicitly.

reserve_backup_gdb() : ext4/resize.c ==> "inode->i_blocks += reserved_gdb * sb->s_blocksize >> 9;"


한가지 더 짚고 넘어갈 내용은, i_blocks_lo가 meta data를 위한 block까지 포함하느냐? 의 문제이다.

ext4_ext_swap_inode_data : ext4/migrate.c를 보면, 아래의 코드라인이 보인다.

One more thing to consider is whether i_blocks_lo includes block for meta data or not?
Following lines of codes are observed at 
ext4_ext_swap_inode_data : ext4/migrate.c

/*

 * Update i_blocks with the new blocks that got

 * allocated while adding extents for extent index

 * blocks.

 *

 * While converting to extents we need not

 * update the orignal inode i_blocks for extent blocks

 * via quota APIs. The quota update happened via tmp_inode already.

 */

spin_lock(&inode->i_lock);

inode->i_blocks += tmp_inode->i_blocks;

즉 meta data를 위한 block역시 i_blocks_lo에 count된다는 뜻이다.

주의 : 그렇지만, meta data는 file의 real data가 아니므로 logical block index번호를 가지지는 않는다.
That is, i_blocks_lo includes blocks for meta data.
Note : But, meta data is NOT real data of file. So, it doesn't have logical block index.

inode의 extent의 경우 60 bytes를 아래와 같은 format으로 사용한다.

Extent for inode uses 60 bytes as following format.


|<- 12 bytes ->|<- 12 bytes ->|<- 12 bytes ->|<- 12 bytes ->|<- 12 bytes ->|

+--------------+--------------+--------------+--------------+--------------+

|    header    |    extent0   |    extent1   |    extent2   |    extent3   |

+--------------+--------------+--------------+--------------+--------------+


관련 code는 아래와 같다 (from ext4_extents.h)

Here are related code (from ext4_extents.h)

/*

 * ext4_inode has i_block array (60 bytes total).

 * The first 12 bytes store ext4_extent_header;

 * the remainder stores an array of ext4_extent.

 */


/*

 * This is the extent on-disk structure.

 * It's used at the bottom of the tree.

 */

struct ext4_extent {

__le32 ee_block; /* first logical block extent covers */

__le16 ee_len; /* number of blocks covered by extent */

__le16 ee_start_hi; /* high 16 bits of physical block */

__le32 ee_start_lo; /* low 32 bits of physical block */

};


/*

 * This is index on-disk structure.

 * It's used at all the levels except the bottom.

 */

struct ext4_extent_idx {

__le32 ei_block; /* index covers logical blocks from 'block' */

__le32 ei_leaf_lo; /* pointer to the physical block of the next *

* level. leaf or next index could be there */

__le16 ei_leaf_hi; /* high 16 bits of physical block */

__u16 ei_unused;

};


/*

 * Each block (leaves and indexes), even inode-stored has header.

 */

struct ext4_extent_header {

__le16 eh_magic; /* probably will support different formats */

__le16 eh_entries; /* number of valid entries */

__le16 eh_max; /* capacity of store in entries */

__le16 eh_depth; /* has tree real underlying blocks? */

__le32 eh_generation; /* generation of the tree */

};


하나의 extent는 연속된 physical block을 나타낸다.

ee_start_hi / ee_start_lo

start block index. ext4는 48bit block index를 사용하고 있다는 것을 상기하자.

sizeof(ee_start_hi) == 2, sizeof(ee_start_lo) == 4

ee_len

length of continous block. __le16 type이므로, 최대 32768개의 연속 block을 나타낼 수 있고, 4KB block size일 경우, 최대 4K * 32768 = 128M 를 표시할 수 있다.

eh_max

현재 block에서 가질 수 있는 max entry 수. entry 수가 이 값을 넘어가면 extent/index 를 split해야 한다.

size = (inode->i_sb->s_blocksize - sizeof(struct ext4_extent_header))

/ sizeof(struct ext4_extent);

--- or ---

size = sizeof(EXT4_I(inode)->i_data);

size -= sizeof(struct ext4_extent_header);

size /= sizeof(struct ext4_extent_idx);

--- or ---

size = sizeof(EXT4_I(inode)->i_data);

size -= sizeof(struct ext4_extent_header);

size /= sizeof(struct ext4_extent);


One extent represents continuous physical blocks.

ee_start_hi / ee_start_lo

start block index. remind that ext4 uses 48bit for block index.

sizeof(ee_start_hi) == 2, sizeof(ee_start_lo) == 4

ee_len

length of continuous block. It's type is __le16. Therefore it can represent maximum 32768 continuous blocks. And in case that block size is 4KB, it can represent maximum 4K * 32768 = 128MB.

eh_max

maximum number of entries in current block. extent/index should be splitted if number of entries exceeded this value.



따라서, 4KB block size를 가정하면, ext4_inode의 i_block array는

최악의 경우 : continuous한 block이 없을 경우 (즉 하나의 extent가 오직 한 block만 가리킨다. ee_len = 1)

4K * 4 = 16KB

최상의 겨우 : ee_len = 32768

128M * 4 = 512M

까지 나타낼 수 있다.
So, assuming 4KB block size, i_block array of ext4_inode can represent
    worst case : There is no continuous block. (That is, one extent can indicate only one block. ee_len = 1)
       
4K * 4 = 16KB
    best case : ee_len = 32768 

128M * 4 = 512M


이 한계를 극복하기 위해서 HTree방식의 extent tree가 사용된다.

아래는 ext4의 extent tree의 layout이다.(출처 : ols2007v2-pages-21-34.pdf from kernel.org)
To win over this limitation, HTree algorithm is used for extent tree.
Following picture is layout of ext4 extent tree.(The source : 
ols2007v2-pages-21-34.pdf from kernel.org)
(http://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&sqi=2&ved=0CEwQFjAA&url=http%3A%2F%2Fkernel.org%2Fdoc%2Fols%2F2007%2Fols2007v2-pages-21-34.pdf&ei=PLHWT_rUOayUiAew86mSAw&usg=AFQjCNF8Yzcg4Z5VRsEGWLMCEWNEJGx2sw&sig2=SDilL5_ETwCMRA9--mQ8lA)

이런 방식의 위의 한계를 극복하고 있다.

This is the way to recover above limitation.


inode의 extent는 60 bytes만 사용가능하지만, leaf nodes의 extent의 경우는 한 block전체를 사용하므로, 처음 12 bytes header를 제외하면, 그 뒤는 쭉~ "struct ext4_extent"의 array이다. array의 크기는 header->eh_entries에 저장되어 있다.

따라서, visit algorithm은
Only 60 bytes are available for inode extent. But, one entire block can be used for leaf node extent. Therefore, excluding first 12 bytes header, all are array of struct ext4_extent.
Length of array is stored at header->eh_entries.
Therefore, visiting algorithm is


struct ext4_extent_header *eh = (struct ext4_extent_header *)block;

struct ext4_extent         *e = (struct ext4_extent *)((char *)block + sizeof(*eh))

struct ext4_extent      *eend = e + eh->eh_entries;

while (e < eend) {

... do something ..

e++;

}


index node block의 경우 leaf node와 마찬가지로 12 bytes header가 처음에 오고, 그 뒤로 "struct ext4_extent_idx"의 array가 나타난다.

따라서 visit algorithm은
In case of index node block, like leaf node, 12 bytes header follows by array of struct ext4_extent_idx.
Therefore visiting algorithm is


struct ext4_extent_header *eh = (struct ext4_extent_header *)block;

struct ext4_extent_idx    *ei = (struct ext4_extent_idx *)((char *)block + sizeof(*eh))

struct ext4_extent     *eiend = ei + eh->eh_entries;

while (ei < eiend) {

... do something ..

/* "(ei->ei_leaf_hi << 32) | ei->ei_leaf_lo" 는 child block을 가리킨다. */
/* ei->ei_leaf_hi << 32 | ei->ei_leaf_lo means child block. */

ei++;

}


그렇다면, 현재 extent block이 leaf node인지 아니면 index node인지는 어떻게 판단할 수 있는가?
Then, how can we know a certain extent block is whether leaf node or index node?


if "header->eh_depth > 0" than "index node"

else if "header->eh_depth == 0" than "leaf node"


따라서, extent tree를 따라가는 algorithm은
So, algorithm for visiting extent tree is


struct ext4_extent_header *eh = (struct ext4_extent_header *)block;

if (eh->depth)

do_index_node(eh, block)

else

do_leaf_node(eh, block)


본격적으로 directory의 구조로 들어가 보자 (extent를 사용하는 경우를 가정하자.)

directory 역시 file이므로 위의 기본 구조를 따른다.

file의 block은 file의 content를 담고 있고, directory 역시 directory content를 담고 있는데, 다음과 같은 format을 따른다.
Now it's time for talking about directory structure (assuming the case using extent).
directory is also file. So, basic structure is same with the one we mentioned so far.
file block has file content, and directory also has directory content. Format is like below.


Structure across 2 blocks

-------------------------


|<------------------------- block 0 ------------------------->|<------------------ block 1--------------------->|

+----+-----------+--------+-----------------------------------+-----------+--------+----------------------------+

|... | struct DE | <name> |  padding                          | struct DE | <name> | ...                        |

+----+-----------+--------+-----------------------------------+-----------+--------+----------------------------+

|... | rec_len = 8 + name + 4 byte align + "len of padding"   |           same as normal (see above)            |

+----+-----------+--------+-----------------------------------+-------------------------------------------------+


< NOTE : 여기서 '8'은 'name' field를 제외한 sizeof(struct ext4_dir_entry_2)를 나타낸다.) >
< NOTE : 8 means sizeof(struct ext4_dir_entry_2) excluding name field. >


대부분의 경우 struct DE == struct ext4_dir_entry_2 를 나타낸다.
Most cases, struct DE means struct ext4_dir_entry_2.


#define EXT4_NAME_LEN 255


struct ext4_dir_entry_2 {

 __le32 inode;                 /* Inode number */

 __le16 rec_len;               /* Directory entry length */

 __u8 name_len;                /* Name length */

 __u8 file_type;

 char name[EXT4_NAME_LEN];     /* File name */

};


위에서 보듯이 ext4에서 file name의 최대 길이는 255이다.
As you can see above, maximum length of file name at ext4 is 255.

즉, directory block의 경우, struct ext4_dir_entry_2 구조가 계속해서 저장되는데, 현재 DE(directory entry)의 위치에서 'rec_len'만큼 더하면, 다음 DE로 갈 수 있다.
따라서, block의 마지막 DE의 rec_len는 항상 "현재 block의 마지막 / 다음 block의 시작" 을 가리킨다.
In case of directory block, ext4_dir_entry_2 structures are stored in sequence. So, we can move to next DE(directory entry) by adding rec_len to current DE.
The last DE of a certain block always means "end of current block / begin of next block".

한가지 주목할 사항은 'inode' 값에 대한 내용인데, '0'은 invalid한 directory entry를 뜻한다.
특히 delete algorithm 을 살펴보면 (For details : ext4_delete_entry() at ext4/namei.c from kernel)
One important thing to know is meaning of inode value. 0 inode value means invalid directory entry.
Especially, you can observe that below algorithm is used to delete file (for details : ext4_delete_entry() at ext4/namei.c from kernel)

< block 중간의 entry - DE1 - 를 삭제하는 경우 >
< Case : Entry in the middle of block - DE1 - is deleted >
+-----+-----+-----+-----+       +-----+-----------+-----+
| ... | DE0 | DE1 | ... |  ==>  | ... |    DE0    | ... |
+-----+-----+-----+-----+       +-----+-----------+-----+
--> 이 경우, DE0->rec_len += DE1->rec_len 가 된다.
--> In this case, DE0->rec_len += DE1->rec_len.

< block의 첫번재 entry - DE0 - 를 삭제하는 경우 >
< Case : The first entry of block - DE0 - is deleted >
+-----+-----+       +---------------+-----+
| DE0 | ... |  ==>  | DE0 <invalid> | ... |
+-----+-----+       +---------------+-----+
--> "DE0->inode = 0" 을 통해서 DE0를 invalid entry로 mark한다.
--> DE0 is marked as invalid by "DE0->inode = 0" statement.

정리해 보면, directory entry를 읽는 대략적인 알고리즘은 다음과 같다.
In summary, rough algorithm for reading directory is
(For details : ext_readdir() at ext4/dir.c from kernel)

struct ext4_dir_entry_2 *de = (struct ext4_dir_entry_2 *)block;
struct ext4_dir_entry_2 *deend = block + block_size; /* block size is usually 4KB */
while (de < deend) {
if (de->inode) { /* check that this is valid or not */
... do something ...
}
de = (struct ext4_dir_entry_2 *)((char*)de + de->rec_len);
}


.... 음 .... 일단 여기까지... 나중에 좀더 정리해야겠당....
... to be continued...

Kernel build system defines KBUILD_MODNAME automatically from each Makefile.
You can easily find below string from build command of each files of modules.

-D"KBUILD_MODNAME=KBUILD_STR(xxxxx)"
('xxxxx' is given module name)

This name xxxxx comes from Makefile.
Below is simple example.

[ in Makefile ]
xxxxx-y := a.o b.o c.o d.o

In above case, KBUILD_MODNAME of a.cb.cc.c and d.c becomes xxxxx.
This can be easily confirmed by checking command line of each objects - a.ob.oc.o and d.o.

In Linux, usually, kernel crashes with so-called Oops report.
This Oops report includes lots of useful information for debugging.

Therefore, I have seen lots of developer suffering from analyzing register, memory and stack dump in the report.
To analyze dumped information, memory information should be matched with source code.
But, this is not easy process.
So, I want to introduce the way to do this easily.

Main concept is, we can make tool to parse Oops report and pass these to debugging tool.
Here is introduction of my case.
I uses TRACE32 software - ARM simulator for debugging tool.
What I did is, implementing simple Perl script that parses Oops report and make cmm file that set register and memory information given by the report.
For example, auto generated cmm file is like this.

R.S cpsr 0x20000013
R.S r0 0x0
R.S r1 0x0
...
D.S 0xc035a248 %long 0xe3a02000
D.S 0xc035a24c %long 0xe3a03020
...

It's time to use TRACE32 PowerView for ARM to analyze the report.
Launching t32marm with simulator mode -> Loading issued 'vmlinux' -> Runnig auto-generated cmm script
Finally, I can see stack frame with local variables and interpreted memory information by virtue of T32.

I'm sorry not to share parsing tool - Perl script - due to ... as you know, something like legal issue... (I hate this.)
I hope this post is helpful for others...

+ Recent posts