info = req->object->ondemand; /* fail OPEN request if copen format is invalid */
ret = kstrtol(psize, 0, &size); if (ret) {
req->error = ret; goto out;
}
/* fail OPEN request if daemon reports an error */ if (size < 0) { if (!IS_ERR_VALUE(size)) {
req->error = -EINVAL;
ret = -EINVAL;
} else {
req->error = size;
ret = 0;
} goto out;
}
spin_lock(&info->lock); /* * The anonymous fd was closed before copen ? Fail the request. * * t1 | t2 * --------------------------------------------------------- * cachefiles_ondemand_copen * req = xa_erase(&cache->reqs, id) * // Anon fd is maliciously closed. * cachefiles_ondemand_fd_release * xa_lock(&cache->reqs) * cachefiles_ondemand_set_object_close(object) * xa_unlock(&cache->reqs) * cachefiles_ondemand_set_object_open * // No one will ever close it again. * cachefiles_ondemand_daemon_read * cachefiles_ondemand_select_req * * Get a read req but its fd is already closed. The daemon can't * issue a cread ioctl with an closed fd, then hung.
*/ if (info->ondemand_id == CACHEFILES_ONDEMAND_ID_CLOSED) {
spin_unlock(&info->lock);
req->error = -EBADFD; goto out;
}
cookie = req->object->cookie;
cookie->object_size = size; if (size)
clear_bit(FSCACHE_COOKIE_NO_DATA_TO_READ, &cookie->flags); else
set_bit(FSCACHE_COOKIE_NO_DATA_TO_READ, &cookie->flags);
trace_cachefiles_ondemand_copen(req->object, id, size);
out:
spin_lock(&info->lock); /* Need to set object close to avoid reopen status continuing */ if (info->ondemand_id == CACHEFILES_ONDEMAND_ID_CLOSED)
cachefiles_ondemand_set_object_close(req->object);
spin_unlock(&info->lock);
complete(&req->done); return ret;
}
int cachefiles_ondemand_restore(struct cachefiles_cache *cache, char *args)
{ struct cachefiles_req *req;
XA_STATE(xas, &cache->reqs, 0);
if (!test_bit(CACHEFILES_ONDEMAND_MODE, &cache->flags)) return -EOPNOTSUPP;
/* * Reset the requests to CACHEFILES_REQ_NEW state, so that the * requests have been processed halfway before the crash of the * user daemon could be reprocessed after the recovery.
*/
xas_lock(&xas);
xas_for_each(&xas, req, ULONG_MAX)
xas_set_mark(&xas, CACHEFILES_REQ_NEW);
xas_unlock(&xas);
/* * If there are any inflight or subsequent READ requests on the * closed object, reopen it. * Skip read requests whose related object is reopening.
*/ staticstruct cachefiles_req *cachefiles_ondemand_select_req(struct xa_state *xas, unsignedlong xa_max)
{ struct cachefiles_req *req; struct cachefiles_object *object; struct cachefiles_ondemand_info *info;
xas_for_each_marked(xas, req, xa_max, CACHEFILES_REQ_NEW) { if (req->msg.opcode != CACHEFILES_OP_READ) return req;
object = req->object;
info = object->ondemand; if (cachefiles_ondemand_object_is_close(object)) {
cachefiles_ondemand_set_object_reopening(object);
queue_work(fscache_wq, &info->ondemand_work); continue;
} if (cachefiles_ondemand_object_is_reopening(object)) continue; return req;
} return NULL;
}
staticinlinebool cachefiles_ondemand_finish_req(struct cachefiles_req *req, struct xa_state *xas, int err)
{ if (unlikely(!xas || !req)) returnfalse;
if (xa_cmpxchg(xas->xa, xas->xa_index, req, NULL, 0) != req) returnfalse;
xa_lock(&cache->reqs); /* * Cyclically search for a request that has not ever been processed, * to prevent requests from being processed repeatedly, and make * request distribution fair.
*/
req = cachefiles_ondemand_select_req(&xas, ULONG_MAX); if (!req && cache->req_id_next > 0) {
xas_set(&xas, 0);
req = cachefiles_ondemand_select_req(&xas, cache->req_id_next - 1);
} if (!req) {
xa_unlock(&cache->reqs); return 0;
}
msg = &req->msg;
n = msg->len;
if (n > buflen) {
xa_unlock(&cache->reqs); return -EMSGSIZE;
}
do { /* * Stop enqueuing the request when daemon is dying. The * following two operations need to be atomic as a whole. * 1) check cache state, and * 2) enqueue request if cache is alive. * Otherwise the request may be enqueued after xarray has been * flushed, leaving the orphan request never being completed. * * CPU 1 CPU 2 * ===== ===== * test CACHEFILES_DEAD bit * set CACHEFILES_DEAD bit * flush requests in the xarray * enqueue the request
*/
xas_lock(&xas);
if (test_bit(CACHEFILES_DEAD, &cache->flags) ||
cachefiles_ondemand_object_is_dropping(object)) {
xas_unlock(&xas);
ret = -EIO; goto out;
}
/* coupled with the barrier in cachefiles_flush_reqs() */
smp_mb();
if (opcode == CACHEFILES_OP_CLOSE &&
!cachefiles_ondemand_object_is_open(object)) {
WARN_ON_ONCE(object->ondemand->ondemand_id == 0);
xas_unlock(&xas);
ret = -EIO; goto out;
}
/* * Cyclically find a free xas to avoid msg_id reuse that would * cause the daemon to successfully copen a stale msg_id.
*/
xas.xa_index = cache->msg_id_next;
xas_find_marked(&xas, UINT_MAX, XA_FREE_MARK); if (xas.xa_node == XAS_RESTART) {
xas.xa_index = 0;
xas_find_marked(&xas, cache->msg_id_next - 1, XA_FREE_MARK);
} if (xas.xa_node == XAS_RESTART)
xas_set_err(&xas, -EBUSY);
xas_store(&xas, req); if (xas_valid(&xas)) {
cache->msg_id_next = xas.xa_index + 1;
xas_clear_mark(&xas, XA_FREE_MARK);
xas_set_mark(&xas, CACHEFILES_REQ_NEW);
}
xas_unlock(&xas);
} while (xas_nomem(&xas, GFP_KERNEL));
ret = xas_error(&xas); if (ret) goto out;
wake_up_all(&cache->daemon_pollwq);
wait:
ret = wait_for_completion_killable(&req->done); if (!ret) {
ret = req->error;
} else {
ret = -EINTR; if (!cachefiles_ondemand_finish_req(req, &xas, ret)) { /* Someone will complete it soon. */
cpu_relax(); goto wait;
}
}
cachefiles_req_put(req); return ret;
out: /* Reset the object to close state in error handling path. * If error occurs after creating the anonymous fd, * cachefiles_ondemand_fd_release() will set object to close.
*/ if (opcode == CACHEFILES_OP_OPEN &&
!cachefiles_ondemand_object_is_dropping(object))
cachefiles_ondemand_set_object_close(object);
kfree(req); return ret;
}
/* * Volume key is a NUL-terminated string. key[0] stores strlen() of the * string, followed by the content of the string (excluding '\0').
*/
volume_key_size = volume->key[0] + 1;
volume_key = volume->key + 1;
/* Cookie key is binary data, which is netfs specific. */
cookie_key_size = cookie->key_len;
cookie_key = fscache_get_key(cookie);
if (!(object->cookie->advice & FSCACHE_ADV_WANT_CACHE_SIZE)) {
pr_err("WANT_CACHE_SIZE is needed for on-demand mode\n"); return -EINVAL;
}
/* * CacheFiles will firstly check the cache file under the root cache * directory. If the coherency check failed, it will fallback to * creating a new tmpfile as the cache file. Reuse the previously * allocated object ID if any.
*/ if (cachefiles_ondemand_object_is_open(object)) return 0;
/* Cancel all requests for the object that is being dropped. */
cache = object->volume->cache;
xa_lock(&cache->reqs);
cachefiles_ondemand_set_object_dropping(object);
xa_for_each(&cache->reqs, index, req) { if (req->object == object) {
req->error = -EIO;
complete(&req->done);
__xa_erase(&cache->reqs, index);
}
}
xa_unlock(&cache->reqs);
/* Wait for ondemand_object_worker() to finish to avoid UAF. */
cancel_work_sync(&object->ondemand->ondemand_work);
}
int cachefiles_ondemand_init_obj_info(struct cachefiles_object *object, struct cachefiles_volume *volume)
{ if (!cachefiles_in_ondemand_mode(volume->cache)) return 0;
object->ondemand = kzalloc(sizeof(struct cachefiles_ondemand_info),
GFP_KERNEL); if (!object->ondemand) return -ENOMEM;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.