4 * Copyright (c) 2012 - 2013 Samsung Electronics Co., Ltd.
6 * Licensed under the Apache License, Version 2.0 (the License);
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
23 #include <sys/mount.h>
24 #include <sys/statvfs.h>
29 #include <sys/statfs.h>
36 #include "core/device-handler.h"
37 #include "core/device-notifier.h"
38 #include "core/common.h"
39 #include "core/devices.h"
40 #include "mmc-handler.h"
42 #include "core/edbus-handler.h"
43 #include "core/list.h"
44 #include "core/config-parser.h"
46 #define VCONFKEY_INTERNAL_PRIVATE_MMC_ID "db/private/sysman/mmc_device_id"
48 #define MMC_PARENT_PATH "/opt/storage"
49 #define MMC_DEV "/dev/mmcblk"
51 #define SMACKFS_MAGIC 0x43415d53
52 #define SMACKFS_MNT "/smack"
55 #define ST_RDONLY 0x0001
58 #define MMC_32GB_SIZE 61315072
59 #define FORMAT_RETRY 3
60 #define UNMOUNT_RETRY 5
62 #define ODE_MOUNT_STATE 1
64 enum unmount_operation {
76 static void *mount_start(void *arg);
77 static void *unmount_start(void *arg);
78 static void *format_start(void *arg);
80 static const struct mmc_thread_func {
81 enum mmc_operation type;
82 void *(*func) (void*);
83 } mmc_func[MMC_END] = {
84 [MMC_MOUNT] = {.type = MMC_MOUNT, .func = mount_start},
85 [MMC_UNMOUNT] = {.type = MMC_UNMOUNT, .func = unmount_start},
86 [MMC_FORMAT] = {.type = MMC_FORMAT, .func = format_start},
100 static dd_list *fs_head;
101 static char *mmc_curpath;
102 static bool smack = false;
103 static bool mmc_disabled = false;
105 int __WEAK__ app2ext_unmount(void);
107 static void __CONSTRUCTOR__ smack_check(void)
113 ret = statfs(SMACKFS_MNT, &sfs);
114 } while (ret < 0 && errno == EINTR);
116 if (ret == 0 && sfs.f_type == SMACKFS_MAGIC)
118 _I("smackfs check %d", smack);
121 void add_fs(const struct mmc_fs_ops *fs)
123 DD_LIST_APPEND(fs_head, (void*)fs);
126 void remove_fs(const struct mmc_fs_ops *fs)
128 DD_LIST_REMOVE(fs_head, (void*)fs);
131 const struct mmc_fs_ops *find_fs(enum mmc_fs_type type)
133 struct mmc_fs_ops *fs;
136 DD_LIST_FOREACH(fs_head, elem, fs) {
137 if (fs->type == type)
143 bool mmc_check_mounted(const char *mount_point)
145 struct stat parent_stat, mount_stat;
146 char parent_path[PATH_MAX];
148 snprintf(parent_path, sizeof(parent_path), "%s", MMC_PARENT_PATH);
150 if (stat(mount_point, &mount_stat) != 0 || stat(parent_path, &parent_stat) != 0)
153 if (mount_stat.st_dev == parent_stat.st_dev)
159 static void launch_syspopup(char *str)
161 struct popup_data *params;
162 static const struct device_ops *apps = NULL;
165 apps = find_device("apps");
169 params = malloc(sizeof(struct popup_data));
170 if (params == NULL) {
174 params->name = MMC_POPUP_NAME;
175 params->key = POPUP_KEY_CONTENT;
176 params->value = strdup(str);
177 apps->init((void *)params);
181 static int get_partition(const char *devpath, char *subpath)
186 for (i = 1; i < 5; ++i) {
187 snprintf(path, sizeof(path), "%sp%d", devpath, i);
188 if (!access(path, R_OK)) {
189 strncpy(subpath, path, strlen(path));
196 static int create_partition(const char *devpath)
201 snprintf(data, sizeof(data), "\"n\\n\\n\\n\\n\\nw\" | fdisk %s", devpath);
203 r = launch_evenif_exist("/usr/bin/printf", data);
204 if (WIFSIGNALED(r) && (WTERMSIG(r) == SIGINT || WTERMSIG(r) == SIGQUIT || WEXITSTATUS(r)))
210 static int mmc_check_and_unmount(const char *path)
212 int ret = 0, retry = 0;
213 while (mount_check(path)) {
217 if (retry > UNMOUNT_RETRY)
224 int get_block_number(void)
233 char *pre_mmc_device_id = NULL;
234 int mmc_dev_changed = 0;
236 if ((dp = opendir("/sys/block")) == NULL) {
237 _E("Can not open directory..");
241 r = chdir("/sys/block");
243 _E("Fail to change the directory..");
248 while ((dir = readdir(dp)) != NULL) {
249 memset(&stat, 0, sizeof(struct stat));
250 if(lstat(dir->d_name, &stat) < 0) {continue;}
251 if (S_ISDIR(stat.st_mode) || S_ISLNK(stat.st_mode)) {
252 if (strncmp(".", dir->d_name, 1) == 0
253 || strncmp("..", dir->d_name, 2) == 0)
255 if (strncmp("mmcblk", dir->d_name, 6) == 0) {
256 snprintf(buf, 255, "/sys/block/%s/device/type",
259 fd = open(buf, O_RDONLY);
263 r = read(fd, buf, 10);
264 if ((r >= 0) && (r < 10))
267 _E("%s read error: %s", buf,
270 if (strncmp("SD", buf, 2) == 0) {
271 char *str_mmcblk_num = strndup((dir->d_name) + 6, 1);
272 if (str_mmcblk_num == NULL) {
273 _E("Memory Allocation Failed");
278 atoi(str_mmcblk_num);
280 free(str_mmcblk_num);
282 _D("%d", mmcblk_num);
284 snprintf(buf, 255, "/sys/block/%s/device/cid", dir->d_name);
286 fd = open(buf, O_RDONLY);
288 _E("%s open error", buf, strerror(errno));
291 r = read(fd, buf, 255);
292 if ((r >=0) && (r < 255)) {
295 _E("%s read error: %s", buf,strerror(errno));
298 pre_mmc_device_id = vconf_get_str(VCONFKEY_INTERNAL_PRIVATE_MMC_ID);
299 if (pre_mmc_device_id) {
300 if (strcmp(pre_mmc_device_id, "") == 0) {
301 vconf_set_str(VCONFKEY_INTERNAL_PRIVATE_MMC_ID, buf);
302 } else if (strncmp(pre_mmc_device_id,buf,33) == 0) {
303 if ( vconf_get_int(VCONFKEY_SYSMAN_MMC_DEVICE_CHANGED,&mmc_dev_changed) == 0
304 && mmc_dev_changed != VCONFKEY_SYSMAN_MMC_NOT_CHANGED) {
305 vconf_set_int(VCONFKEY_SYSMAN_MMC_DEVICE_CHANGED, VCONFKEY_SYSMAN_MMC_NOT_CHANGED);
307 } else if (strncmp(pre_mmc_device_id,buf,32) != 0) {
308 vconf_set_str(VCONFKEY_INTERNAL_PRIVATE_MMC_ID, buf);
309 vconf_set_int(VCONFKEY_SYSMAN_MMC_DEVICE_CHANGED, VCONFKEY_SYSMAN_MMC_CHANGED);
311 free(pre_mmc_device_id);
313 _E("failed to get pre_mmc_device_id");
322 _E("failed to find mmc block number");
326 static int find_mmc_node(char devpath[])
330 num = get_block_number();
334 snprintf(devpath, NAME_MAX, "%s%d", MMC_DEV, num);
338 static int get_mmc_size(const char *devpath)
341 unsigned long long ullbytes;
344 fd = open(devpath, O_RDONLY);
350 r = ioctl(fd, BLKGETSIZE64, &ullbytes);
354 _E("ioctl BLKGETSIZE64 error");
358 nbytes = ullbytes/512;
359 _D("block size(64) : %d", nbytes);
363 static int rw_mount(const char *szPath)
365 struct statvfs mount_stat;
366 if (!statvfs(szPath, &mount_stat)) {
367 if ((mount_stat.f_flag & ST_RDONLY) == ST_RDONLY)
373 static int mmc_mount(const char *devpath, const char *mount_point)
375 struct mmc_fs_ops *fs;
377 char path[NAME_MAX] = {0,};
380 /* mmc_disabled set by start/stop func. */
387 /* check partition */
388 r = get_partition(devpath, path);
392 DD_LIST_FOREACH(fs_head, elem, fs) {
393 if (fs->match(devpath))
400 _D("devpath : %s", devpath);
401 r = fs->check(devpath);
403 _E("failt to check devpath : %s", devpath);
405 r = fs->mount(smack, devpath, mount_point);
409 r = rw_mount(mount_point);
416 static void *mount_start(void *arg)
418 struct mmc_data *data = (struct mmc_data*)arg;
422 devpath = data->devpath;
426 /* clear previous filesystem */
427 mmc_check_and_unmount(MMC_MOUNT_POINT);
429 /* check mount point */
430 if (access(MMC_MOUNT_POINT, R_OK) != 0) {
431 if (mkdir(MMC_MOUNT_POINT, 0755) < 0) {
437 /* mount operation */
438 r = mmc_mount(devpath, MMC_MOUNT_POINT);
440 launch_syspopup("mountrdonly");
444 mmc_set_config(MAX_RATIO);
448 vconf_set_int(VCONFKEY_SYSMAN_MMC_STATUS, VCONFKEY_SYSMAN_MMC_MOUNTED);
449 vconf_set_int(VCONFKEY_SYSMAN_MMC_MOUNT, VCONFKEY_SYSMAN_MMC_MOUNT_COMPLETED);
453 launch_syspopup("mounterr");
454 vconf_set_int(VCONFKEY_SYSMAN_MMC_MOUNT, VCONFKEY_SYSMAN_MMC_MOUNT_FAILED);
455 vconf_set_int(VCONFKEY_SYSMAN_MMC_STATUS, VCONFKEY_SYSMAN_MMC_INSERTED_NOT_MOUNTED);
459 _E("failed to mount device : %s", strerror(-r));
463 static int mmc_unmount(int option, const char *mount_point)
468 /* try to unmount app2ext */
469 r = app2ext_unmount();
471 _I("Faild to unmount app2ext : %s", strerror(-r));
472 r = mmc_check_and_unmount(mount_point);
475 if (option == UNMOUNT_NORMAL) {
476 _I("Failed to unmount with normal option : %s", strerror(-r));
480 _I("Execute force unmount!");
481 /* Force Unmount Scenario */
485 /* At first, notify to other app who already access sdcard */
486 _I("Notify to other app who already access sdcard");
487 vconf_set_int(VCONFKEY_SYSMAN_MMC_STATUS, VCONFKEY_SYSMAN_MMC_INSERTED_NOT_MOUNTED);
490 /* Second, kill app with SIGTERM */
491 _I("Kill app with SIGTERM");
492 terminate_process(MMC_MOUNT_POINT, false);
495 /* Last time, kill app with SIGKILL */
496 _I("Kill app with SIGKILL");
497 terminate_process(MMC_MOUNT_POINT, true);
500 if (umount2(mount_point, MNT_DETACH) != 0) {
501 _I("Failed to unmount with lazy option : %s", strerror(errno));
507 /* it takes some seconds til other app completely clean up */
510 /* try to unmount app2ext */
511 r = app2ext_unmount();
513 _I("Faild to unmount app2ext : %s", strerror(-r));
515 r = mmc_check_and_unmount(mount_point);
523 static void *unmount_start(void *arg)
525 struct mmc_data *data = (struct mmc_data*)arg;
528 option = data->option;
530 assert(option == UNMOUNT_NORMAL || option == UNMOUNT_FORCE);
532 r = mmc_unmount(option, MMC_MOUNT_POINT);
537 vconf_set_int(VCONFKEY_SYSMAN_MMC_STATUS, VCONFKEY_SYSMAN_MMC_INSERTED_NOT_MOUNTED);
542 _E("Failed to unmount device : %s", strerror(-r));
543 vconf_set_int(VCONFKEY_SYSMAN_MMC_STATUS, VCONFKEY_SYSMAN_MMC_MOUNTED);
547 static int format(const char *devpath)
549 const struct mmc_fs_ops *fs = NULL;
551 char path[NAME_MAX] = {0,};
557 /* check partition */
558 r = get_partition(devpath, path);
560 /* if there is partition, find partition file system */
561 DD_LIST_FOREACH(fs_head, elem, fs) {
566 /* if there isn't partition, create partition */
567 create_partition(devpath);
568 r = get_partition(devpath, path);
570 memcpy(path, devpath, strlen(devpath));
573 _I("format partition : %s", path);
576 /* find root file system */
577 DD_LIST_FOREACH(fs_head, elem, fs) {
578 if (fs->match(devpath))
584 /* cannot find root and partition file system,
585 find suitable file system */
586 size = get_mmc_size(path);
587 if (size <= MMC_32GB_SIZE)
588 fs = find_fs(FS_TYPE_VFAT);
590 fs = find_fs(FS_TYPE_EXFAT);
596 for (retry = FORMAT_RETRY; retry > 0; --retry) {
598 _D("format path : %s", path);
599 r = fs->format(path);
606 static void *format_start(void *arg)
608 struct mmc_data *data = (struct mmc_data*)arg;
610 int option, r, key = VCONFKEY_SYSMAN_MMC_MOUNTED;
611 bool format_ret = true;
613 option = data->option;
614 devpath = data->devpath;
617 assert(option == UNMOUNT_NORMAL || option == UNMOUNT_FORCE);
619 _I("Format Start (option:%d)", option);
620 r = mmc_unmount(option, MMC_MOUNT_POINT);
624 vconf_set_int(VCONFKEY_SYSMAN_MMC_FORMAT_PROGRESS, VCONFKEY_SYSMAN_MMC_FORMAT_PROGRESS_NOW);
626 vconf_set_int(VCONFKEY_SYSMAN_MMC_FORMAT_PROGRESS, VCONFKEY_SYSMAN_MMC_FORMAT_PROGRESS_NONE);
633 _I("Format Successful");
634 vconf_set_int(VCONFKEY_SYSMAN_MMC_FORMAT, VCONFKEY_SYSMAN_MMC_FORMAT_COMPLETED);
641 _E("Format Failed : %s", strerror(-r));
642 vconf_set_int(VCONFKEY_SYSMAN_MMC_FORMAT, VCONFKEY_SYSMAN_MMC_FORMAT_FAILED);
646 static int mmc_make_thread(int type, int option, const char *devpath)
649 struct mmc_data *pdata;
652 if (type < 0 || type >= MMC_END)
655 pdata = malloc(sizeof(struct mmc_data));
662 pdata->option = option;
664 pdata->devpath = strdup(devpath);
665 r = pthread_create(&th, NULL, mmc_func[type].func, pdata);
667 _E("pthread create failed");
668 free(pdata->devpath);
677 static int mmc_inserted(const char *devpath)
680 _I("MMC inserted : %s", devpath);
681 mmc_curpath = strdup(devpath);
682 r = mmc_make_thread(MMC_MOUNT, -1, devpath);
688 static int mmc_removed(void)
691 /* first, try to unmount app2ext */
694 mmc_check_and_unmount((const char *)MMC_MOUNT_POINT);
695 vconf_set_int(VCONFKEY_SYSMAN_MMC_STATUS, VCONFKEY_SYSMAN_MMC_REMOVED);
701 static int mmc_changed_cb(void *data)
703 char *devpath = (char*)data;
705 /* if MMC is inserted */
707 return mmc_inserted(devpath);
709 return mmc_removed();
712 static int mmc_booting_done(void* data)
714 char devpath[NAME_MAX] = {0,};
717 /* check mmc exists */
718 r = find_mmc_node(devpath);
723 return mmc_inserted(devpath);
726 static DBusMessage *edbus_request_secure_mount(E_DBus_Object *obj, DBusMessage *msg)
728 DBusMessageIter iter;
733 ret = dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &path,
745 /* check mount point */
746 if (access(path, R_OK) != 0) {
747 if (mkdir(path, 0755) < 0) {
753 _D("mount path : %s", path);
754 ret = mmc_mount(mmc_curpath, path);
757 reply = dbus_message_new_method_return(msg);
758 dbus_message_iter_init_append(reply, &iter);
759 dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
763 static DBusMessage *edbus_request_secure_unmount(E_DBus_Object *obj, DBusMessage *msg)
765 DBusMessageIter iter;
770 ret = dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &path,
777 _D("unmount path : %s", path);
778 ret = mmc_unmount(UNMOUNT_NORMAL, path);
781 reply = dbus_message_new_method_return(msg);
782 dbus_message_iter_init_append(reply, &iter);
783 dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
787 static DBusMessage *edbus_request_mount(E_DBus_Object *obj, DBusMessage *msg)
789 DBusMessageIter iter;
791 struct mmc_data *pdata;
799 pdata = malloc(sizeof(struct mmc_data));
806 pdata->devpath = strdup(mmc_curpath);
807 if (!pdata->devpath) {
813 ret = (int)mount_start(pdata);
816 reply = dbus_message_new_method_return(msg);
817 dbus_message_iter_init_append(reply, &iter);
818 dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
822 static DBusMessage *edbus_request_unmount(E_DBus_Object *obj, DBusMessage *msg)
824 DBusMessageIter iter;
827 char params[NAME_MAX];
828 struct mmc_data *pdata;
830 ret = dbus_message_get_args(msg, NULL,
831 DBUS_TYPE_INT32, &opt, DBUS_TYPE_INVALID);
833 _I("there is no message");
838 pdata = malloc(sizeof(struct mmc_data));
846 ret = (int)unmount_start(pdata);
849 reply = dbus_message_new_method_return(msg);
850 dbus_message_iter_init_append(reply, &iter);
851 dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
855 static DBusMessage *edbus_request_format(E_DBus_Object *obj, DBusMessage *msg)
857 DBusMessageIter iter;
860 struct mmc_data *pdata;
862 ret = dbus_message_get_args(msg, NULL,
863 DBUS_TYPE_INT32, &opt, DBUS_TYPE_INVALID);
865 _I("there is no message");
875 pdata = malloc(sizeof(struct mmc_data));
883 pdata->devpath = strdup(mmc_curpath);
884 if (!pdata->devpath) {
890 ret = (int)format_start(pdata);
893 reply = dbus_message_new_method_return(msg);
894 dbus_message_iter_init_append(reply, &iter);
895 dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
899 int get_mmc_devpath(char devpath[])
905 snprintf(devpath, NAME_MAX, "%s", mmc_curpath);
909 static const struct edbus_method edbus_methods[] = {
910 { "RequestSecureMount", "s", "i", edbus_request_secure_mount },
911 { "RequestSecureUnmount", "s", "i", edbus_request_secure_unmount },
912 { "RequestMount", NULL, "i", edbus_request_mount },
913 { "RequestUnmount", "i", "i", edbus_request_unmount },
914 { "RequestFormat", "i", "i", edbus_request_format },
917 static int mmc_poweroff(void *data)
923 static void mmc_init(void *data)
928 ret = register_edbus_method(DEVICED_PATH_MMC, edbus_methods, ARRAY_SIZE(edbus_methods));
930 _E("fail to init edbus method(%d)", ret);
932 /* register mmc uevent control routine */
933 ret = mmc_uevent_start();
935 _E("fail to mmc uevent start");
937 /* register notifier if mmc exist or not */
938 register_notifier(DEVICE_NOTIFIER_POWEROFF, mmc_poweroff);
939 register_notifier(DEVICE_NOTIFIER_BOOTING_DONE, mmc_booting_done);
940 register_notifier(DEVICE_NOTIFIER_MMC, mmc_changed_cb);
943 static void mmc_exit(void *data)
945 /* unregister notifier */
946 unregister_notifier(DEVICE_NOTIFIER_POWEROFF, mmc_poweroff);
947 unregister_notifier(DEVICE_NOTIFIER_MMC, mmc_changed_cb);
948 unregister_notifier(DEVICE_NOTIFIER_BOOTING_DONE, mmc_booting_done);
950 /* unregister mmc uevent control routine */
954 static int mmc_start(void)
956 mmc_disabled = false;
961 static int mmc_stop(void)
964 vconf_set_int(VCONFKEY_SYSMAN_MMC_STATUS, VCONFKEY_SYSMAN_MMC_REMOVED);
969 const struct device_ops mmc_device_ops = {
970 .priority = DEVICE_PRIORITY_NORMAL,
978 DEVICE_OPS_REGISTER(&mmc_device_ops)