Checklist/UnlinkedFiles/Solutions/Relink

From Linux Checkpoint / Restart Wiki
Jump to: navigation, search

OBSOLETE CONTENT

This wiki has been archived and the content is no longer updated.

Use vfs_link() to relink the unlinked file back into the filesystem for checkpointing by the normal filesystem checkpoint procedure (e.g. snapshots, freeze fs + backup, etc).

The problem with relinking the file is there could already be another file with the same name in that location during checkpoint. Relinking the file to a different location has the same issue. Furthermore, we need to obey permissions and stay within the same filesystem when relinking.

The issue may reappear during restart depending on how we restore the filesystem contents. A complete restart would include all filesystem contents, so the solution adopted during checkpoint should be useful during restart. However, there has been talk of "subtree checkpoint/restart" (where subtree is not referring to filesystem content nor, strictly speaking, process trees). In these cases the file used for relinking may already exist and so a temporary rename may be necessary. Of course all of this assumes that the name and path of the unlinked file are important (they show up in /proc/<pid>/fd but with "(deleted)" appended).

Without fully accounting for these complications the code would look roughly like:

+/*
+ * Relink an unlinked file near its original location.
+ * /foo/bar becomes /foo/.relinked/bar during checkpoint
+ * then becomes /foo/bar briefly during restart (may involve a mv to
+ * preserve a new /foo/bar).
+ */
+static int relink_file(struct ckpt_ctx *ctx, struct file *file)
+{
+       char pathname[PATH_MAX];
+       char filename[PATH_MAX];
+       char *pn;
+       char *rlink;
+       size_t len;
+       struct nameidata nd;
+       struct dentry *d_ent;
+       int err;
+
+       pn = d_path(&file->f_path, pathname, sizeof(pathname));
+       if (IS_ERR(pn)) {
+               /* TODO */
+       }
+       len = strlen(pn);
+       memmove(pathname, pn, len + 1);
+       /* We know file is unlinked -- remove the otherwise-ambiguous string */
+       len -= strlen(" (deleted)");
+       rlink = pn + len;
+       *rlink = '\0';
+
+       /* We need to add a .relinked sibling directory */
+       while (*rlink != '/')
+               rlink--;
+       rlink++;
+       /* Save the filename */
+       strcpy(filename, rlink);
+       /* Make pathname the sibling directory */
+       strncat(rlink, ".relinked", PATH_MAX - (rlink - pathname));
+       err = sys_mkdir(pn, S_IRWXU);
+       if (err < 0 && err != -EEXIST) {
+               /* TODO */
+       }
+
+       /* Link the file into its .relinked sibling directory */
+       rlink += strlen(".relinked");
+       strncat(rlink, filename, PATH_MAX - (rlink - pathname));
+       err = path_lookup(pathname, LOOKUP_CREATE, &nd);
+       if (err < 0)
+               return err;
+       if (file->f_vfsmnt != nd.mnt) {
+               /* Should never happen */
+               err = -EXDEV;
+               goto release;
+       }
+       d_ent = lookup_create(&nd, 0);
+       if (IS_ERR(d_ent)) {
+               err = PTR_ERR(d_ent);
+               goto up;
+       }
+       err = vfs_link(file->f_dentry, nd.dentry->d_inode, d_ent);
+       dput(d_ent);
+up:
+       up(&nd.entry->d_inode->i_sem);
+release:
+       path_release(&nd);
+       return err;
+}
Personal tools