/* * fs/dcache.c * * Complete reimplementation * (C) 1997 Thomas Schoebel-Theuer, * with heavy changes by Linus Torvalds */ /* * Notes on the allocation strategy: * * The dcache is a master of the icache - whenever a dcache entry * exists, the inode will always exist. "iput()" is done either when * the dcache entry is deleted or garbage collected. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "internal.h" int sysctl_vfs_cache_pressure __read_mostly = 100; EXPORT_SYMBOL_GPL(sysctl_vfs_cache_pressure); __cacheline_aligned_in_smp DEFINE_SPINLOCK(dcache_lock); __cacheline_aligned_in_smp DEFINE_SEQLOCK(rename_lock); EXPORT_SYMBOL(dcache_lock); static struct kmem_cache *dentry_cache __read_mostly; #define DNAME_INLINE_LEN (sizeof(struct dentry)-offsetof(struct dentry,d_iname)) /* * This is the single most critical data structure when it comes * to the dcache: the hashtable for lookups. Somebody should try * to make this good - I've just made it work. * * This hash-function tries to avoid losing too many bits of hash * information, yet avoid using a prime hash-size or similar. */ #define D_HASHBITS d_hash_shift #define D_HASHMASK d_hash_mask static unsigned int d_hash_mask __read_mostly; static unsigned int d_hash_shift __read_mostly; static struct hlist_head *dentry_hashtable __read_mostly; /* Statistics gathering. */ struct dentry_stat_t dentry_stat = { .age_limit = 45, }; static void __d_free(struct dentry *dentry) { WARN_ON(!list_empty(&dentry->d_alias)); if (dname_external(dentry)) kfree(dentry->d_name.name); kmem_cache_free(dentry_cache, dentry); } static void d_callback(struct rcu_head *head) { struct dentry * dentry = container_of(head, struct dentry, d_u.d_rcu); __d_free(dentry); } /* * no dcache_lock, please. The caller must decrement dentry_stat.nr_dentry * inside dcache_lock. */ static void d_free(struct dentry *dentry) { if (dentry->d_op && dentry->d_op->d_release) dentry->d_op->d_release(dentry); /* if dentry was never inserted into hash, immediate free is OK */ if (hlist_unhashed(&dentry->d_hash)) __d_free(dentry); else call_rcu(&dentry->d_u.d_rcu, d_callback); } /* * Release the dentry's inode, using the filesystem * d_iput() operation if defined. */ static void dentry_iput(struct dentry * dentry) __releases(dentry->d_lock) __releases(dcache_lock) { struct inode *inode = dentry->d_inode; if (inode) { dentry->d_inode = NULL; list_del_init(&dentry->d_alias); spin_unlock(&dentry->d_lock); spin_unlock(&dcache_lock); if (!inode->i_nlink) fsnotify_inoderemove(inode); if (dentry->d_op && dentry->d_op->d_iput) dentry->d_op->d_iput(dentry, inode); else iput(inode); } else { spin_unlock(&dentry->d_lock); spin_unlock(&dcache_lock); } } /* * dentry_lru_(add|add_tail|del|del_init) must be called with dcache_lock held. */ static void dentry_lru_add(struct dentry *dentry) { list_add(&dentry->d_lru, &dentry->d_sb->s_dentry_lru); dentry->d_sb->s_nr_dentry_unused++; dentry_stat.nr_unused++; } static void dentry_lru_add_tail(struct dentry *dentry) { list_add_tail(&dentry->d_lru, &dentry->d_sb->s_dentry_lru); dentry->d_sb->s_nr_dentry_unused++; dentry_stat.nr_unused++; } static void dentry_lru_del(struct dentry *dentry) { if (!list_empty(&dentry->d_lru)) { list_del(&dentry->d_lru); dentry->d_sb->s_nr_dentry_unused--; dentry_stat.nr_unused--; } } static void dentry_lru_del_init(struct dentry *dentry) { if (likely(!list_empty(&dentry->d_lru))) { list_del_init(&dentry->d_lru); dentry->d_sb->s_nr_dentry_unused--; dentry_stat.nr_unused--; } } /** * d_kill - kill dentry and return parent * @dentry: dentry to kill * * The dentry must already be unhashed and removed from the LRU. * * If this is the root of the dentry tree, return NULL. */ static struct dentry *d_kill(struct dentry *dentry) __releases(dentry->d_lock) __releases(dcache_lock) { struct dentry *parent; list_del(&dentry->d_u.d_child); dentry_stat.nr_dentry--; /* For d_free, below */ /*drops the locks, at that point nobody can reach this dentry */ dentry_iput(dentry); if (IS_ROOT(dentry)) parent = NULL; else parent = dentry->d_parent; d_free(dentry); return parent; } /* * This is dput * * This is complicated by the fact that we do not want to put * dentries that are no longer on any hash chain on the unused * list: we'd much rather just get rid of them immediately. * * However, that implies that we have to traverse the dentry * tree upwards to the parents which might _also_ now be * scheduled for deletion (it may have been only waiting for * its last child to go away). * * This tail recursion is done by hand as we don't want to depend * on the compiler to always get this right (gcc generally doesn't). * Real recursion would eat up our stack space. */ /* * dput - release a dentry * @dentry: dentry to release * * Release a dentry. This will drop the usage count and if appropriate * call the dentry unlink method as well as removing it from the queues and * releasing its resources. If the parent dentries were scheduled for release * they too may now get deleted. * * no dcache lock, please. */ void dput(struct dentry *dentry) { if (!dentry) return; repeat: if (atomic_read(&dentry->d_count) == 1) might_sleep(); if (!atomic_dec_and_lock(&dentry->d_count, &dcache_lock)) return; spin_lock(&dentry->d_lock); if (atomic_read(&dentry->d_count)) { spin_unlock(&dentry->d_lock); spin_unlock(&dcache_lock); return; } /* * AV: ->d_delete() is _NOT_ allowed to block now. */ if (dentry->d_op && dentry->d_op->d_delete) { if (dentry->d_op->d_delete(dentry)) goto unhash_it; } /* Unreachable? Get rid of it */ if (d_unhashed(dentry)) goto kill_it; if (list_empty(&dentry->d_lru)) { dentry->d_flags |= DCACHE_REFERENCED; dentry_lru_add(dentry); } spin_unlock(&dentry->d_lock); spin_unlock(&dcache_lock); return; unhash_it: __d_drop(dentry); kill_it: /* if dentry was on the d_lru list delete it from there */ dentry_lru_del(dentry); dentry = d_kill(dentry); if (dentry) goto repeat; } /** * d_invalidate - invalidate a dentry * @dentry: dentry to invalidate * * Try to invalidate the dentry if it turns out to be * possible. If there are other dentries that can be * reached through this one we can't delete it and we * return -EBUSY. On success we return 0. * * no dcache lock. */ int d_invalidate(struct dentry * dentry) { /* * If it's already been dropped, return OK. */ spin_lock(&dcache_lock); if (d_unhashed(dentry)) { spin_unlock(&dcache_lock); return 0; } /* * Check whether to do a partial shrink_dcache * to get rid of unused child entries. */ if (!list_empty(&dentry->d_subdirs)) { spin_unlock(&dcache_lock); shrink_dcache_parent(dentry); spin_lock(&dcache_lock); } /* * Somebody else still using it? * * If it's a directory, we can't drop it * for fear of somebody re-populating it * with children (even though dropping it * would make it unreachable from the root, * we might still populate it if it was a * working directory or similar). */ spin_lock(&dentry->d_lock); if (atomic_read(&dentry->d_count) > 1) { if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)) { spin_unlock(&dentry->d_lock); spin_unlock(&dcache_lock); return -EBUSY; } } __d_drop(dentry); spin_unlock(&dentry->d_lock); spin_unlock(&dcache_lock); return 0; } /* This should be called _only_ with dcache_lock held */ static inline struct dentry * __dget_locked(struct dentry *dentry) { atomic_inc(&dentry->d_count); dentry_lru_del_init(dentry); return dentry; } struct dentry * dget_locked(struct dentry *dentry) { return __dget_locked(dentry); } /** * d_find_alias - grab a hashed alias of inode * @inode: inode in question * @want_discon: flag, used by d_splice_alias, to request * that only a DISCONNECTED alias be returned. * * If inode has a hashed alias, or is a directory and has any alias, * acquire the reference to alias and return it. Otherwise return NULL. * Notice that if inode is a directory there can be only one alias and * it can be unhashed only if it has no children, or if it is the root * of a filesystem. * * If the inode has an IS_ROOT, DCACHE_DISCONNECTED alias, then prefer * any other hashed alias over that one unless @want_discon is set, * in which case only return an IS_ROOT, DCACHE_DISCONNECTED alias. */ static struct dentry * __d_find_alias(struct inode *inode, int want_discon) { struct list_head *head, *next, *tmp; struct dentry *alias, *discon_alias=NULL; head = &inode->i_dentry; next = inode->i_dentry.next; while (next != head) { tmp = next; next = tmp->next; prefetch(next); alias = list_entry(tmp, struct dentry, d_alias); if (S_ISDIR(inode->i_mode) || !d_unhashed(alias)) { if (IS_ROOT(alias) && (alias->d_flags & DCACHE_DISCONNECTED)) discon_alias = alias; else if (!want_discon) { __dget_locked(alias); return alias; } } } if (discon_alias) __dget_locked(discon_alias); return discon_alias; } struct dentry * d_find_alias(struct inode *inode) { struct dentry *de = NULL; if (!list_empty(&inode->i_dentry)) { spin_lock(&dcache_lock); de = __d_find_alias(inode, 0); spin_unlock(&dcache_lock); } return de; } /* * Try to kill dentries associated with this inode. * WARNING: you must own a reference to inode. */ void d_prune_aliases(struct inode *inode) { struct dentry *dentry; restart: spin_lock(&dcache_lock); list_for_each_entry(dentry, &inode->i_dentry, d_alias) { spin_lock(&dentry->d_lock); if (!atomic_read(&dentry->d_count)) { __dget_locked(dentry); __d_drop(dentry); spin_unlock(&dentry->d_lock); spin_unlock(&dcache_lock); dput(dentry); goto restart; } spin_unlock(&dentry->d_lock); } spin_unlock(&dcache_lock); } /* * Throw away a dentry - free the inode, dput the parent. This requires that * the LRU list has already been removed. * * Try to prune ancestors as well. This is necessary to prevent * quadratic behavior of shrink_dcache_parent(), but is also expected * to be beneficial in reducing dentry cache fragmentation. */ static void prune_one_dentry(struct dentry * dentry) __releases(dentry->d_lock) __releases(dcache_lock) __acquires(dcache_lock) { __d_drop(dentry); dentry = d_kill(dentry); /* * Prune ancestors. Locking is simpler than in dput(), * because dcache_lock needs to be taken anyway. */ spin_lock(&dcache_lock); while (dentry) { if (!atomic_dec_and_lock(&dentry->d_count, &dentry->d_lock)) return; if (dentry->d_op && dentry->d_op->d_delete) dentry->d_op->d_delete(dentry); dentry_lru_del_init(dentry); __d_drop(dentry); dentry = d_kill(dentry); spin_lock(&dcache_lock); } } /* * Shrink the dentry LRU on a given superblock. * @sb : superblock to shrink dentry LRU. * @count: If count is NULL, we prune all dentries on superblock. * @flags: If flags is non-zero, we need to do special processing based on * which flags are set. This means we don't need to maintain multiple * similar copies of this loop. */ static void __shrink_dcache_sb(struct super_block *sb, int *count, int flags) { LIST_HEAD(referenced); LIST_HEAD(tmp); struct dentry *dentry; int cnt = 0; BUG_ON(!sb); BUG_ON((flags & DCACHE_REFERENCED) && count == NULL); spin_lock(&dcache_lock); if (count != NULL) /* called from prune_dcache() and shrink_dcache_parent() */ cnt = *count; restart: if (count == NULL) list_splice_init(&sb->s_dentry_lru, &tmp); else { while (!list_empty(&sb->s_dentry_lru)) { dentry = list_entry(sb->s_dentry_lru.prev, struct dentry, d_lru); BUG_ON(dentry->d_sb != sb); spin_lock(&dentry->d_lock); /* * If we are honouring the DCACHE_REFERENCED flag and * the dentry has this flag set, don't free it. Clear * the flag and put it back on the LRU. */ if ((flags & DCACHE_REFERENCED) && (dentry->d_flags & DCACHE_REFERENCED)) { dentry->d_flags &= ~DCACHE_RE
{ "translations": {
    "Request failed, network connection unavailable!" : "¡Se presentó una falla en la solicitud, la conexión a la red no está disponible!",
    "Request unauthorized. Are you logged in?" : "Solicitud no autorizada. ¿Has iniciado sesisón?",
    "Request forbidden. Are you an admin?" : "Solicitud prohibida. ¿Eres un adminsitrador?",
    "Token expired or app not enabled! Reload the page!" : "¡La ficha expiró o la aplicación no está habilitada! ¡Por favor recarga la página!",
    "Internal server error! Please check your data/nextcloud.log file for additional information!" : "¡Se presentó un error interno del servidor! ¡Por favor verifica el archivo data/nextcloud.log para más informacón!",
    "Request failed, Nextcloud is in currently in maintenance mode!" : "¡Se presentó una falla en la solicitud, por el momento Nextcloud se encuentra en modo mantenimiento!",
    "Can not add feed: Exists already" : "No es posible agregar la fuente: ya existe",
    "Articles without feed" : "Artículos sin fuente",
    "Can not add folder: Exists already" : "No se puede agregar la carpeta: ya existe",
    "News" : "Noticias",
    "Use system cron for updates" : "Usa el cron del sistema para las actualizaciones",
    "Disable this if you run a custom updater such as the Python updater included in the app" : "Deshabilita esto si usas un actualizador personalizado como el actualizador de Python en la aplicación",
    "Purge interval" : "Intervalo de purga",
    "Minimum amount of seconds after deleted feeds and folders are removed from the database; values below 60 seconds are ignored" : "Número mínimo de segundos a esperar antes de eliminar de la base de datos carpetas y fuentes borrados; valores inferiores a 60 segundos son ignorados",
    "Maximum read count per feed" : "Conteo máximo de lecturas por fuente",
    "Defines the maximum amount of articles that can be read per feed which won't be deleted by the cleanup job; if old articles reappear after being read, increase this value; negative values such as -1 will turn this feature off" : "Define el número máximo de artículos que pueden ser leídos por fuente que no serán borrados por el trabajo de limpieza; si reaparecen artículos anteriores después de ser leídos, incrementa este valor; valores negativos tales como -1 desactivarán esta funcionalidad. ",
    "Maximum redirects" : "Redirecciones máximas",
    "How many redirects the feed fetcher should follow" : "Cuantas redirecciones debería seguir el buscador de fuentes",
    "Maximum feed page size" : "Tamaño máximo de la página de la fuente",
    "Maximum feed size in bytes. If the RSS/Atom page is bigger than this value, the update will be aborted" : "Tamaño máximo de una fuente en bytes. Si la página RSS/Atom es mayor a este valor, la actualización será abortada ",
    "Feed fetcher timeout" : "Tiempo de vida para el buscador de fuentes",
    "Maximum number of seconds to wait for an RSS or Atom feed to load; if it takes longer the update will be aborted" : "Número máximo de segundos a esperar a que cargue una fuente RSS o Atom; si toma más tiempo, será abortada",
    "Explore Service URL" : "Explorar la URL del Servicio",
    "If given, this service's URL will be queried for displaying the feeds in the explore feed section. To fall back to the built in explore service, leave this input empty" : "Si es proporcionado, la URL de este servicio será consultada para desplegar las fuentes en la seccion de explorar fuente. Para regresar al servicio integrado de exploración, déjalo vacío",
    "For more information check the wiki" : "Para más información consulta el wiki",
    "Saved" : "Guardado",
    "Download" : "Descargar",
    "Close" : "Cerrar",
    "filter" : "filtro",
    "Language" : "Idioma",
    "Subscribe" : "Suscribir",
    "Got more awesome feeds? Share them with us!" : "¿Cuentas con más fuentes increíbles? ¡Compártelas con nosotros!",
    "No articles available" : "No hay artículos disponibles",
    "No unread articles available" : "No hay artículos sin leer disponibles",
    "Open website" : "Abrir sitio web",
    "Star article" : "Marcar el artículo",
    "Unstar article" : "Desmarcar artículo",
    "Keep article unread" : "Mantener el artículo como no-leído",
    "Remove keep article unread" : "Eliminar mantener artículo como no leído",
    "by" : "por",
    "from" : "de",
    "Play audio" : "Reproducir audio",
    "Download video" : "Descargar video",
    "Download audio" : "Descargar audio",
    "Keyboard shortcut" : "Atajo del teclado",
    "Description" : "Descripción",
    "right" : "derecha",
    "Jump to next article" : "Ir al artículo siguiente ",
    "left" : "izquierda",
    "Jump to previous article" : "Ir al artículo anterior",
    "Toggle star article" : "Alternar artículos marcados",
    "Star article and jump to next one" : "Marcar el artículo y continuar al siguiente",
    "Toggle keep current article unread" : "Alternar mantener artículo actual como no leído",
    "Open article in new tab" : "Abrir el artículo en una nueva pestaña",
    "Toggle expand article in compact view" : "Alternar expandir artículo en vista compacta",
    "Refresh" : "Actualizar",
    "Load next feed" : "Cargar la siguiente fuente ",
    "Load previous feed" : "Cargar fuente anterior",
    "Load next folder" : "Cargar la siguiente carpeta",
    "Load previous folder" : "Cargar carpeta anterrior",
    "Scroll to active navigation entry" : "Desplázate hasta el elemento de navegación activo",
    "Focus search field" : "Ir al campo de búsqueda",
    "Mark current article's feed/folder read" : "Marcar la fuente/carpeta del artículo actual como leído",
    "Ajax or webcron mode detected! Your feeds will not be updated!" : "¡Se ha detectado el modo Ajax o webron! ¡Tus fuentes no serán actualizadas!",
    "How to set up the operating system cron" : "Cómo establecer las tareas programadas del sistema operativo",
    "Install and set up a faster parallel updater that uses the News app's update API" : "Instala y configura un actualizador en paralelo más rápido que use el API de actualización de la nueva aplicación",
    "Non UTF-8 charset for MySQL/MariaDB database detected!" : "¡Se ha detectado un juego de caracteres no UTF-8 para la base de datos MySql/MariaDB!",
    "Learn how to convert your database to utf8mb4 (make a backup beforehand)" : "Descubre como convertir tu base de datos a utf8mb4 (haz un respaldo de antemano)",
    "Web address" : "Dirección web",
    "Feed exists already!" : "¡La fuente ya existe!",
    "Folder" : "Carpeta",
    "No folder" : "No hay carpetas",
    "New folder" : "Carpeta nueva ",
    "Folder name" : "Nombre de la carpeta",
    "Go back" : "Regresar",
    "Folder exists already!" : "¡La carpeta ya existe!",
    "Advanced settings" : "Configuraciones avanzados",
    "Credentials" : "Credenciales",
    "HTTP Basic Auth credentials must be stored unencrypted! Everyone with access to the server or database will be able to access them!" : "¡Las credenciales HTTP Basic Auth deben ser almacenadas decriptadas! ¡Todos aquellos que tengan acceso al servidor o a la base de datos tendrán acceso a ellas!",
    "Username" : "Usuario",
    "Password" : "Contraseña",
    "New Folder" : "Carpeta Nueva",
    "Create" : "Crear",
    "Explore" : "Explorar",
    "Update failed more than 50 times" : "La carga falló más de 50 veces",
    "Deleted feed" : "Borrar fuente",
    "Undo delete feed" : "Deshacer borrar fuente",
    "Rename" : "Renombrar",
    "Menu" : "Menú",
    "Mark read" : "Marcar como leído",
    "Unpin from top" : "Desanclar a la parte superior",
    "Pin to top" : "Anclar a la parte superior",
    "Newest first" : "Más reciente primero",
    "Oldest first" : "Más antiguo primero",
    "Default order" : "Ordenamiento predeterminado",
    "Enable full text" : "Habilitar texto completo",
    "Disable full text" : "Deshabilitar texto completo",
    "Unread updated" : "No-leídos actualizados",
    "Ignore updated" : "Ignorar actualizados",
    "Open feed URL" : "Abrir URL de la fuente",
    "Delete" : "Borrar",
    "Dismiss" : "Descartar",
    "Collapse" : "Colapsar",
    "Deleted folder" : "Borrar carpeta",
    "Undo delete folder" : "Deshacer borrar carpeta",
    "Starred" : "Marcados",
    "Unread articles" : "Artículos sin leer",
    "All articles" : "Todos los artículos",
    "Settings" : "Configuraciones ",
    "Disable mark read through scrolling" : "Deshabilitar el marcar como leído al desplazar",
    "Compact view" : "Vista compacta",
    "Expand articles on key navigation" : "Expandir artículos al navegar con el teclado",
    "Show all articles" : "Mostrar todos los artículos",
    "Reverse ordering (oldest on top)" : "Orden inverso (más antiguo arriba)",
    "Subscriptions (OPML)" : "Suscripciones (OPML)",
    "Import" : "Importar",
    "Export" : "Exportar",
    "Error when importing: File does not contain valid OPML" : "Se presentó un error al importar: El archivo no contiene un OPML válido",
    "Error when importing: OPML is does neither contain feeds nor folders" : "Se presentó un error al importar: OPML no contiene fuentes o carpetas",
    "Unread/Starred Articles" : "Artículos No Leídos/Marcados",
    "Error when importing: file does not contain valid JSON" : "Se presentó un error al importar: el archivo no contiene un JSON válido",
    "Help" : "Ayuda",
    "Keyboard shortcuts" : "Atajos del teclado",
    "Documentation" : "Documentación",
    "Report a bug" : "Reportar una falla"
},"pluralForm" :"nplurals=2; plural=(n != 1);"
}
uflen, "/", 1) != 0)) goto Elong; retval = end; dentry = parent; } out: spin_unlock(&vfsmount_lock); return retval; global_root: retval += 1; /* hit the slash */ if (prepend_name(&retval, &buflen, &dentry->d_name) != 0) goto Elong; root->mnt = vfsmnt; root->dentry = dentry; goto out; Elong: retval = ERR_PTR(-ENAMETOOLONG); goto out; } /** * d_path - return the path of a dentry * @path: path to report * @buf: buffer to return value in * @buflen: buffer length * * Convert a dentry into an ASCII path name. If the entry has been deleted * the string " (deleted)" is appended. Note that this is ambiguous. * * Returns a pointer into the buffer or an error code if the path was * too long. Note: Callers should use the returned pointer, not the passed * in buffer, to use the name! The implementation often starts at an offset * into the buffer, and may leave 0 bytes at the start. * * "buflen" should be positive. */ char *d_path(const struct path *path, char *buf, int buflen) { char *res; struct path root; struct path tmp; /* * We have various synthetic filesystems that never get mounted. On * these filesystems dentries are never used for lookup purposes, and * thus don't need to be hashed. They also don't need a name until a * user wants to identify the object in /proc/pid/fd/. The little hack * below allows us to generate a name for these objects on demand: */ if (path->dentry->d_op && path->dentry->d_op->d_dname) return path->dentry->d_op->d_dname(path->dentry, buf, buflen); read_lock(¤t->fs->lock); root = current->fs->root; path_get(&root); read_unlock(¤t->fs->lock); spin_lock(&dcache_lock); tmp = root; res = __d_path(path, &tmp, buf, buflen); spin_unlock(&dcache_lock); path_put(&root); return res; } /* * Helper function for dentry_operations.d_dname() members */ char *dynamic_dname(struct dentry *dentry, char *buffer, int buflen, const char *fmt, ...) { va_list args; char temp[64]; int sz; va_start(args, fmt); sz = vsnprintf(temp, sizeof(temp), fmt, args) + 1; va_end(args); if (sz > sizeof(temp) || sz > buflen) return ERR_PTR(-ENAMETOOLONG); buffer += buflen - sz; return memcpy(buffer, temp, sz); } /* * Write full pathname from the root of the filesystem into the buffer. */ char *dentry_path(struct dentry *dentry, char *buf, int buflen) { char *end = buf + buflen; char *retval; spin_lock(&dcache_lock); prepend(&end, &buflen, "\0", 1); if (d_unlinked(dentry) && (prepend(&end, &buflen, "//deleted", 9) != 0)) goto Elong; if (buflen < 1) goto Elong; /* Get '/' right */ retval = end-1; *retval = '/'; while (!IS_ROOT(dentry)) { struct dentry *parent = dentry->d_parent; prefetch(parent); if ((prepend_name(&end, &buflen, &dentry->d_name) != 0) || (prepend(&end, &buflen, "/", 1) != 0)) goto Elong; retval = end; dentry = parent; } spin_unlock(&dcache_lock); return retval; Elong: spin_unlock(&dcache_lock); return ERR_PTR(-ENAMETOOLONG); } /* * NOTE! The user-level library version returns a * character pointer. The kernel system call just * returns the length of the buffer filled (which * includes the ending '\0' character), or a negative * error value. So libc would do something like * * char *getcwd(char * buf, size_t size) * { * int retval; * * retval = sys_getcwd(buf, size); * if (retval >= 0) * return buf; * errno = -retval; * return NULL; * } */ SYSCALL_DEFINE2(getcwd, char __user *, buf, unsigned long, size) { int error; struct path pwd, root; char *page = (char *) __get_free_page(GFP_USER); if (!page) return -ENOMEM; read_lock(¤t->fs->lock); pwd = current->fs->pwd; path_get(&pwd); root = current->fs->root; path_get(&root); read_unlock(¤t->fs->lock); error = -ENOENT; spin_lock(&dcache_lock); if (!d_unlinked(pwd.dentry)) { unsigned long len; struct path tmp = root; char * cwd; cwd = __d_path(&pwd, &tmp, page, PAGE_SIZE); spin_unlock(&dcache_lock); error = PTR_ERR(cwd); if (IS_ERR(cwd)) goto out; error = -ERANGE; len = PAGE_SIZE + page - cwd; if (len <= size) { error = len; if (copy_to_user(buf, cwd, len)) error = -EFAULT; } } else spin_unlock(&dcache_lock); out: path_put(&pwd); path_put(&root); free_page((unsigned long) page); return error; } /* * Test whether new_dentry is a subdirectory of old_dentry. * * Trivially implemented using the dcache structure */ /** * is_subdir - is new dentry a subdirectory of old_dentry * @new_dentry: new dentry * @old_dentry: old dentry * * Returns 1 if new_dentry is a subdirectory of the parent (at any depth). * Returns 0 otherwise. * Caller must ensure that "new_dentry" is pinned before calling is_subdir() */ int is_subdir(struct dentry *new_dentry, struct dentry *old_dentry) { int result; unsigned long seq; if (new_dentry == old_dentry) return 1; /* * Need rcu_readlock to protect against the d_parent trashing * due to d_move */ rcu_read_lock(); do { /* for restarting inner loop in case of seq retry */ seq = read_seqbegin(&rename_lock); if (d_ancestor(old_dentry, new_dentry)) result = 1; else result = 0; } while (read_seqretry(&rename_lock, seq)); rcu_read_unlock(); return result; } void d_genocide(struct dentry *root) { struct dentry *this_parent = root; struct list_head *next; spin_lock(&dcache_lock); repeat: next = this_parent->d_subdirs.next; resume: while (next != &this_parent->d_subdirs) { struct list_head *tmp = next; struct dentry *dentry = list_entry(tmp, struct dentry, d_u.d_child); next = tmp->next; if (d_unhashed(dentry)||!dentry->d_inode) continue; if (!list_empty(&dentry->d_subdirs)) { this_parent = dentry; goto repeat; } atomic_dec(&dentry->d_count); } if (this_parent != root) { next = this_parent->d_u.d_child.next; atomic_dec(&this_parent->d_count); this_parent = this_parent->d_parent; goto resume; } spin_unlock(&dcache_lock); } /** * find_inode_number - check for dentry with name * @dir: directory to check * @name: Name to find. * * Check whether a dentry already exists for the given name, * and return the inode number if it has an inode. Otherwise * 0 is returned. * * This routine is used to post-process directory listings for * filesystems using synthetic inode numbers, and is necessary * to keep getcwd() working. */ ino_t find_inode_number(struct dentry *dir, struct qstr *name) { struct dentry * dentry; ino_t ino = 0; dentry = d_hash_and_lookup(dir, name); if (dentry) { if (dentry->d_inode) ino = dentry->d_inode->i_ino; dput(dentry); } return ino; } static __initdata unsigned long dhash_entries; static int __init set_dhash_entries(char *str) { if (!str) return 0; dhash_entries = simple_strtoul(str, &str, 0); return 1; } __setup("dhash_entries=", set_dhash_entries); static void __init dcache_init_early(void) { int loop; /* If hashes are distributed across NUMA nodes, defer * hash allocation until vmalloc space is available. */ if (hashdist) return; dentry_hashtable = alloc_large_system_hash("Dentry cache", sizeof(struct hlist_head), dhash_entries, 13, HASH_EARLY, &d_hash_shift, &d_hash_mask, 0); for (loop = 0; loop < (1 << d_hash_shift); loop++) INIT_HLIST_HEAD(&dentry_hashtable[loop]); } static void __init dcache_init(void) { int loop; /* * A constructor could be added for stable state like the lists, * but it is probably not worth it because of the cache nature * of the dcache. */ dentry_cache = KMEM_CACHE(dentry, SLAB_RECLAIM_ACCOUNT|SLAB_PANIC|SLAB_MEM_SPREAD); register_shrinker(&dcache_shrinker); /* Hash may have been set up in dcache_init_early */ if (!hashdist) return; dentry_hashtable = alloc_large_system_hash("Dentry cache", sizeof(struct hlist_head), dhash_entries, 13, 0, &d_hash_shift, &d_hash_mask, 0); for (loop = 0; loop < (1 << d_hash_shift); loop++) INIT_HLIST_HEAD(&dentry_hashtable[loop]); } /* SLAB cache for __getname() consumers */ struct kmem_cache *names_cachep __read_mostly; EXPORT_SYMBOL(d_genocide); void __init vfs_caches_init_early(void) { dcache_init_early(); inode_init_early(); } void __init vfs_caches_init(unsigned long mempages) { unsigned long reserve; /* Base hash sizes on available memory, with a reserve equal to 150% of current kernel size */ reserve = min((mempages - nr_free_pages()) * 3/2, mempages - 1); mempages -= reserve; names_cachep = kmem_cache_create("names_cache", PATH_MAX, 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL); dcache_init(); inode_init(); files_init(mempages); mnt_init(); bdev_cache_init(); chrdev_init(); } EXPORT_SYMBOL(d_alloc); EXPORT_SYMBOL(d_alloc_root); EXPORT_SYMBOL(d_delete); EXPORT_SYMBOL(d_find_alias); EXPORT_SYMBOL(d_instantiate); EXPORT_SYMBOL(d_invalidate); EXPORT_SYMBOL(d_lookup); EXPORT_SYMBOL(d_move); EXPORT_SYMBOL_GPL(d_materialise_unique); EXPORT_SYMBOL(d_path); EXPORT_SYMBOL(d_prune_aliases); EXPORT_SYMBOL(d_rehash); EXPORT_SYMBOL(d_splice_alias); EXPORT_SYMBOL(d_add_ci); EXPORT_SYMBOL(d_validate); EXPORT_SYMBOL(dget_locked); EXPORT_SYMBOL(dput); EXPORT_SYMBOL(find_inode_number); EXPORT_SYMBOL(have_submounts); EXPORT_SYMBOL(names_cachep); EXPORT_SYMBOL(shrink_dcache_parent); EXPORT_SYMBOL(shrink_dcache_sb);